グリッドに沿ってプレーヤーキャラを動かす


グリッドに沿ってプレーヤーキャラを動かすver0.3

壁に衝突した際のグリッドの吸着補正を実装
ほぼ動きに関する処理は完成

<要点>
このコードではベクトルを様々な用途で利用している点に注目
ベクトルは方向であり速度であり距離、運動量、位置でもある
またMathfのメンバー関数による四捨五入やint⇔floatの型変換による小数点以下切り捨ての動作なども利用している

プレーヤーの状態を観察すると以下の種類がある
  • ゲーム開始直後の静止した状態(最初の入力後は運動量が0になることは無い)
  • グリッドの交差点に位置している(方向転換可能な状態)
  • それ以外の状態(真後ろにのみすぐに方向転換可能)
  • 進む方向の壁に接触して一番近い交差点に位置補正した状態


using UnityEngine;
using System.Collections;

public class GridPlayer_Learning : MonoBehaviour
{
  
   // Use this for initialization
   void Start ()
   {
   
   }
   
   const int GRID_SPAN = 1;
   const float SPEED = 2.0f;
   Vector3 inputDirect;        //入力の向き 
   Vector3 direction;            //プレーヤーキャラの方向性 
   Vector3 prePos;                //1フレーム前の状態保存
   Vector3 pos;                //現フレーム位置
   Vector3 currentGrid;
   bool cross;                    //グリッド境界
   
   // Update is called once per frame
   void Update ()
   {
       cross = false;
       //あくまで静止状態から初速を得るためだけの入力要求 
       //ゲームを開始して一度でも方向キーが入力されたらdirectionがVector3.zeroになることは今後一切ない
       //その意味で以下は起動直後用のみのコード
       if (direction == Vector3.zero) {
           direction = GetDirect ();
       }
       
       prePos = this.transform.position;
       pos = this.transform.position; 
       pos += direction * SPEED * Time.deltaTime; 
       
       prePos /= GRID_SPAN;    //1÷3=0.3333になる この理屈でGRID_SPAN毎に判定が可能となる
       pos /= GRID_SPAN;
       
       if ((int)pos.x != (int)prePos.x) {
           cross = true;
       }
       if ((int)pos.z != (int)prePos.z) {
           cross = true;
       }
       
       //近いグリッド
       Vector3 nearGrid = new Vector3 (Mathf.Round (pos.x), pos.y, Mathf.Round (pos.z));
       
       // posが「?.01」~「?.49」までの間フラグが立つ(例0.01~0.49の間など) 
       // forwardPosはフラグ検出用に補助に使われているだけ
       Vector3 forwardPos = pos + (direction * 0.5f / GRID_SPAN);
       if (Mathf.RoundToInt (forwardPos.x) != Mathf.RoundToInt (pos.x) | |
           Mathf.RoundToInt (forwardPos.z) != Mathf.RoundToInt (pos.z)) {
           //進む先の壁のチェック
           if (Physics.Raycast (pos, direction, 1.0f * GRID_SPAN)) {
               //見かけは静止しているが運動量(direction)を弄らず位置を補正し続けている為「静止している訳ではない」 
               //運動量はVector3.Zero以外の値を持ち続けている事に注意 
               pos = nearGrid;
               cross = true;
           }
       }
       
       //この入力が本当の意味でのゲーム内プレーヤー入力
       inputDirect = GetDirect ();
       //入力をプレーヤーの方向性に反映させる 状況により反映方法が変わる
       if (cross) {
           if (inputDirect != Vector3.zero) {
               //入力した方向に曲がれるかどうかの壁チェック 
               if (!Physics.Raycast (nearGrid, inputDirect, 1.0f * GRID_SPAN)) {
                   direction = inputDirect;
               }
           }
       } else {
           if (Vector3.Dot (direction, inputDirect) < -0.9999f) {
               direction = inputDirect;
           }
       }
           
       this.transform.position = pos;    //最期に補正の有無にかかわらず現フレーム位置を反映させる 
   }
   
