Q: Unity ステート マシンのコード例。少なくとも 4 つの状態が必要です
A: 以下は、C# で書かれた、Unity エンジンに基づく単純なステート マシンのサンプル コードです。ステート マシンには、待機、移動、攻撃、負傷の 4 つの状態が含まれます。スタンバイ状態では、プログラムはユーザーが「スペース」キーを押すのを待ってから、移動状態に移行し、移動アニメーションを再生します。移動状態では、プログラムはユーザーが「スペース」キーを押すのを待ってから、攻撃状態に移行し、攻撃アニメーションを再生します。攻撃状態では、プログラムはしばらく待機した後、待機状態に移行し、待機アニメーションを再生します。負傷状態では、プログラムは一定時間待機した後、スタンバイ状態に移行し、スタンバイ アニメーションを再生します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SimpleStateMachine : MonoBehaviour
{
private enum State { Idle, Move, Attack, Hurt };
private Animator animator;
private State currentState = State.Idle;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
switch (currentState)
{
case State.Idle:
if (Input.GetKeyDown(KeyCode.Space))
{
currentState = State.Move;
animator.SetBool("isMoving", true);
}
break;
case State.Move:
if (Input.GetKeyDown(KeyCode.Space))
{
currentState = State.Attack;
animator.SetBool("isMoving", false);
animator.SetBool("isAttacking", true);
StartCoroutine(ResetStateAfterDelay(1.0f, State.Idle));
}
break;
case State.Attack:
// Do nothing. Wait for animation to complete and reset state.
break;
case State.Hurt:
// Do nothing. Wait for animation to complete and reset state.
break;
}
}
private IEnumerator ResetStateAfterDelay(float delay, State state)
{
yield return new WaitForSeconds(delay);
currentState = state;
animator.SetBool("isAttacking", false);
}
public void TakeDamage()
{
currentState = State.Hurt;
animator.SetTrigger("isHurt");
StartCoroutine(ResetStateAfterDelay(1.0f, State.Idle));
}
}
この例では、列挙型State を使用して状態を表し、currentState変数を使用して現在の状態を追跡します。Updateメソッドでは、 switchステートメントを使用して対応するアクションを実行し、状態を次の状態に移動します。Unity に付属する Animator コンポーネントを使用してアニメーションの再生を制御し、コルーチンを使用して状態がリセットされるまで一定時間待機します。最後に、ダメージを受けた状態でアニメーションをトリガーし、状態をリセットするTakeDamageというパブリック メソッドを追加しました。
Q: 上記のステートマシンをゲームキャラクターに適用してください
A: わかりました。上記のステート マシンをゲーム キャラクターに適用するサンプル コードを次に示します。この例では、単純なキャラクター コントローラーを作成し、上記のステート マシンを使用してキャラクターの動作とアニメーションを制御します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private enum State { Idle, Move, Attack, Hurt };
private Animator animator;
private State currentState = State.Idle;
private Vector3 targetPosition;
private float moveSpeed = 5.0f;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
switch (currentState)
{
case State.Idle:
if (Input.GetKeyDown(KeyCode.Space))
{
targetPosition = transform.position + new Vector3(Random.Range(-2.0f, 2.0f), 0, Random.Range(-2.0f, 2.0f));
currentState = State.Move;
animator.SetBool("isMoving", true);
}
break;
case State.Move:
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
if (transform.position == targetPosition)
{
currentState = State.Attack;
animator.SetBool("isMoving", false);
animator.SetBool("isAttacking", true);
StartCoroutine(ResetStateAfterDelay(1.0f, State.Idle));
}
break;
case State.Attack:
// Do nothing. Wait for animation to complete and reset state.
break;
case State.Hurt:
// Do nothing. Wait for animation to complete and reset state.
break;
}
}
private IEnumerator ResetStateAfterDelay(float delay, State state)
{
yield return new WaitForSeconds(delay);
currentState = state;
animator.SetBool("isAttacking", false);
}
public void TakeDamage()
{
currentState = State.Hurt;
animator.SetTrigger("isHurt");
StartCoroutine(ResetStateAfterDelay(1.0f, State.Idle));
}
}
この例では、 PlayerControllerというスクリプトを作成し、それをゲーム オブジェクトにアタッチします。Startメソッドでオブジェクトの Animator コンポーネントを取得し、Updateメソッドで上記のステート マシンを使用してキャラクターの動作とアニメーションを制御します。
アイドル状態では、キャラクターは静止したままになり、ユーザーが「スペース」キーを押すのを待ちます。ユーザーが「スペース」キーを押すと、プログラムはランダムにターゲット位置を生成し、キャラクターをその位置に移動します。移動状態では、キャラクターは目標位置に到達するまで直線に沿って目標位置に向かって移動します。ターゲットの位置に到達すると、プログラムは攻撃状態に移行し、攻撃アニメーションを再生します。攻撃状態では、キャラクターは静止したままで、攻撃アニメーションの再生が終了するのを待ちます。最後に、プログラムはアイドル状態に移行し、ユーザーが再度「スペース」キーを押すのを待ちます。
また、ダメージを受けた状態でアニメーションをトリガーし、状態をリセットするTakeDamageというパブリック メソッドをキャラクターに追加しました。この例では、キャラクターが攻撃されたときに、他のゲーム オブジェクトがPlayerControllerのTakeDamageメソッドを呼び出して、キャラクターを Hurt 状態に切り替え、ダメージ アニメーションを再生できます。怪我のアニメーションの再生が終了すると、プログラムはアイドル状態に移行します。
PlayerControllerスクリプトを使用してゲーム内のキャラクターの動作を制御する方法の簡単な例を次に示します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour
{
public GameObject player;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
player.GetComponent<PlayerController>().TakeDamage();
}
}
}
上記の状態に加えて、ジャンプ、攻撃などの他の状態をキャラクターに追加することもできます。これらの状態はゲームのニーズに応じてカスタマイズできますが、状態間の移行プロセスは秩序立った論理関係に従う必要があります。
以下は、ステート マシンを使用してキャラクターの動作を制御する方法を示す完全な例です。ここでは、アイドル、移動、ジャンプ、攻撃の 4 つの状態をキャラクターに追加し、それらの間の遷移関係を定義します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Animator animator;
private Rigidbody2D rigidbody2d;
// Define states
private enum State
{
Idle,
Move,
Jump,
Attack
}
private State currentState;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
rigidbody2d = GetComponent<Rigidbody2D>();
currentState = State.Idle;
}
// Update is called once per frame
void Update()
{
switch (currentState)
{
case State.Idle:
HandleIdleState();
break;
case State.Move:
HandleMoveState();
break;
case State.Jump:
HandleJumpState();
break;
case State.Attack:
HandleAttackState();
break;
}
}
private void HandleIdleState()
{
// Transition to Move state
if (Input.GetKeyDown(KeyCode.Space))
{
currentState = State.Move;
animator.SetTrigger("StartMoving");
}
// Transition to Attack state
else if (Input.GetKeyDown(KeyCode.Z))
{
currentState = State.Attack;
animator.SetTrigger("StartAttacking");
}
}
private void HandleMoveState()
{
// Move left or right
float moveInput = Input.GetAxis("Horizontal");
rigidbody2d.velocity = new Vector2(moveInput * 5f, rigidbody2d.velocity.y);
// Transition to Jump state
if (Input.GetKeyDown(KeyCode.Space))
{
currentState = State.Jump;
animator.SetTrigger("StartJumping");
}
// Transition to Attack state
else if (Input.GetKeyDown(KeyCode.Z))
{
currentState = State.Attack;
animator.SetTrigger("StartAttacking");
}
// Transition to Idle state
else if (moveInput == 0)
{
currentState = State.Idle;
animator.SetTrigger("StopMoving");
}
}
private void HandleJumpState()
{
// Apply jump force
rigidbody2d.velocity = new Vector2(rigidbody2d.velocity.x, 10f);
// Transition to Move state
if (Input.GetKey(KeyCode.LeftArrow))
{
rigidbody2d.velocity = new Vector2(-5f, rigidbody2d.velocity.y);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
rigidbody2d.velocity = new Vector2(5f, rigidbody2d.velocity.y);
}
else if (Input.GetKeyDown(KeyCode.Z))
{
currentState = State.Attack;
animator.SetTrigger("StartAttacking");
}
// Transition to Idle state
else if (rigidbody2d.velocity.y <= 0 && transform.position.y < -5f)
{
currentState = State.Idle;
animator.SetTrigger("StopJumping");
}
}
private void HandleAttackState()
{
// Attack animation will play Transition to Idle state
if (Input.GetKeyDown(KeyCode.X))
{
currentState = State.Idle;
animator.SetTrigger("StopAttacking");
}
}
}
この例では、ステート マシンを単純なゲーム キャラクターに適用します。キャラクターの動作は、アイドル、移動、ジャンプ、攻撃の 4 つの状態によって制御されます。各状態の処理方法では、入力を確認し、必要に応じて状態遷移を行います。
たとえば、キャラクターがアイドル状態にあるときに、スペース キーを押すと移動状態に変更でき、Z キーを押すと攻撃状態に変更できます。キャラクターが移動状態にあるときに、スペース キーを押すと状態がジャンプ状態に切り替わり、Z キーを押すと状態が攻撃状態に切り替わり、左右の矢印キーを放すと状態がアイドル状態に切り替わります。州。
このようにして、キャラクターの動作を簡単に制御でき、状態間の遷移プロセスがより秩序正しく制御可能になります。実際のゲーム開発では、ステート マシンは、ゲーム オブジェクトの動作をより適切に管理するのに役立つ非常に一般的なプログラミング パターンです。