Unityを使ったゲーム開発記録

Unityのゲーム開発などを中心に投稿していきます。何か参考になる様な記事ではなく自分用の備忘録になる為、生暖かい目で見て下さい。

Unityでのゲーム開発 〜Rigidbodyとコライダーを適用したScript〜

Scriptを変更する前に


前回の記事でCharacterControllerかRigidbodyとコライダーを適用した事に関して触れたと思いますが、今の状態だと階段も登れない子になってしまうので_:(´ཀ`」 ∠):
そこを解消していきたいと思います。


現在のPlayerの状態⇩
f:id:mochisakucoco:20210314142821g:plain

見事に見えない壁にぶち当たってますねw



なぜ登れないのか?

f:id:mochisakucoco:20210314143555p:plain
なぜなのかと言うと今回取り付けているCapsuleColliderは👆の様な形状になっています。
このカプセル状態の物体が現実世界で階段を登っていたら恐怖でしかないです・・・足が生えていれば別ですが

Avatarとして外見は完全に人型をしていますが実際はカプセル状に判定があるので階段を登れていないとゆう事になります。FPSやTPSでも部位によって当たり判定やダメージが変化すると思いますが、今の状態だと何処に当ててもヘッドショット扱いになってしまう様な物です。
ダメージ判定などを分けるには各部位によってコライダーをつける必要があります。今は必要ない為、軽く覚えておいて貰えれば大丈夫です。




階段を登らせるには?

f:id:mochisakucoco:20210310054121p:plain
前回説明したCharacterControllerのStep Offsetを作れば解決出来ると思います。

プレイヤーの足元からRey(光線とゆう意味)を飛ばして登れる坂や階段なのかを調べる様にしたいと思います。
*銃の射線管理にも使えるraycastとゆうオブジェクトを検知するのに役に立つ機能もあります(後々使うと思います)

空のオブジェクトを作成して名前を[StepRayPoint]にしてPlayerの子オブジェクトとして設定しておきます。
positionなどは⇩の様に設定してます
f:id:mochisakucoco:20210314170727p:plain




Scriptの変更

今回の記事からmoveSpeedをwalkSpeedに変更しています(歩きと走りを分けたい為)
スクリプトは適宜変更する為ご了承ください:(;゙゚'ω゚'):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{

    [SerializeField] private GameObject aimCamera;          //エイムカメラを紐づける
    [SerializeField] private GameObject thirdPersonCamera;  //第三者視点カメラを紐づける


    private float Horizontal;                               //←・→、Aキー・Dキー
    private float Vertical;                                 //↑・↓、Wキー・Sキー

    private Animator animator;                              //animator
    private Vector3 velocity;                               //移動速度
    private Vector3 input;                                  //入力値
    private Rigidbody rd;                                   //rigidbody
    int layerMask;                                          //Playerレイヤー以外のレイヤーマスク
    [SerializeField] private bool isGrounded;               //地面に接地しているかどうか
    [SerializeField] private float walkSpeed = 1.5f;        //歩く速さ
    [SerializeField] private float jumpPower = 5f;          //ジャンプ力
    [SerializeField] private Transform stepRay;             //段差を昇る為のレイを飛ばす位置
    [SerializeField] private float stepDistance = 0.5f;     //レイを飛ばす距離
    [SerializeField] private float stepOffset = 0.3f;       //昇れる段差
    [SerializeField] private float slopeLimit = 65f;        //昇れる角度
    [SerializeField] private float slopeDistance = 1f;      //昇れる段差の位置から飛ばすレイの距離


    void Start()
    {
        animator = GetComponent<Animator>();    //Animatorコンポーネントの取得
        rd = GetComponent<Rigidbody>();         //Rigidbodyコンポーネントの取得
        layerMask = ~(1 << LayerMask.NameToLayer("Player"));
    }
 
    void Update()
    {

        Horizontal = Input.GetAxisRaw("Horizontal");
        Vertical   = Input.GetAxisRaw("Vertical");

        if (Input.GetKeyDown(KeyCode.B))  //対応したキーを押されたらカメラを切り換えるプログラムの呼び出し
            {
                aimCamera.SetActive(!aimCamera.activeInHierarchy);
                thirdPersonCamera.SetActive(!thirdPersonCamera.activeInHierarchy);
            }

        //キャラクターが接地している場合
        if (isGrounded)
            {
            //接地したので移動速度を0にする
            velocity = Vector3.zero;
            input = new Vector3(Horizontal, 0f, Vertical);
 
            //方向キーが押されている時
            if (input.magnitude > 0f)
                {
                animator.SetFloat("Speed", input.magnitude);

                //昇れる段差を表示
                Debug.DrawLine(transform.position + new Vector3(0f, stepOffset, 0f), transform.position + new Vector3(0f, stepOffset, 0f) + transform.forward * slopeDistance, Color.green);

                //ステップ用のレイが地面に接触しているかどうか
                if (Physics.Linecast(stepRay.position, stepRay.position + stepRay.forward * stepDistance, out var stepHit, LayerMask.GetMask("Field", "Block")))
                    {
                    //進行方向の地面の角度が指定以下、または昇れる段差より下だった場合の移動処理
                    if (Vector3.Angle(transform.up, stepHit.normal) <= slopeLimit 
                        || (Vector3.Angle(transform.up, stepHit.normal) > slopeLimit 
                        && !Physics.Linecast(transform.position + new Vector3(0f, stepOffset, 0f), transform.position + new Vector3(0f, stepOffset, 0f) + transform.forward * slopeDistance, LayerMask.GetMask("Field", "Block"))))
                    {
                        velocity = new Vector3(0f, (Quaternion.FromToRotation(Vector3.up, stepHit.normal) * transform.forward * walkSpeed).y, 0f) + transform.forward * walkSpeed;
                        Debug.Log(Vector3.Angle(transform.up, stepHit.normal));
 
                    } else {
                        velocity += transform.forward * walkSpeed;
                    }
 
                    Debug.Log(Vector3.Angle(Vector3.up, stepHit.normal));
 
                //地面に接触していない場合
                } else {
                    velocity += transform.forward * walkSpeed;
                }
            //キーを押していない場合は移動しない
            } else {
                animator.SetFloat("Speed", 0f);
            }


            //ジャンプの処理
            if (Input.GetButtonDown("Jump")
                && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump")
                && !animator.IsInTransition(0))      //遷移途中にジャンプさせない条件
            {
                animator.SetBool("Jump", true);
                //ジャンプしたら接地していない状態にする
                isGrounded = false;
                velocity.y += jumpPower;
            }
        }
    }


    void FixedUpdate()
    {
        // //キャラクターを移動させる処理
        rd.MovePosition(transform.position + velocity * Time.fixedDeltaTime);
        // カメラの方向から、X-Z平面の単位ベクトルを取得
        Vector3 cameraForward = Vector3.Scale(thirdPersonCamera.transform.forward, new Vector3(1, 0, 1)).normalized;

        // 方向キーの入力値とカメラの向きから、移動方向を決定
        Vector3 moveForward = cameraForward * Vertical + thirdPersonCamera.transform.right * Horizontal;

        // 移動方向にスピードを掛ける。ジャンプや落下がある場合は、別途Y軸方向の速度ベクトルを足す。
        velocity = moveForward * walkSpeed + new Vector3(0, velocity.y, 0);

        // キャラクターの向きを進行方向にする(キャラクターの向きの設定は"ThirdPeasonCamera"が有効の場合のみ)
        if (moveForward != Vector3.zero && thirdPersonCamera.activeInHierarchy)
        {
            transform.rotation = Quaternion.LookRotation(moveForward);
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        //地面に着地したかどうかの判定
        if (Physics.CheckSphere(transform.position, 0.3f, layerMask))
        {
            isGrounded = true;
            animator.SetBool("Jump", false);
            velocity.y = 0f;
        }
    }
 
    private void OnCollisionExit(Collision collision)
    {
        //地面から降りた時の処理
        //Fieldレイヤーのゲームオブジェクトから離れた時
        if (1 << collision.gameObject.layer != layerMask)
        {
            //下向きにレイヤーを飛ばしFieldレイヤーと接触しなければ地面から離れたと判定する
            if (!Physics.Linecast(transform.position + Vector3.up * 0.2f, transform.position + Vector3.down * 0.1f, LayerMask.GetMask("Field", "Block")))
            {
                isGrounded = false;
            }
        }
    }
}





スクリプト内容確認

layerMask = ~(1 << LayerMask.NameToLayer("Player"));

LayerMaskにPlayerレイヤーを設定、それ以外はビット演算子で新規でレイヤーを作成している。
http://www.ibe.kagoshima-u.ac.jp/edu/gengo0/p6.html⇦ビット演算子に関して

レイヤー - Unity マニュアル

 

//昇れる段差を表示
Debug.DrawLine(transform.position + new Vector3(0f, stepOffset, 0f), transform.position + new Vector3(0f, stepOffset, 0f) + transform.forward * slopeDistance, Color.green);

f:id:mochisakucoco:20210318114059p:plain




//進行方向の地面の角度が指定以下、または昇れる段差より下だった場合の移動処理
if (Vector3.Angle(transform.up, stepHit.normal) <= slopeLimit || (Vector3.Angle(transform.up, stepHit.normal) > slopeLimit 
&& !Physics.Linecast(transform.position + new Vector3(0f, stepOffset, 0f), transform.position + new Vector3(0f, stepOffset, 0f) + transform.forward * slopeDistance, LayerMask.GetMask("Field", "Block"))))

if (Vector3.Angle(transform.up, stepHit.normal) <=・・・<=(以下演算子)で坂及び階段等の条件指定をします
slopeLimit・・・昇れる角度
||・・・または
(Vector3.Angle(transform.up, stepHit.normal) > slopeLimit・・・進行方向の角度が昇れる角度より下だった場合
&&・・・かつ
!Physics.Linecast(transform.position + new Vector3(0f, stepOffset, 0f), transform.position + new Vector3(0f, stepOffset, 0f) + transform.forward * slopeDistance, LayerMask.GetMask("Field", "Block"))))・・・Rayがレイヤーを検知している場合


上記の条件下で下記の移動方法を行える⇩⇩

velocity = new Vector3(0f, (Quaternion.FromToRotation(Vector3.up, stepHit.normal) * transform.forward * walkSpeed).y, 0f) + transform.forward * walkSpeed;
Debug.Log(Vector3.Angle(transform.up, stepHit.normal));

Quaternion.FromToRotation(Vector3.up, stepHit.normal)で上方向とRayの接触面の間の角度を計算しそれにtransform.forwardをかける事で坂の角度方向へのベクトルを計算しています。


Physics-Linecast - Unity スクリプトリファレンス






 
最終的な物がこちら⇩
f:id:mochisakucoco:20210317074456g:plain


今回はAnimatorに関して何も触れていませんが、通常はモーションは何も設定されていない為次回はAnimatorの設定を詳しくいじって行こうと思います。



今回参考にさせて頂いた記事⇩
キャラ操作をCharacterControllerからRigidbody+コライダに変更する | Unityを使った3Dゲームの作り方(かめくめ)

Unityでのゲーム開発 〜Rigidbody+αとCharacterController〜

Rigidbody+αとCharacterController

今回はRigidbody+α(〇〇Collider)CharacterControlle使用方法と違いについてを見ていきたいと思います。

前置きとしてRigidbody+α(〇〇Collider)とCharacterControlleどちらが優れているかではありません、使用用途によって使い分ける事が一番大事だと思います。



Rigidbody

Rigidbodyをオブジェクトに追加すると、Unityの物理エンジンの制御を出来る様になります。Rigidbodyを付けたオブジェクトは重力の働きを追加する事が出来ます。
+αでColliderコンポーネントも存在する場合は、他のオブジェクトとの衝突にも反応させる様に出来ます。
下記で記述していますが、衝突時の挙動なども細かく設定可能な為、コードの記述量などは増えますが現実的に近い表現も可能になります。



前の記事でも触れましたがスクリプトを書く場所は、FixedUpdateに記載して設定を変更する場所とすることが推奨されます(対照的にUpdateは、その他ほとんどのフレーム更新作業で使用されます)。理由は、物理挙動の更新が時間進行単位で行われている為、フレーム更新は関係ないからだそうです。FixedUpdateは物理挙動更新の直前に呼び出されるため、その中で行ったどのような変更も直接反映されます。


f:id:mochisakucoco:20210308070113p:plain

RigidbodyのInspector内の詳細

  • Mass・・・物体の質量(Kg 単位) *物の体重だと思えば分かりやすいと思います

 

  • Drag・・・力によって動く際に、オブジェクトに影響する空気抵抗の大きさ。0の場合、空気抵抗は0。無限の場合、オブジェクトは動きを止めます。

*Drag 値が低いと、オブジェクトが重く見えるようになります。この値が高いと、軽く見えます。 Drag の通常の値は、.001(金属の塊)と10(羽) の間です。 

 

  • Angular Drag・・・トルクによって回転する際に、オブジェクトに影響する空気抵抗の大きさ。0 の場合、空気抵抗は 0。このパラメーターの値を無限に設定するだけでは、オブジェクトの回転を止められないことに注意してください。

 

  • Use Gravity・・・有効にすると、オブジェクトは重力の影響を受けます。

 

  • Is Kinematic・・・有効にすると、オブジェクトは物理エンジンによって駆動されず、その Transform によってのみ操作されます。これは、プラットフォームを移動したい場合や、Hinge Joint を加えたリジッドボディをアニメーション化したい場合に便利です。

 

  • Interpolate・・・リジッドボディの動きがぎこちないとき、以下のオプションの1つを利用すると改善する場合があります。
  1. None 補間を適用しない。
  2. Interpolate 前フレームのTransformにもとづいてTransformをスムージングします。
  3. Extrapolate 次のフレームのTransformを予測してTransformをスムージングします。

 

  • Collision Detection・・・これを使用すると、高速で動くオブジェクトが、衝突を検知せずに他のオブジェクトをすり抜けてしまうことを防ぎます。
  1. Discrete これを選択すると、シーン内のすべてのコライダーに対し Discrete (離散型) 衝突検知を使用します。他のコライダーがこのリジッドボディに対して衝突をテストするときには、離散型衝突検知を行います。一般的な衝突に使用します (デフォルト)。
  2. Continuous これを選択すると、動的なコライダー (リジッドボディあり) に対しては離散型衝突検知を使用し、静的メッシュコライダー (リジッドボディなし) に対してはスイープに基づく Continuous (連続型) 衝突検知を使用します。Continuous Dynamic (連続型かつ動的) に設定されたリジッドボディは、このリジッドボディに対する衝突のテストには連続型衝突検知を使用します。その他のリジッドボディは、離散型衝突検知を使用します。このオプションは、Continuous Dynamic に設定されたリジッドボディが衝突するオブジェクトに使用します。(この設定は物理演算のパフォーマンスに大きく影響するため、高速なオブジェクトの衝突で問題がない場合は Discrete の設定のままにしてください。)
  3. Continuous Dynamic これを選択すると、Continuous や Continuous Dynamic を適用したゲームオブジェクトに対して、スイープに基づく 連続型衝突検知を行ないます。静的なコライダー (リジッドボディなし) にも連続型衝突検知を適用します。その他のコライダーについては、離散型衝突検知を行ないます。高速で動くオブジェクトに使用します。
  4. Continuous Speculative これを選択すると、リジッドボディとコライダーに対する投機的な連続衝突検出を使用します。これは、キネマティックなボディを設定できる唯一の CCD モードです。この方法は、スイープに基づく連続衝突検出よりも負荷が少なくなる傾向があります。

 

  • Constraints リジッドボディの動きに関する制限。
  1. Freeze Position ワールド座標の選択した軸でリジッドボディの移動を停止します。
  2. Freeze Rotation ローカル座標の選択した軸でリジッドボディの回転を停止します。

docs.unity3d.com

 

〇〇Collider(コライダー)

コライダーは、衝突を発生させるために必要なリジッドボディとともに加える、もう1つのコンポーネントです。2つのリジッドボディが衝突する場合、両方のゲームオブジェクトにコライダーが設定されていないと物理エンジンは衝突を計算できません。コライダーのないリジッドボディは、物理特性シミュレーションで互いにすり抜けてしまいます。


AddComponent⇨Physics メニューでコライダーを加えます。コライダーには複数の種類があります。

  • Box Collider・・・キューブ(箱型)形状
  • Sphere Collider・・・スフィア(球体型)形状
  • Capsule Collider・・・カプセル型の形状
  • Mesh Collider・・・メッシュ(ゲームオブジェクトと同じ形)からコライダーを作成します、メッシュコライダーとは衝突できません
  • Wheel Collider・・・特に、車両やその他の移動する乗り物用
  • Terrain Collider・・・Terrain との衝突を処理します

 


*今回はCapsule Colliderを例に見てみます
f:id:mochisakucoco:20210310053427p:plain

  • Is Trigger・・・オンにすると、コライダーはイベントのトリガーとなり、物理エンジンにより無視されます。
  • Material・・・使用する 物理マテリアル への参照。物理マテリアルによりコライダーが他と衝突したとき、どのように相互作用するかが決定されます。
  • Center・・・コライダーの位置。
  • Radius・・・コライダーの半径。
  • Height・・・コライダーの高さ。
  • Direction・・・オブジェクトの軸の向き。

 


CharacterController

CharacterControllerは簡単に基本的な機能の実装が可能な為、ある程度の物理的な法則を無視してでも動かす事が出来ます。

f:id:mochisakucoco:20210310054121p:plain

  • Slope Limit・・・コライダーが、指定した値以下の勾配(単位は度)のみを登るよう制限します。
  • Step Offset・・・指定された値よりも地面に近い場合にのみ、キャラクターが階段を登ります。キャラクターコントローラーの高さより大きくした場合エラーが発生します。
  • Skin width・・・2つのコライダーが、Skin Width の深さまで互いに食い込みます。Skin Width が大きくなると、ジッタが減ります。Skin Width が小さいと、キャラクターが動けなくなる場合があります。値を Radius の 10% に設定しておくとよいでしょう。
  • Min Move Distance・・・キャラクターが指定された値未満で動こうとしても、まったく動きません。これは、ジッタを減らすのに利用できます。ほとんどの場合、この値は 0 のままにしておいてください。
  • Center・・・これにより、ワールド空間でカプセルコライダーをオフセットしますが、キャラクターがどのように旋回するかには影響しません。
  • Radius・・・カプセルコライダーのローカル半径。これは基本的にコライダーの幅となります。
  • Height・・・キャラクターの カプセルコライダーの高さ。この値を変更すると、正負の方向で Y 軸に沿ってコライダーが拡大縮小されます。



結論としてRigidbody+αとCharacterControllerは用途でプロジェクトによって使い分けるといいと思います。
(*´ω`*)

