6.Moving Object Script
さて、ここから一気にスクリプトが難しくなりますw
protected IEnumerator SmoothMovement (Vector3 end) { //Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter. //Square magnitude is used instead of magnitude because it's computationally cheaper. float sqrRemainingDistance = (transform.position - end).sqrMagnitude; //While that distance is greater than a very small amount (Epsilon, almost zero): while(sqrRemainingDistance > float.Epsilon) { //Find a new position proportionally closer to the end, based on the moveTime Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime); //Call MovePosition on attached Rigidbody2D and move it to the calculated position. rb2D.MovePosition (newPostion); //Recalculate the remaining distance after moving. sqrRemainingDistance = (transform.position - end).sqrMagnitude; //Return and loop until sqrRemainingDistance is close enough to zero to end the function yield return null; } }
まず、スムーズに移動を行うためのコルーチン、SmoothMovement。移動先となるendを引数にします。
現在の座標と、移動先のendのsqrMagnitudeを取得します。
sqrMagnitudeは、2つのベクトル(現在の座標と、移動先)の距離を比較するときに使われる値で、これがfloat.Epsilon(ゼロではないが十分に小さい数)よりも大きい場合に、ちょっとづつ移動を行う、というループが動きます。
「ちょっと移動する」ときの移動先は、
Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
で計算します。MoveTowards関数の3番めは、「1フレーム毎の早さ x フレーム数」となり、endにどのくらい進むか、という値を示しています。
Rididbody2DのMovePositionに移動先を指定して、再度sqrMagnitudeを計算します。
protected bool Move (int xDir, int yDir, out RaycastHit2D hit) { //Store start position to move from, based on objects current transform position. Vector2 start = transform.position; // Calculate end position based on the direction parameters passed in when calling Move. Vector2 end = start + new Vector2 (xDir, yDir); //Disable the boxCollider so that linecast doesn't hit this object's own collider. boxCollider.enabled = false; //Cast a line from start point to end point checking collision on blockingLayer. hit = Physics2D.Linecast (start, end, blockingLayer); //Re-enable boxCollider after linecast boxCollider.enabled = true; //Check if anything was hit if(hit.transform == null) { //If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination StartCoroutine (SmoothMovement (end)); //Return true to say that Move was successful return true; } //If something was hit, return false, Move was unsuccesful. return false; }
out引数のRaicast2Dは、移動先に障害物があるかどうかを検知するためのもので、
Physics2D.Linecast (start, end, blockingLayer);
で判定しています。blockingLayerは、publicプロパティで設定するLayerMaskになります。
(最初に、自分自身のColliderをfalseにして無視してから判定して、戻す)
障害物がない場合は、先ほどのSmoothMoveを使って移動を行います。
//The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword. //AttemptMove takes a generic parameter T to specify the type of component we expect our unit to interact with if blocked (Player for Enemies, Wall for Player). protected virtual void AttemptMove <t> (int xDir, int yDir) where T : Component { //Hit will store whatever our linecast hits when Move is called. RaycastHit2D hit; //Set canMove to true if Move was successful, false if failed. bool canMove = Move (xDir, yDir, out hit); //Check if nothing was hit by linecast if(hit.transform == null) //If nothing was hit, return and don't execute further code. return; //Get a component reference to the component of type T attached to the object that was hit T hitComponent = hit.transform.GetComponent <t> (); //If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with. if(!canMove && hitComponent != null) //Call the OnCantMove function and pass it hitComponent as a parameter. OnCantMove (hitComponent); }
移動を試みる、という意味のAttemptMove関数です。
<T>というのは、C#のジェネリックという記載方法で、型を明示せず、汎用的に使用できるようにする方法です。
この場合は、where : Componentで、Component型のオブジェクト、またはそれを継承したオブジェクト(=Sceneに登場する全てのGameObject)を指定することができます。
具体的には、このスクリプトを使うのがPlayerの場合はWallを、Enemyの場合はPlayerを指定できるようにしています。
(PlayerがWallに当たった時に、Wallへ与えるダメージ計算をするために使用します。なお、このゲームはEnemyを破壊することはできませんので、Wallだけの指定になります)
RaycastHit2Dの変数を用意して、先ほど作成したMove変数のoutによって値を生成します。
移動に成功した場合はreturnで抜け、失敗した場合は、ヒットしたオブジェクトをhitComponentに代入します。
そして、OnCantMoveにそれを渡して、別の処理を行います。