『UNITYゲーム プログラミング・バイブル』「ENTRY No.01 3D RUNゲーム」学習記録2

Unityゲーム プログラミング・バイブル

Unityゲーム プログラミング・バイブル

序文

『UNITYゲーム プログラミング・バイブル』ブログ更新日換算で2日目です。

もう7月半ばですか…8月末までに第一弾アプリ完成させる予定なんですが…ほとんど進捗してない…

進捗

  • ENTRY No.01 3D RUNゲーム
    • Unityアニメーションの基礎知識

(学習時間:3時間)

コード実装部分

\Theme_3D\No01_RunGame\No01_RunGame\RunGame\Assets\Scripts\Field\Character\RunCharacter.cs

using UnityEngine;
using System.Collections;

// Animatorコンポーネントが必須であることを示す
[RequireComponent(typeof(Animator))]
// CharacterControllerコンポーネントが必須であることを示す
[RequireComponent(typeof(CharacterController))]
public class RunCharacter : MonoBehaviour
{
    // runステートをあらわす定数
    static readonly int runState = Animator.StringToHash("Base Layer.Run");
    // jumpステートをあらわす定数
    static readonly int jumpState = Animator.StringToHash("Base Layer.Jump");

    // CharacterControllerコンポーネント
    // SerializeField属性を付与することで
    // Unity上でprivateな値を扱えるようになる
    [SerializeField] CharacterController charController;

    // Animatorコンポーネント
    Animator animator;
    // 現在のステートをあらわす変数
    AnimatorStateInfo currentState;
    // 筆者の独自クラス(障害物ジャンプの一連を管理している)
    StageEvent currentStageEvent;

    // キャラクターが走る速度
    float speed = 0f;

    // Transformコンポーネント(変数)
    Transform cachedTransform = null;
    // Transformコンポーネント(プロパティ)
    Transform CachedTransform
    {
        // ??はNull合体演算子(知らなかった!)
        // cachedTransformがnullなら
        // cachedTransformにtransformを代入して、cachedTransformを返す
        get { return cachedTransform ?? (cachedTransform = transform); }
    }

    // 具体的な位置を表すプロパティ
    public Vector3 Position
    {
        get { return CachedTransform.position; }
        set { CachedTransform.position = value; }
    }

    // 大まかな中心位置
    public Vector3 CenterPosition
    {
        get
        {
            // キャラクターの中心位置をCharacterControllerコンポーネントからローカル座標で取得し、ワールド座標に変換して返す
            return CachedTransform.TransformPoint(charController.center);
        }
    }

    // 障害物ジャンプのイベント中かどうかを返すプロパティ
    bool InEvent { get { return (currentStageEvent != null); } }

    // 現在のステートがjumpであるかを返すプロパティ
    bool IsJumpState { get { return (currentState.fullPathHash == jumpState); } }

    // キャラクターを走らせてよいかを判定するプロパティ
    public bool IsAvairableRun
    {
        get
        {
            // 障害物ジャンプに失敗したときはfalse(ゲームオーバー)
            if (InEvent && currentStageEvent.IsFailed) return false;
            // Jumpステートでなければtrue
            if (!IsJumpState) return true;
            // その他の場合はfalse(Jumpステート中など)
            return false;
        }
    }

    // ユーザーのインプットを受け付けてよい状態かどうかを判定するプロパティ
    public bool IsAvairableInput
    {
        // jumpステート中でなければユーザーのインプットを受け付ける
        get { return !IsJumpState; }
    }

    // 定数じゃダメなの・・・?
    float JumpAnimationSpeed { get { return 1f; } }

    void Start()
    {
        // Animatorコンポーネントを取得
        this.animator = GetComponent<Animator>();
    }