当記事はアクションを細かく実装したいのでRigidbodyを活用するとうまく行くと思ったので、こちらをメインで使用したいと思っています。
簡単な横スクロールだったり、激しい動きが要求されないゲーム性ならCharacterControllerを使用しコードの記述を少なくエラー解消や修正がしやすい点もメリットになると思います。




現状のスクリプトでは階段も登れない為、次回はPlayerスクリプトをRigidbody用に変更したいと思います。

Unityでのゲーム開発 〜エイムカメラの設定〜

エイムカメラの設定

前回はキャラクターとカメラのスクリプトを作りました。(´-`).。oO
そんなに大した事やってないのに分からない事を調べる時間のが長い、、、

まあそんな訳で今回は簡単な設定をして見ようと思います。

TPSを触った事ある方はわかるかもしれませんが、武器を構えるとカメラが近くになり操作キャラが端によったりしますよね?今回はそれです٩( ᐛ )و


まず必要なのはメインカメラ(前回作成した"ThirdPersonCamera")の他のカメラを用意します。
"Hierarchy"で右クリックを押して"Camera"をクリックすれば作成可能です。名前は分かりやすく"AimCamera"とします。
f:id:mochisakucoco:20210301140045p:plain



AimCameraのTransformは下記の様に設定しました。
f:id:mochisakucoco:20210301141228p:plain

⇩⇩⇩⇩⇩実際のゲーム画面はコチラ⇩⇩⇩⇩⇩⇩

f:id:mochisakucoco:20210301141337g:plain


"AimCamera"はPlayerに追従する様に動かしたいのでPlayerの階層に紐づけて親子関係にします。
f:id:mochisakucoco:20210301143439g:plain



Playerスクリプトの変更

カメラの切り替えにはPlayerの操作で切り替わる事になる為、"Player"スクリプトに変更を加えます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{

    [SerializeField] private GameObject aimCamera;  //エイムカメラを紐づける
    [SerializeField] private GameObject thirdPersonCamera;  //第三者視点カメラを紐づける
    private float Horizontal;
    private float Vertical;
    Rigidbody rb;
    public float moveSpeed = 5f;  //移動速度を1秒間で何フレーム動かすのかの設定

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        Horizontal = Input.GetAxisRaw("Horizontal");
        Vertical = Input.GetAxisRaw("Vertical");

        if (Input.GetKeyDown(KeyCode.B))  //対応したキーを押されたら内部のプログラムが呼び出される
            {
                aimCamera.SetActive(!aimCamera.activeInHierarchy);
                thirdPersonCamera.SetActive(!thirdPersonCamera.activeInHierarchy);
            }
    }

    void FixedUpdate()
    {
        // カメラの方向から、X-Z平面の単位ベクトルを取得
        Vector3 cameraForward = Vector3.Scale(thirdPersonCamera.transform.forward, new Vector3(1, 0, 1)).normalized;

        // 方向キーの入力値とカメラの向きから、移動方向を決定
        Vector3 moveForward = cameraForward * Vertical + thirdPersonCamera.transform.right * Horizontal;

        // 移動方向にスピードを掛ける。ジャンプや落下がある場合は、別途Y軸方向の速度ベクトルを足す。
        rb.velocity = moveForward * moveSpeed + new Vector3(0, rb.velocity.y, 0);

        // キャラクターの向きを進行方向にする(キャラクターの向きの設定は"ThirdPeasonCamera"が有効の場合のみ)
        if (moveForward != Vector3.zero && thirdPersonCamera.activeInHierarchy)
        {
            transform.rotation = Quaternion.LookRotation(moveForward);
        }
    }
} 

上記の内容を少し深堀していきます。




スクリプト変更部分

[SerializeField] private GameObject aimCamera;  //エイムカメラを紐づける
[SerializeField] private GameObject thirdPersonCamera;  //第三者視点カメラを紐づける

UnityのInspectorで編集可能にする為にフィールド宣言をしています。
上記の内容が正常に動作していれば"Inspector"ないの"Player"のスクリプトが変わっていると思います。
f:id:mochisakucoco:20210303004133p:plain



if (Input.GetKeyDown(KeyCode.B))  //対応したキーを押されたら内部のプログラムが呼び出される
     {
        aimCamera.SetActive(!aimCamera.activeInHierarchy);
        thirdPersonCamera.SetActive(!thirdPersonCamera.activeInHierarchy);
     }

void Update()の部分に追記した内容は[B]キーが押された時にHierarchy上にあるAimCameraとThirdPersonCameraのアクティブ状態を反転(on・off)させる内容となっています。

下記の様にプロジェクト上ではオブジェクトの名前の横のチェックボックスで簡単に切り替えられます。
しかしゲームプレイ中に編集画面が表示されている訳ではないので、プログラムを組み切り替え出来る様にしている状態です。


*重要)カメラを2つともアクティブ状態にしてしまうと同時にoffになってしまうのでゲーム開始時には下記の様な方法で"AimCamera"のチェックボックスは外しておいて下さい。

f:id:mochisakucoco:20210303011217g:plain



void FixedUpdate()
     {
        // カメラの方向から、X-Z平面の単位ベクトルを取得
        Vector3 cameraForward = Vector3.Scale(thirdPersonCamera.transform.forward, new Vector3(1, 0, 1)).normalized;

        // 方向キーの入力値とカメラの向きから、移動方向を決定
        Vector3 moveForward = cameraForward * Vertical + thirdPersonCamera.transform.right * Horizontal;

void FixedUpdate()の部分の"Camera.main"⇨"thirdPersonCamera"へ変更します。

  ⇩⇩⇩変更前⇩⇩⇩

  • Vector3 cameraForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
  • Vector3 moveForward = cameraForward * Vertical + Camera.main.transform.right * Horizontal;

 

// キャラクターの向きを進行方向にする(キャラクターの向きの設定は"ThirdPeasonCamera"が有効の場合のみ)
if (moveForward != Vector3.zero && thirdPersonCamera.activeInHierarchy)
     {
        transform.rotation = Quaternion.LookRotation(moveForward);
     }

if (moveForward != Vector3.zero)の部分に"&& thirdPersonCamera.activeInHierarchy"を追記
三者視点カメラの時にキャラクターが向いている方向の時に進む処理に変更。


スクリプト設定後の細かい調整

カメラの優先度設定

f:id:mochisakucoco:20210304003311p:plain
今回の設定ではActive状態をoffで始めているのであまり意味あはりませんが、カメラ深度についての説明になります
Depthはカメラの優先順位とおもって頂ければ大丈夫です。大きい値を持つカメラが、小さい値のカメラの手前に描画されます。
試しに"AimCamera"と"ThirdPersonCamera"のActive状態を両方とも有効にして、この数値を片方の数値より高くすると映されるカメラが切り替わると思います。
その他のリファレンスはこちら⇩
カメラ - Unity マニュアル


Playerスクリプトへカメラを反映

f:id:mochisakucoco:20210304022004g:plain



ゲーム画面の確認

上記の設定が正常に動作している場合は⇩の様になっている筈です。
f:id:mochisakucoco:20210304034021g:plain

今回はここまでで٩( ᐛ )و



Unity用語

[SerializeField]・・・シリアル化は、データ構造やオブジェクトの状態を Unity が保存して後で再構成できる形式に変換する自動プロセスです。 Unityのビルトイン機能の中には、シリアル化を使用するものがあります。保存とロード、インスペクターウィンドウ、インスタンス化、プレハブなどの機能が含まれます。


activeInHierarchy・・・シーン中のGameObjectが有効かどうかを取得できるプロパティGameObject.activeInHierarchyの他にもGameObject.activeSelf があります。


&&・・・条件演算子C#意外でも使われるプログラミング用語「&&」はAND(かつ)。「||」はOR(または)の意味となります。





⇩⇩⇩⇩⇩⇩前回の記事はこちら⇩⇩⇩⇩⇩
mochisakucoco.hatenablog.com

Unityでのゲーム開発 〜キャラクターとカメラScript〜

キャラクターの移動スクリプト

まずScript(今後のスクリプト)を書く前に保存するフォルダーの作成します。
プロジェクトの[Asset]にカーソルを合わせて右クリック[Create]⇨[Folder]を選択してフォルダ名を「Scripts」に変更してください。
*複数のスクリプトを保存するので今回は複数形の["s"]を付けていますが自分のわかりやすい名前でOK

f:id:mochisakucoco:20210228001111p:plain


スクリプトの作成方法

f:id:mochisakucoco:20210228001705p:plainf:id:mochisakucoco:20210228001717p:plain

スクリプトフォルダで右クリックを押して[Create]⇨[C#Script]の手順でスクリプトを作成してそのまま名前を変更します。
*ここでの名前変更は重要な役割があり、変更した際にクラス名も一緒に決定して作成します。
!注)スクリプトの名前とスクリプト内のクラス名はリンクしている為、どちらかを間違えて変更してしまった場合はどちらかに合わせる様にしてください。




今回は[Player]と[ThirdPersonCamera]の2つを作成してください。
ついでにキャラクター名も長すぎるので"ThirdPersonController_LITE" ⇨ "Player"へ変更しておくとお今後楽です。
*名前の変更方法は変更したい項目で右クリックをして[Rename]を選択するかHierarchyの上部にある名前の場合から変更可能です。
前回の記事でPlayerに付いていた"Animator"も復活させておいてください。
無くても良いのですがキャラクターがカカシの様な状態になってしまうので分かりづらい場合がある為、のちのちAnimatorの設定もいじるので今回はアタッチされている物を使います。



Playerスクリプトの記述

こちらの記事を参考に致しました。
Unityでカメラの向きを基準に移動する方法と、追従して回転できるカメラの実装

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{

    private float Horizontal;
    private float Vertical;
    Rigidbody rb;
    public float moveSpeed = 5f;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        Horizontal = Input.GetAxisRaw("Horizontal");
        Vertical = Input.GetAxisRaw("Vertical");
    }

    void FixedUpdate()
    {
        // カメラの方向から、X-Z平面の単位ベクトルを取得
        Vector3 cameraForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;

        // 方向キーの入力値とカメラの向きから、移動方向を決定
        Vector3 moveForward = cameraForward * Vertical + Camera.main.transform.right * Horizontal;

        // 移動方向にスピードを掛ける。ジャンプや落下がある場合は、別途Y軸方向の速度ベクトルを足す。
        rb.velocity = moveForward * moveSpeed + new Vector3(0, rb.velocity.y, 0);

        // キャラクターの向きを進行方向に
        if (moveForward != Vector3.zero)
        {
            transform.rotation = Quaternion.LookRotation(moveForward);
        }
    }
}

スクリプトが出来たら下記の様にスクリプトを"Hierarchy"もしくは"Inspector"にドラック&ドロップしてください
f:id:mochisakucoco:20210301105041p:plain
移動処理が問題なければ下記の様な動きになると思います。
f:id:mochisakucoco:20210301104405g:plain



ThirdPersonCameraの記述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ThirdPersonCamera : MonoBehaviour
{

    GameObject targetObj;
    Vector3 targetPos;

    void Start ()
    {
        targetObj = GameObject.Find("Player");
        targetPos = targetObj.transform.position;
    }

    void Update()
    {
    // targetの移動量分、自分(カメラ)も移動する
    transform.position += targetObj.transform.position - targetPos;
    targetPos = targetObj.transform.position;

    // マウスホイールをスクロールするとドリーイン・ドリーアウトする
    float scroll = Input.GetAxis("Mouse ScrollWheel");
    transform.position += transform.forward * scroll;

        // マウスの右クリックを押している間
        if (Input.GetMouseButton(1))
        {
            // マウスの移動量
            float mouseInputX = Input.GetAxis("Mouse X");
            float mouseInputY = Input.GetAxis("Mouse Y");
            // targetの位置のY軸を中心に、回転(公転)する
            transform.RotateAround(targetPos, Vector3.up, mouseInputX * Time.deltaTime * 200f);
            // カメラの垂直移動(※角度制限なし、必要が無ければコメントアウト)
            transform.RotateAround(targetPos, transform.right, mouseInputY * Time.deltaTime * 200f);
        }
    }
}

アタッチする前に"MainCamera"を"ThirdPersonCamera"に名前を変えて下さい。
名前の変更が完了したら上記のPlayerの時と同じ様に"ThirdPersonCamera"へスクリプトをアタッチしてください。
f:id:mochisakucoco:20210301110154g:plain


!今回はここまで次回はエイムカメラの設定をやってみたいと思います。








Unity用語

〜共通部分〜

using 〇〇;・・・これはC#スクリプトを作成した際に一緒に作成されるもので、using(使用するとゆう意味)な為usingの後に続く〇〇を使用しますって意味になります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

上記のusing以降の" UnityEngine;"等は「名前空間」と呼ばれています。
色々な設定をまとめている物だと思っておけば良いと思います。特に「using UnityEngine;」はUnityで使用する色々な要素をまとめてくれている物です。
*コレがないとエラーになります_:(´ཀ`」 ∠):

