TPSオンラインゲーム制作 キャラとカメラの移動、カバーアクション、銃を撃つまで
こんにちは!ジェイです。今回から、UnityのアセットのThird Person Shooter Bundleを改造して、MonobitEngineでオンラインゲームにするまでの解説します。その前に下のアセットのどちらかが必要になるのでご了承ください。キャラクターモデルは無料のUnityちゃんを使わせていただきます。
ステージ準備
さっそくですが、以下の手順通りにステージを作ります。
- Third Person Shooter Bundleをインポート
- Unityちゃんモデルのインポート
- 床としてPlaneを配置
- 各コンポーネントの追加
- ステージを管理するGameControllerを追加
Planeの追加
3Dobject→Planeを追加して、MeshCollderを外して、BoxCollderを追加して、Staticにチェックをいれます。

キャラの配置とコンポーネントの追加
Unityちゃんをインポートした後にモデルをPlane上の任意の場所に配置します。その後以下の手順でコンポーネントを追加します。
Third Person Orbit Cam(Script)
unitychanを配置したら、まずMainCameraにThird Person Orbit Cam(Script)を追加して、Playerにunitychanをアタッチします。

・Rigidbody
・CapsuleCollider
・AimBehaviour
・MoveBehaviour
・BasicBehaviour
AnimatorとRigidbody
- ControllerをCharacterControllerに設定
- FreezeRotationのX,Y,Zにチェック

CapsulCollider

AimBehaviouとMoveBehaviourとBasicBehaviourを追加
3つのスクリプトを追加し、以下のように変更します。PlayerCameraにMainCameraをアタッチするのを忘れないようにしましょう。

実行結果
壁に隠れられるようにする
壁を隠れられるようにするために必要な以下のものを追加します。
- CreateEmptyで空のオブジェクトを生成し、GameContorllerと名前を付ける
- 1で作ったGameContorllerにGameContorllerのタグを設定
- PickupHUD,ScreenHUD,ShotEffects,Signsの4つをプレハブからコピーして、GameContorllerに子オブジェクトとして追加する

unitychanにCoverBehavorを追加
CoverSign,TrunCoverSign,ChangeCoverSign,JumpCoverSignには先ほどGameControllerの子オブジェクトとして、作った4つのコンポーネントをアタッチしてください。

NavigationでBakeする
Cube(壁)を追加して以下のように設定します。

次にCube(壁)のNavigation→ObjectのNavigation AreaをNotWalkableに変更します。

その後、Planeを選択して以下の画面のBakeをクリックします。

実行結果
上手く隠れられないときのチェック点
- PlaneとCubeのstaticにチェックを入れた後にBakeしているか
- CubeのLayerをCoverに設定しているか
- CapsulColliderの値が適切か
特にCapsulColliderはcenterのyを高くしすぎると、隠れられなくなります。CapsulColliderが地面にぴったりつくようにcenterのyの値を減らすと上手くいく場合があります。
武器を使えるようにする
unitychanにShotBehaviourを追加して、以下のように設定します。この時にflash,tracer,sparksの3つのオブジェクトは、先ほど追加したGameControllerのShotEffectsの子オブジェクトに3つともあります。

武器の追加
TPS Bundle→Extras→PrefabsにPistol,Rifle,AK,G36の4つの武器があるので、そこからシーンに追加しましょう。