   float inputTimer;
   const float THRESHOLD = 0.3f;
   const float CONTINUATION = 2.0f;
   Vector3 preInput;
   
   //4方向レバー 無入力時2秒延滞 
   Vector3 GetDirect ()
   {
       inputTimer += Time.deltaTime;
       Vector3 input = new Vector3 (Input.GetAxis ("Horizontal"), 0.0f, Input.GetAxis ("Vertical"));
       float absX = Mathf.Abs (input.x); 
       float absZ = Mathf.Abs (input.z); 
       
       if (absX < THRESHOLD && absZ < THRESHOLD) {
           if (inputTimer < CONTINUATION) {
               return preInput;
           }
           return Vector3.zero;
       }

      inputTimer = 0;
       if (absX < absZ) {
           input.x = 0.0f;
       } else {
           input.z = 0.0f;
       }
       
       input = input.normalized;
       preInput = input;
       return input;
   }
   
}

グリッドに沿ってプレーヤーキャラを動かすver0.2


壁の判定を追加
グリッド単位の変更も可能か模索する

using UnityEngine;
using System.Collections;

public class GridPlayer_Learning : MonoBehaviour
{
  
   // Use this for initialization
   void Start ()
   {
   
   }
   
   const int GRID_SPAN = 1;
   const float SPEED = 2.0f;
   Vector3 inputDirect;        //入力の向き 
   Vector3 direction;            //プレーヤーキャラの方向性 
   Vector3 prePos;                //1フレーム前の状態保存
   Vector3 pos;                //現フレームの状態保存
   Vector3 currentGrid;
   bool cross;                    //グリッド境界
   
   // Update is called once per frame
   void Update ()
   {
       cross = false;
       if (direction == Vector3.zero) {        //あくまでプレーヤーの方向性からルーチンに入ってる 
           direction = GetDirect ();            //方向性が無しの場合入力をたしてクロス判定を行う
       }
       
       prePos = this.transform.position;
       pos = this.transform.position; 
       pos += direction * SPEED * Time.deltaTime; 
       
       prePos /= GRID_SPAN;    //1÷3=0.3333になる この理屈でGRID_SPAN毎に判定が可能となる
       pos /= GRID_SPAN;
       
       if ((int)pos.x != (int)prePos.x) {
           cross = true;
       }
       if ((int)pos.z != (int)prePos.z) {
           cross = true;
       }
       
       //近いグリッド
       Vector3 nearGrid = new Vector3 (Mathf.Round (pos.x), pos.y, Mathf.Round (pos.z));
       //移動前方、半グリッド先(前方1キャラクタ分を判定しようとするとこうなる)
       Vector3 forwardPos = pos + (direction * 0.5f / GRID_SPAN);
       
       // posが「?.01」~「?.49」までの間フラグが立つ(例0.01~0.49の間など) 
       if (Mathf.RoundToInt (forwardPos.x) != Mathf.RoundToInt (pos.x) | |
           Mathf.RoundToInt (forwardPos.z) != Mathf.RoundToInt (pos.z)) {
           
           //進む先の壁のチェック
           if (Physics.Raycast (pos, direction, 1.0f * GRID_SPAN)) {
               direction = Vector3.zero;
               cross = true;
           }
       }
       
       inputDirect = GetDirect ();
       //入力をプレーヤーの方向性に反映させる 状況により反映方法が変わる
       if (cross) {
           if (inputDirect != Vector3.zero) {
               //入力した方向に曲がれるかどうかの壁チェック 
               if (!Physics.Raycast (nearGrid, inputDirect, 1.0f * GRID_SPAN)) {
                   direction = inputDirect;
               }
           }
       } else {
           if (Vector3.Dot (direction, inputDirect) < -0.9999f) {
               direction = inputDirect;
           }
       }
           
       this.transform.position += direction * SPEED * Time.deltaTime;
   }
   