用意された物だけでなく作成した物を下記の様にすると定義する事もできます。

namespace 〇〇;

定義付けしたら他のスクリプトで[using 〇〇;]でアクセス可能になります。

*namespaceのメリットはスクリプトの名前被りを避けるのにも役に立ちます。よく使うスクリプトは被ってしまう為DLしたAssetを複数使うとより被るリスクが増えますがnamespaceを利用すればスクリプト名が被っていても使用できます。


public class 〇〇 : MonoBehaviour・・・〇〇の部分はスクリプト作成した際に生成される為に作成する事に変わります。〇〇クラスを定義する宣言になります。


MonoBehaviour・・・Unityプログラミングにおいて用いられる全てのクラスの基底クラス(親クラス)となるクラスです。もともとUnity上で定義されているクラスであり、プログラマはここから派生して様々な派生クラス(子クラス)を作製します。
Unityで作られるゲーム中で出てくる物体などの動作のきっかけ(イベント)で動かす処理のかたまり(メソッド)等をつなぐ役割をしています。
*場合によって変更する場面も出てくるかと思います。
UnityEngine.MonoBehaviour - Unity スクリプトリファレンス


void Start()・・・プロジェクト開始時に1度だけ呼び出されて処理される項目をプログラムする場所だと思って貰えれば良いと思います。


