###前提・実現したいこと
Unity 2017.1.1f1
を使っています。
現在以下のUnityのAssetを使って勉強中です
2D Roguelike tutorial
また、日本語の解説は以下のサイト(ソース付き)を参考にさせて頂いてます。
【Unity7】キャラクターを動かすための抽象クラス作成【2Dローグライク6】
このTutorialの完成版を編集して、Unityを勉強しているところです。
そこで、敵キャラクターの攻撃動作を分かりやすくするために
「敵の攻撃時に、敵キャラクターがプレイヤーに半マス分体当たりして、自分のマスに戻る」
という表現をさせたいのです。
しかし、自分で書いたスクリプトでは2つの点で上手くいきませんでした。
1.敵キャラクターがプレイヤーのマスに入るまではいいが、自分のマスに戻らず、次のターンに必ず下のマスに抜ける
2.プレイヤー方向への移動距離を1マスではなく1/2にすると動かなくなる
もしお詳しい方がいらしたら、見ていただけたらと思います。
よろしくお願いします
言語はC♯を使っています
###Enemy.cs
//Enemy inherits from MovingObject, our base class for objects that can move, Player also inherits from this. public class Enemy : MovingObject { public int playerDamage; private Animator animator; //Variable of type Animator to store a reference to the enemy's Animator component. private Transform target; //プレイヤーの位置情報 private bool skipMove; //敵キャラが動くかどうかの判定 //MovingObjectのStartメソッドを継承 protected override void Start () { //Register this enemy with our instance of GameManager by adding it to a list of Enemy objects. //This allows the GameManager to issue movement commands. GameManager.instance.AddEnemyToList (this); //Animatorをキャッシュしておく animator = GetComponent<Animator>(); //Playerの位置情報を取得 target = GameObject.FindGameObjectWithTag("Player").transform; //MovingObjectのStartメソッド呼び出し base.Start(); } //Override the AttemptMove function of MovingObject to include functionality needed for Enemy to skip turns. //See comments in MovingObject for more on how base AttemptMove function works. protected override void AttemptMove <T> (int xDir, int yDir) { //Check if skipMove is true, if so set it to false and skip this turn. if(skipMove) { skipMove = false; return; } //Call the AttemptMove function from MovingObject. base.AttemptMove <T> (xDir, yDir); //移動が終了したらtrueにする skipMove = true; } //敵キャラ移動用メソッド GameManagerから呼ばれる public void MoveEnemy () { //Declare variables for X and Y axis move directions, these range from -1 to 1. //These values allow us to choose between the cardinal directions: up, down, left and right. int xDir = 0; int yDir = 0; //Mathf.Abs: 絶対値をとる。-1なら1となる。 if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon) { //プレイヤーが上にいれば+1、下に入れば-1する yDir = target.position.y > transform.position.y ? 1 : -1; } else { //プレイヤーが右にいれば+1、左にいれば-1する xDir = target.position.x > transform.position.x ? 1 : -1; } //ジェネリック機能 攻撃対象はPlayerのみなので、型引数はPlayer AttemptMove<Player>(xDir, yDir); } //OnCantMove is called if Enemy attempts to move into a space occupied by a Player, it overrides the OnCantMove function of MovingObject //and takes a generic parameter T which we use to pass in the component we expect to encounter, in this case Player protected override void OnCantMove <T> (T component) { //Playerクラスを取得 Player hitPlayer = component as Player; //PlayerクラスのLoseFoodメソッドを呼び出す 引数はダメージ量 hitPlayer.LoseFood(playerDamage); //敵の攻撃動作 //animator.SetTrigger ("enemyAttack"); boxCollider.enabled = false; int xDir = 0; int yDir = 0; //Mathf.Abs: 絶対値をとる。-1なら1となる。 if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon) { //プレイヤーが上にいれば+1、下に入れば-1する yDir = target.position.y > transform.position.y ? 1 : -1; animator.SetInteger("RabbitIdle", yDir); } else { //プレイヤーが右にいれば+1、左にいれば-1する xDir = target.position.x > transform.position.x ? 1 : -1; Vector3 scale = transform.localScale; scale.x = -xDir; transform.localScale = scale; animator.SetInteger("RabbitIdle", 2); } float halfX = xDir; float halfY = yDir; //現在地→プレイヤーへ Vector2 start = transform.position; Vector2 end = start + new Vector2(halfX, halfY); StartCoroutine(SmoothMovement(end)); StartCoroutine(SmoothMovement(start)); boxCollider.enabled = true; } }
###MovingObject.cs(継承元のSmoothMovemtを持つ)
//abstract:継承される側(スーパークラス)に付ける修飾子 public abstract class MovingObject : MonoBehaviour { public float moveTime = 0.1f; //Time it will take object to move, in seconds. public LayerMask blockingLayer; //Layer on which collision will be checked. [HideInInspector] public BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object. [HideInInspector] public Rigidbody2D rb2D; //The Rigidbody2D component attached to this object. //moveTimeを計算するのを単純化するための変数 private float inverseMoveTime; //virtual : 継承されるメソッドに付ける修飾子 protected virtual void Start() { //BoxCollider2DとRigidbody2Dを何度もGetComponentしなくて済むよう //Startメソッドにてキャッシュしておく boxCollider = GetComponent<BoxCollider2D>(); rb2D = GetComponent<Rigidbody2D>(); //デフォルトだと 1f ÷ 0.1f = 10.0f inverseMoveTime = 1f / moveTime; } //移動可能かを判断するメソッド 可能な場合はSmoothMovementへ protected bool Move(int xDir, int yDir, out RaycastHit2D hit) { //現在地を取得 Vector2 start = transform.position; //目的地を取得 Vector2 end = start + new Vector2(xDir, yDir); //自身のColliderを無効にし、Linecastで自分自身を判定しないようにする boxCollider.enabled = false; //現在地と目的地との間にblockingLayerのついたオブジェクトが無いか判定 hit = Physics2D.Linecast(start, end, blockingLayer); //Colliderを有効に戻す boxCollider.enabled = true; //何も無ければSmoothMovementへ遷移し移動処理 if (hit.transform == null) { StartCoroutine(SmoothMovement(end)); //移動が成功したことを伝える return true; } //移動に失敗したことを伝える return false; } //現在地から目的地(引数end)へ移動するためのメソッド protected IEnumerator SmoothMovement(Vector3 end) { //現在地から目的地を引き、2点間の距離を求める(Vector3型) //sqrMagnitudeはベクトルを2乗したあと2点間の距離に変換する(float型) float sqrRemainingDistance = (transform.position - end).sqrMagnitude; //2点間の距離が0になった時、ループを抜ける //Epsilon : ほとんど0に近い数値を表す while (sqrRemainingDistance > float.Epsilon) { //現在地と移動先の間を1秒間にinverseMoveTime分だけ移動する場合の、 //1フレーム分の移動距離を算出する Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime); //算出した移動距離分、移動する rb2D.MovePosition(newPosition); //現在地が目的地寄りになった結果、sqrRemainDistanceが小さくなる sqrRemainingDistance = (transform.position - end).sqrMagnitude; //1フレーム待ってから、while文の先頭へ戻る yield return null; } } //移動を試みるメソッド //virtual : 継承されるメソッドに付ける修飾子 //<T>:ジェネリック機能 型を決めておかず、後から指定する //Playerが移動するスクリプトの場合はWall型が、Enemyが移動するスクリプトの場合はPlayer型がこの<T>に入る protected virtual void AttemptMove<T>(int xDir, int yDir) //ジェネリック用の型引数をComponent型で限定 where T : Component { RaycastHit2D hit; //Moveメソッド実行 戻り値がtrueなら移動成功、falseなら移動失敗 bool canMove = Move(xDir, yDir, out hit); //Moveメソッドで確認した障害物が何も無ければメソッド終了 if (hit.transform == null) { return; } //障害物があった場合、障害物を型引数の型で取得 //型が<T>で指定したものと違う場合、取得できない T hitComponent = hit.transform.GetComponent<T>(); //障害物がある場合OnCantMoveを呼び出す if (!canMove && hitComponent != null) { OnCantMove(hitComponent); } } //abstract: メソッドの中身はこちらでは書かず、サブクラスにて書く //<T>:AttemptMoveと同じくジェネリック機能 //障害物があり移動ができなかった場合に呼び出される protected abstract void OnCantMove<T>(T component) where T : Component; }
###試したこと
特に見ていただきたいのは自分で記述した以下の部分です。
もともと設定してあったanimatorをコメントアウトして、体当たりに見える動作を加えています
//敵の攻撃動作
~(中略)~
//ここで1/2にするとそもそも動かなくなる
float halfX = xDir;
float halfY = yDir;
//現在地→プレイヤーへ Vector2 start = transform.position; Vector2 end = start + new Vector2(halfX, halfY);
//実行すると1つ目のSmoothMovementは動くが、2つ目が動作していないように見える
StartCoroutine(SmoothMovement(end));
StartCoroutine(SmoothMovement(start));
boxCollider.enabled = true;
もしどなたか原因や修正方法がわかる方がいたら、教えていただきたいです。
足りない情報は追加させていただきますので、指摘をしていただければと思います。
よろしくお願いいたします。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/05/08 07:42
2018/05/08 08:37
2018/05/09 06:53