   float inputTimer;
   const float THRESHOLD = 0.3f;
   const float CONTINUATION = 2.0f;
   Vector3 preInput;
   
   //4方向レバー 無入力時2秒延滞 
   Vector3 GetDirect ()
   {
       inputTimer += Time.deltaTime;
       Vector3 input = new Vector3 (Input.GetAxis ("Horizontal"), 0.0f, Input.GetAxis ("Vertical"));
       float absX = Mathf.Abs (input.x); 
       float absZ = Mathf.Abs (input.z); 
       
       if (absX < THRESHOLD && absZ < THRESHOLD) {
           if (inputTimer < CONTINUATION) {
               return preInput;
           }
           return Vector3.zero;
       }

      inputTimer = 0;
       if (absX < absZ) {
           input.x = 0.0f;
       } else {
           input.z = 0.0f;
       }
       
       input = input.normalized;
       preInput = input;
       return input;
   }
   
}


グリッドに沿ってプレーヤーキャラを動かすver0.1


using UnityEngine;
using System.Collections;

public class GridPlayer_Learning : MonoBehaviour
{
  
   // Use this for initialization
   void Start ()
   {
   
   }
   
   const int GRID_SPAN = 3;
   const float SPEED = 2.0f;
   Vector3 inputDirect;        //入力の向き 
   Vector3 direction;            //プレーヤーキャラの方向性 
   Vector3 prePos;                //1フレーム前の状態保存
   Vector3 pos;                //現フレームの状態保存
   bool cross;                    //グリッド境界
   
   // Update is called once per frame
   void Update ()
   {
       cross = false;
       if (direction == Vector3.zero) {        //あくまでプレーヤーの方向性からルーチンに入ってる 
           direction = GetDirect ();            //方向性が無しの場合入力をたしてクロス判定を行う
       }
       
       prePos = this.transform.position;
       pos = this.transform.position; 
       pos += direction * SPEED * Time.deltaTime; 
       
       prePos /= GRID_SPAN;    //1÷3=0.3333になる この理屈でGRID_SPAN毎に判定が可能となる
       pos /= GRID_SPAN;
       
      
       if ((int)pos.x != (int)prePos.x) {
           cross = true;
       }
       if ((int)pos.z != (int)prePos.z) {
           cross = true;
       }
       
       //入力をプレーヤーの方向性に反映させる 状況により反映方法が変わる
       inputDirect = GetDirect ();
       if (cross) {
           if (inputDirect != Vector3.zero)
               direction = inputDirect;
       } else {
           if (Vector3.Dot (direction, inputDirect) < -0.9999f) {
               direction = inputDirect;
           }
       }
       this.transform.position += direction * SPEED * Time.deltaTime;
   }
   
   float inputTimer;
   const float THRESHOLD = 0.3f;
   const float CONTINUATION = 2.0f;
   Vector3 preInput;
   
   //4方向レバー 無入力時2秒延滞 
   Vector3 GetDirect ()
   {
       inputTimer += Time.deltaTime;
       Vector3 input = new Vector3 (Input.GetAxis ("Horizontal"), 0.0f, Input.GetAxis ("Vertical"));
       float absX = Mathf.Abs (input.x); 
       float absZ = Mathf.Abs (input.z); 
       
       if (absX < THRESHOLD && absZ < THRESHOLD) {
           if (inputTimer < CONTINUATION) {
               return preInput;
           }
           return Vector3.zero;
       }

      inputTimer = 0;
       if (absX < absZ) {
           input.x = 0.0f;
       } else {
           input.z = 0.0f;
       }
       
       input = input.normalized;
       preInput = input;
       return input;
   }
   
}

メニュー



  • 最終更新:2014-06-13 11:57:21

このWIKIを編集するにはパスワード入力が必要です

認証パスワード