void Update()・・・プロジェクトでキー入力等で何度も呼び出されて実行される処理項目をプログラムする場所だと思って貰えれば良いと思います。



〜Player部分スクリプト

private・・・アクセス修飾子と呼ばれるものです。アクセス修飾子は、クラスや変数等のアクセスレベルを制限するために定義して、他のクラスから参照できるか否かを調整するためのキーワードとなります。
アクセス修飾子によって、変数やクラスが自分のクラスでしか参照できないのか、外部からも参照できるのかを制限できるので、不用意に値を編集できなくする事も可能な為チーム開発の際には重要な項目になります。

アクセス修飾子の種類
public:この型またはメンバーには、同じアセンブリ内の他のコードや、そのアセンブリを参照する別のアセンブリ内の任意のコードからアクセスできます。
private:この型またはメンバーには、同じ class 内または同じ struct 内のコードからのみアクセスできます。
protected:この型またはメンバーには、同じ class 内のコードか、その class から派生した class 内のコードからのみアクセスできます。
internal:この型またはメンバーには、同じアセンブリ内の任意のコードからアクセスできますが、別のアセンブリからはアクセスできません。
protected internal:この型またはメンバーには、それが宣言されているアセンブリ内の任意のコードからアクセスできるほか、別のアセンブリの派生 class 内からアクセスすることができます。
private protected:この型またはメンバーには、同じ class のコードか、その class から派生した型のコードによって、その宣言アセンブリ内でのみ、アクセスできます。