    void Update()
    {
        // キャラクターの加速値
        const float accelPerSecond = 0.5f;

        // 自動でまっすぐ走らせる
        // 走らせても良い状態なら
        if (IsAvairableRun)
        {
            // フレーム間隔を考慮して加速させる
            speed += accelPerSecond * Time.deltaTime;
        }
        // ジャンプ中なら
        else if (IsJumpState)
        {
            // ジャンプの速度は定数
            speed = JumpAnimationSpeed;
        }
        // それ以外ならストップ
        else
        {
            speed = 0f;
        }
        // speedは0~1までの間に収める
        speed = Mathf.Clamp01(speed);

        // 前進させる
        UpdateMove();

        // トリガーになっているSpeedパラメタに値を渡す
        animator.SetFloat("Speed", speed);          // Animator側で設定している"Speed"パラメタに値を渡す
        // 現在のステートを取得する
        currentState = animator.GetCurrentAnimatorStateInfo(0); // 参照用のステート変数にBase Layer (0)の現在のステートを設定する
        // 現在のステートがrunなら
        if (currentState.fullPathHash == runState)
        {
            // ジャンプのチェックを行う
            UpdateStateRun();
        }
    }

    // キャラクターを前進させる
    void UpdateMove()
    {
        // runステートに切り替えるしきい値
        const float runThreshold = 0.1f;
        // run中の前進速度
        const float runSpeed = 7f;      // 前進速度
        // jump中の前進速度
        const float jumpSpeed = 4.5f;       // ジャンプ中の前進速度

        // 前進速度の基本値
        // これにrunSpeedやjumpSpeedを乗算して最終的な前進速度とする
        float forwardSpeed = speed;
        // speedがrunステートに切り替えるしきい値以上なら
        if (speed > runThreshold)
        {
            // forwardSpeedに最終的な前進速度を設定する
            forwardSpeed *= (IsJumpState) ? jumpSpeed : runSpeed;
        }

        // forwardSpeedを移動量に変換
        Vector3 velocity = new Vector3(0f, 0f, forwardSpeed);   // 上下のキー入力からZ軸方向の移動量を取得 (← そんな処理してなくない…??????????
        // ローカル座標からワールド座標に変換
        velocity = CachedTransform.TransformDirection(velocity); // キャラクターのローカル空間での方向に変換
        // キャラクターのローカル座標を移動
        CachedTransform.localPosition += velocity * Time.deltaTime;
    }

    // ジャンプのチェックを行う
    void UpdateStateRun()
    {
        // イベント領域外のときに自由にジャンプできるようにする
        // インプットを受け付ける状態かチェックする
        if (!IsAvairableInput) return;
        // イベント中でないかチェックする
        // 障害物が登場した時のジャンプは別のクラスで管理している模様
        if (InEvent) return;
        // クリックされたらジャンプする
        if (!InputController.GetClick()) return;
        Jump();
    }

    // イベントに入ったときの処理
    public void OnEventEnter(StageEvent stageEvent)
    {
        // 現在のEventを設定する
        currentStageEvent = stageEvent;
    }

    // イベント終了時の処理
    public void OnEventExit()
    {
        // 現在のイベントをnullに戻す
        currentStageEvent = null;
    }

    // ジャンプ処理
    public void Jump()
    {
        // ステート遷移中なら無視する
        if (animator.IsInTransition(0)) return;
        // "Jump"トリガーをオンにする
        animator.SetTrigger("Jump");
    }
}

実行結果

www.youtube.com

感想

アニメーション機能の概要は昨日勉強したので、今日は完成プロジェクトのアニメーションまわりをひたすら読み解きました…。

コードを読み解きつつ、Unityの画面もいったりきたりして大変。

とりあえずアニメーションまわりのコードに、ひたすらコメントを書いてみましたが、↑のコード以外にも筆者の方の自作クラスが大量に入り組んでて、完璧な理解にはほど遠い感じです。

単純な障害物をタップでジャンプしてよけるだけのアプリなんですが、障害をよけるジャンプと、それ以外のジャンプを別のクラスで管理していたりして…本当にもう。(いや、必然性はあるんだろうけど)

それでも、Is~みたいな判定処理をプロパティとして実装するとか、Null合体演算子とか未知の内容もたくさん出てきて、少しずつレベルアップしていきたいと思います。

カロリーメイトください。

参考サイト

docs.unity3d.com transform.TransformPointメソッドについて。(よくわかってない)

kuroeveryday.blogspot.com null合体演算子について

teratail.com SerializeField属性について

ja.stackoverflow.com 特定のスクリプトがアタッチされているオブジェクトの探し方

dymn.hatenablog.com animator.IsInTransition()メソッドについて

BGM

恋わずわず / lyrical school

www.youtube.com

この曲(恋わずわず)HALCALIのフワフワ・ブランニューと同じコンビなのよねー「現在、過去、フューチャー!」