この中で特に大事なのが、RightHandPositionとRelativeRotationで武器の位置の調整をする時にはここの値をいじります。Magは一度に入る弾の数で、TotalBulletsはリロードも含めたすべての総弾数です。
的の追加
3DobjectからCube(的)を追加して、Rigidbodyと以下のスクリプトを新規作成して追加します。
using UnityEngine;
public class CTargerManager : HealthManager
{
public override void TakeDamage(Vector3 location, Vector3 direction, float damage, Collider bodyPart = null, GameObject origin = null)
{
GetComponent<Rigidbody>().AddForce(direction * 5f, ForceMode.Impulse);
}
}
実行結果
武器の変更方法を変える
デフォルトのままではタブキーを押すと武器を変更できますが、マウスのホイールでできたほうが便利です。なので、その機能を実装するスクリプトを紹介します。ChangeWeponとUpdateを変更するだけで簡単に実装できます。
private void ChangeWeapon(int oldWeapon, int newWeapon, bool up_falg = true)
{
// Previously armed? Disable weapon.
if (oldWeapon > 0)
{
weapons[oldWeapon].gameObject.SetActive(false);
gunMuzzle = null;
weapons[oldWeapon].Toggle(false);
}
// Cycle trought empty slots to find next existing weapon or the no weapon slot.
if (up_falg)
{
while (weapons[newWeapon] == null && newWeapon > 0)
{
newWeapon = (newWeapon + 1) % weapons.Count;
}
}
else
{
while (weapons[newWeapon] == null && newWeapon > 0)
{
newWeapon = (newWeapon + (weapons.Count - 1)) % weapons.Count;
}
}
// Next weapon exists? Activate it.
if (newWeapon > 0)
{
weapons[newWeapon].gameObject.SetActive(true);
gunMuzzle = weapons[newWeapon].transform.Find("muzzle");
weapons[newWeapon].Toggle(true);
}
activeWeapon = newWeapon;
// Call change weapon animation if new weapon type is different.
if (oldWeapon != newWeapon)
{
behaviourManager.GetAnim.SetTrigger(changeWeaponTrigger);
behaviourManager.GetAnim.SetInteger(weaponTypeInt, weapons[newWeapon] ? (int)weapons[newWeapon].type : 0);
}
// Set crosshair if armed.
SetWeaponCrosshair(newWeapon > 0);
}
private void Update()
{
// Handle shoot weapon action.
if (Input.GetAxisRaw(shootButton) != 0 && !isShooting && activeWeapon > 0 && burstShotCount == 0)
{
isShooting = true;
ShootWeapon(activeWeapon);
}
else if (isShooting && Input.GetAxisRaw(shootButton) == 0)
{
isShooting = false;
}
// Handle reload weapon action.
else if (Input.GetButtonUp(reloadButton) && activeWeapon > 0)
{
if (weapons[activeWeapon].StartReload())
{
AudioSource.PlayClipAtPoint(weapons[activeWeapon].reloadSound, gunMuzzle.position, 0.5f);
behaviourManager.GetAnim.SetBool(reloadBool, true);
}
}
// Handle drop weapon action.
else if (Input.GetButtonDown(dropButton) && activeWeapon > 0)
{
// End reload paramters, drop weapon and change to another one in inventory.
EndReloadWeapon();
int weaponToDrop = activeWeapon;
ChangeWeapon(activeWeapon, 0);
weapons[weaponToDrop].Drop();
weapons[weaponToDrop] = null;
}
// Handle change weapon action.
else
{
if (Input.mouseScrollDelta.y < 0 && !isChangingWeapon)
{
isChangingWeapon = true;
int nextWeapon = activeWeapon + (weapons.Count - 1) % weapons.Count;
ChangeWeapon(activeWeapon, (nextWeapon) % weapons.Count, false);
}
else if (Input.mouseScrollDelta.y > 0 && !isChangingWeapon)
{
isChangingWeapon = true;
int nextWeapon = activeWeapon + 1;
ChangeWeapon(activeWeapon, (nextWeapon) % weapons.Count);
}
else
{
isChangingWeapon = false;
}
//if ((Input.GetAxisRaw(changeButton) != 0 && !isChangingWeapon))
//{
// isChangingWeapon = true;
// int nextWeapon = activeWeapon + 1;
// ChangeWeapon(activeWeapon, (nextWeapon) % weapons.Count);
//}
//else if (Input.GetAxisRaw(changeButton) == 0)
//{
// isChangingWeapon = false;
//}
}
// Manage shot parameters after shooting action.
if (isShotAlive)
ShotDecay();
isAiming = behaviourManager.GetAnim.GetBool(aimBool);
}