詳しくはこちらから⇩
アクセス修飾子 - C# プログラミング ガイド | Microsoft Docs


Input.GetAxisRaw・・・ジョイスティックやマウスなどの移動量を、-1.0~1.0の数値で取得する関数。


Horizontal・・・カーソルキーのAキーDキー
マウスの横向きの移動
ゲームパッドの横向きの傾き
*Unityのシステムでこのワードだけで上記の処理を出来る様にしてくれます


Vertical・・・カーソルキーのWキーSキー
マウスの縦向きの移動
ゲームパッドの縦向きの傾き
*Unityのシステムでこのワードだけで上記の処理を出来る様にしてくれます


void FixedUpdate()・・・FixedUpdateメソッドを使う代表的な例は物理演算を行うときです。

Updateメソッドに物理演算を書くと、端末の描画速度によりUpdateメソッドが呼ばれるタイミングにラグなどが出てしまうので、物体の移動がカクカクになったり物理演算の結果に再現性が保てなくなってしまう可能性があります。
FixedUpdateメソッドの方は、Updateメソッドがフレーム落ちしていても、独立して一定時間ごとに呼び出されるので、物理演算の結果再現性が保てるようになります。

ブッ、、、物理演算_:(´ཀ`」 ∠):なんぞやそれって方はWikipediaさんに聞いてみましょう
物理演算エンジン - Wikipedia

Physics」って単語はよく使用すると思うので覚えておいてもいいと思います。


Forward・・・前方とゆう意味なのでVector3(0, 0, 1) と同じになります。


Quaternion.LookRotation・・・Quaternion[クォータニオン]は回転を表すのに使用されます。
LookRotationは指定された forward と upwards 方向に回転します。

  • forward・・・前方向。Vector3(0, 0, 1) と同じ意味。
  • upwards・・・上方向を定義するベクトルです。 Vector3(0, 1, 0) と同意。

ふーん、、、⇨Quaternion-LookRotation - Unity スクリプトリファレンス



〜ThirdPersonCamera部分スクリプト

Time.deltaTime・・・現在のフレームと前のフレームの間の経過した時間。
これを使えば方向キーを押している間、移動方向に繰り返し移動処理をする事が出来る様になります。
MonoBehaviour.FixedUpdateは、deltaTimeの代わりにfixedDeltaTimeを使用します。

Unityでのゲーム開発 〜キャラクターの配置・カメラ設定〜

キャラクターの設置

当記事から新しいワードや重要な項目は記事最後に説明を載せます。



まずはキャラクターを配置する為に下準備をしていきます。
[Hierarchy]にカーソルを持っていきマウスの右クリックでメニュー表示をさせます。
[3D Object]⇨[Plane]で平面をゲームシーン内に設置します。

*Planeの設置する理由としてはキャラクターを配置しても地面がないとずっと下に落ちてしまう為です。

f:id:mochisakucoco:20210218170119p:plain

[Inspector]内の[Transform]とゆう項目があると思います。
色々な場面で出てくる概念な為、把握しておくと便利だと思います。
TransformのPositionの値を(X:0 Y:0 Z:0) Scaleの値を(X:100 Y:1 Z:100)に変更します。

実際にシーン内にキャラクター設置

f:id:mochisakucoco:20210218171459g:plain

上記の通り、前回Asset Storeからダウンロードした[Invector-3rdPersonController_LITE]⇨[Prefabs]の順にクリックしてPrefabsの中の[ThirdPersonController_LITE]を上にあるシーン内にドラック&ドロップをするかHierarchyにドラック&ドロップするとキャラクターが反映されます。
反映されたキャラクターのTransformのPosition値を(X:0 Y:0 Z:0)に変更するのも忘れずに。


・・・簡単ですね٩( 'ω' )و

こんな簡単にキャラクターを配置できるのはUnityのシステム自体もそうですが、[Asset Store]やアドビのサービスの[Mixamo]などクリエイターさんが無料で使用出来る仕組みを組んでくれているお陰です!ありがたいですね(・∀・)



f:id:mochisakucoco:20210220224054p:plain
今回の使用しているAssetには、[Script]、[Animator]、[Rigitbody]、[Capusule Collider]が標準で付いてます。
これだけでもキャラクターを動かす事ができます。今回はセットされている[Script]、[Animator]使用しない予定なので[V Comment]〜[V Third Person Input]まで削除してください。[︙]を押してRemove Componentをクリックすると削除されます。


*他の項目でも間違えってアタッチしてしまったスクリプトなどはこの方法で削除可能です。
*今回のキャラクターにはPrefab化されている為、削除してもグレーの文字で残ります。新しく作成した物などに関しては綺麗に消えます。


*補足としてEdit Scriptを押すとコード内容の確認もできます。今回のスクリプトはTPSの内容が多いので参考にする為、グレーのまま残しておいてもいいと思います。


*完全に削除する方法は⇩
f:id:mochisakucoco:20210221011241p:plain

文字がグレーの時に[︙]を押してRemove Componentをクリックすると[Apply to Prefab "~~"]の項目を選択すると完全に削除可能です。
*Apply(適用するとゆう意味)なので、Prefabにも適用する事になる為DLしたPrefab自体に変更を加える事になります。



カメラの取り付け

f:id:mochisakucoco:20210221014113p:plain

カメラを追従させる方法として簡単な方法としてキャラクターに取り付ける方法を紹介します。(スクリプトなどを利用する方法も載せていければと思っています。)

今回は[Main Camera]のトランスフォームポジションを変更します。
画像の通り、X軸は横方向、Y軸は上下方向、Z軸は奥行きの位置を変更できます。

TPS視点になる為少し後方を追従する様にしたい為、(X0.5 Y1.75 Z-1.7)に変更します。

キャラクターの位置を(X:0 Y:0 Z:0)に変更しているはずなので、X軸を0.5とすると右方向に少し動き、Y軸を1.75とすると上方向に動き、Z軸を-1.7としているのでキャラクターより手前側にカメラが移動する事になります。
*数値の前にー(マイナス)を入れれば逆方向に動きます。今回はキャラクターの位置をわかりやすく0にしている為数値をいじりましたが本来はシーン上で位置を確認しながらの変更がの望ましいです。


上記の設定が終わったら、メインカメラをキャラクターの親子関係にさせる為にドラック&ドロップするだけです。
!注)動きの確認が取れたら階層から戻しておいて下さい、次回以降は別の階層としての内容となる為


今回は以上で次回からはキャラクターを動かすスクリプトなどを変更したいと思います。

Unity用語

Hierarchy・・・Hierarchy (ヒエラルキー) ウィンドウには現在のシーンにおける「オブジェクト」のリストが表示されます。ゲームを構成するオブジェクトはシーンの中で加えたり、削除したりするので、Hierarchy ウィンドウでも、表示されたり、消えたりします。

Inspector・・・Unityのプロジェクトは、スクリプトサウンド、メッシュ、その他のグラフィカルな要素 (ライトなど) を含む複数のゲームオブジェクトから構成されています。Inspector ウィンドウ (「インスペクター」とも呼ばれます) は、現在選択されているゲームオブジェクトに関する詳細な情報を、ゲームオブジェクトにアタッチされた コンポーネント やそのプロパティーの情報も合わせて表示します。また、Inspector ウィンドウではシーンのゲームオブジェクトの機能を変更することができます。

Transform・・・TransformはゲームオブジェクトのPosition(位置)・Rotation(回転)・Scale(スケール[拡大・縮小])と親子関係を保持するので非常に重要です。ゲームオブジェクトは常時Transformコンポーネントをアタッチしているため、Transform コンポーネントをはずす事はできず、1つのゲームオブジェクトに対して1つしか存在できません。
*Transformは3次元空間(3D)であれば X ・ Y ・ Z 軸、2次元空間(2D)であれば X ・ Y 軸を Unity 上で操作できます。

Prefab・・・プレハブはテンプレートとして使われる、シーン内に新しいオブジェクトインスタンスを作成することができる。自分で編集作成をしたオブジェクトは定期的に保存をしていつでも使える様にテンプレート化をする事をオススメします。

Remove Component・・・ゲームオブジェクトにアタッチしているコンポ―ネントがもう必要でない場合に利用するコマンドです。

Apply・・・Apply(アプライ)適用するとゆう意味、変更加えた場合にアプライしないと反映されない場合もある為注意が必要。

Unityでのゲーム開発 〜プロジェクト起動からAsset導入〜

Unityのプロジェクト起動

!今回の記事は写真が多くなっております

Unityバージョン:[2020.2.4f1]

Unity Hubを起動して新規作成ボタンから新規プロジェクトを起動します。

プロジェクト名を好きな名前に変更、今回は「Unity_3D_Game」で作成します。

変更が完了したら「作成」を押して
*バージョンが違う場合は新規作成の横の🔽でバージョン管理可能。

f:id:mochisakucoco:20210215152718p:plain

Unity Hubのインストール方法⇩
Unity Hub のインストール - Unity マニュアル


UnityからAsset導入

f:id:mochisakucoco:20210215163029p:plain

①のUnityを起動時のPC上部の「Window」をクリック
②「Asset Store」とゆう項目が出てくるのでクリックをすると下記の様なUnity内にAsset Storeのタブが生成される為、タブ内部の「Search online」をクリックしてAsset Storeへ飛びます。
f:id:mochisakucoco:20210215164101p:plain


!Asset Storeの公式ページの関してはレイアウト等の変更がかかる場合がある為現状での説明となります。
!注)Aseet Storeの利用にはアカウント登録などが必要になる為、先に登録する事をオススメします。

下記の「アセット」「ツール」「サービス」「Unity公式」「業界別」など項目事にプルダウン出来る様になっている為、使用したい項目を選んでください。
f:id:mochisakucoco:20210215165047p:plain

f:id:mochisakucoco:20210215165505p:plain



今回は3Dゲーム作成の為「3D」項目を選択しました。
下記の画像の様に、絞り込みからカテゴリー項目のチェックを入れる事で探す事も可能です。
f:id:mochisakucoco:20210215170515p:plain



*今回使用する予定のAssetです

⇩下記は有料Asset
assetstore.unity.com
assetstore.unity.com


⇩下記は無料Asset
assetstore.unity.com


今回のキャラクターとして無料のAssetからお借りしようと思います。
色々と行動のプログラミングを書いたら、キャラクターも変更する予定です。

今回の有料Assetはアニメーションの部類になる為、プロジェクトが変更になってもアニメーションによっては使い回しも出来ます。
1つ1つの金額は高いと思いますが複数のプロジェクトに使用できる為、利点は大きいかと思います。

Assetのインストールとプロジェクトでの使用

f:id:mochisakucoco:20210216200904p:plain


Unityを起動時のPC上部の「Window」をクリック

「Package Manager」とゆう項目が出てくるのでクリックをすると、Package Managerのタブが生成されているのでAsset Storeでダウンロードした物が表示されていると思います。

f:id:mochisakucoco:20210216211918p:plain
f:id:mochisakucoco:20210216211947p:plain
f:id:mochisakucoco:20210216212018p:plain

上記の手順でAsset Storeからダウンロードした物は、まだUnityでは使用できない状態なので「Download」をクリックすると「Import」に変わるのでインポートします。
別のウインドが表示されるので「Import」をクリックするとプロジェクト内で使用できる様になります。
*Assetによってダウンロードとインポートの時間が異なります。



f:id:mochisakucoco:20210216212818p:plain
Assetによってはスクリプトが一緒にインストールされる物がある為、プロジェクトに途中から組み込む場合にエラー表示がされる事があります。
Asset自体が使用できない訳ではない事が多い為、何かしらエラーの原因になっている物があるのでコンソールでエラーの内容など解消すれば使える様になると思います。

*複数のAssetを利用する場合は上記の画像の様に1つダウンロードしたら「プレイ」ボタンでプロジェクトが正常に動作するか確認をとった方が確実だと思います。

次回以降は実際にプロジェクトを触ってこうと思います。

Unityでのゲーム開発 〜ブログについて〜

当ブログについての注意点

まず当ブログに関して、投稿者自身が他の方の記事やUnityのリファレンスを見て独自の解釈が入ってしまっている場合がありますので、参考記事としてではなく投稿者自身の備忘録として投稿している物となります。

簡単なプロジェクトは何個か作成している為、初歩的なUnityの導入方法やその他使用ツールの設定は省かせていただく事があります。
Assetに関しても無料の物だけでなく有料のAssetも使用していく予定です。

基本的には不定期更新になってしまうと思いますが、長い目で見ていただけるとありがたいです。٩( 'ω' )و



それでは作成していきたいゲーム開発の企画(仕様書)をざっと上げて行こうと思います。

企画(仕様書)

  • TPSゲーム
  • スチーム(蒸気)をテーマとした作品(なんとなく・・・)
  • メインとしてワイヤーアクションを組み込みたい(ロストプラネットが好きだった為)
  • マルチプレイ戦(Gears of Warみたいな4対4が理想)
  • 銃撃戦と近接戦も出来る様なバランス調整
  • キャラクターメイクなどは今回は無しで既存キャラを複数体用意(のちのち自作してみたい)


もっと細かな仕様などはプロジェクトを進めていく上で変更する場合もあります。
昔ハマっていたGears of Warロストプラネット合わせたら面白いんじゃないかと単純な発想でやっていきたいと思います。
テーマ性がないとと思い蒸気系を駆使したギミックなんかあると面白いかなと思い上げてみました( ˊ̱˂˃ˋ̱ )



次回からプロジェクトの作成からAssetの準備などしていきたいと思います(形から入る主義・・・笑