【Unity】ゲームに使える数学/追尾編

【Unity】ゲームに使える数学/追尾編

  • このエントリーをはてなブックマークに追加

お久しぶりです。去年入社致しました、ウエムラです。

1月に開催された、世界で開かれているグローバルゲームジャムに参加してきました。
初めての参加なのでびくびくしてましたが、とても楽しかったです!
できた作品:ゲームジャム作品
(私の担当した部分は[結果画面]で、あまり役に立っていませんが…)

さて、今回はゲームに使える数学についてです。
ゲームを作る上で数学は避けては通れぬ道…
私は数学が全然得意ではありませんが、そんな私も調べながらであれば少しは出来るので、記事にしてみようと思いました!

最近、本格的にUnityを始めたので、Unityで書いていこうと思います

素材はこちらを使っていきます。(見た目を変えるだけなので、なくてもOKです!)
アセット素材

追尾

どんな用途に使うか?(参考)

・敵の移動
・敵の攻撃
・カーソルのエフェクト

追尾は色々なゲームで使いますが、主に上記であげた内容で使うと思います。
なくてもゲームは作れますが、あればとても面白い動きを実現できます!
では、早速やっていきましょう!

下準備としてこんな感じに配置してみました。
(ひよこは気にしないでください…メモ帳です…)

続きを読む


Unityでゲームの追尾をプログラミング

エネミーを配置してみる

まずは敵のオブジェクトを作りましょう。
1. 2D Object -> Sprite でオブジェクトを作ります
2. 名前をわかりやすい名前に変更(私はEnemyにしました。以下EnemyObjで説明)
3. EnemyObj の Sprite Renderer に 画像をくっつける
4. EnemyObj に Rigidbody2D を追加する
5. Rigidbody2D の isKinematic にチェックを入れる

私の場合、こんな感じになりました。
敵を配置

エネミーにスクリプトをつけてみる

では、追尾させるスクリプトを組んでみましょう!(C#で進めていきます)
スクリプト名は Enemy.cs としました。

using UnityEngine;
using System.Collections;

public class Ememy : MonoBehaviour 
{
  Vector3 mouseToWorldPos; // マウスポジション
  Vector3 prev; // 1フレーム前の座標保持
  float speed = 0.1f; // 移動量
  float moveForce = 3.0f; // 移動量(Rigidbody用)

  void Start()
  {
    prev = this.gameObject.transform.localPosition;
  }

  void Update()
  {
    MousePositionUpdate();
    NormalMove();
    RigidbodyMove();
    AngleUpdate();
  }

  /// マウスのポジション
  void MousePositionUpdate()
  {
    // マウスの座標取得
    Vector2 mousePos = Input.mousePosition;
    // マウスの座標を元にワールド座標を取得
    mouseToWorldPos = Camera.main.ScreenToWorldPoint(mousePos);
  }

  /// RigidBody無しの移動
  void NormalMove()
  {
  }

  // RigidBody有りの移動
  void RigidbodyMove()
  {
  }

  /// エネミー角度
  void AngleUpdate()
  {
  }
}

一旦ここまで作ります。
UnityEditor側に戻り、EnemyObjにこのスクリプトをアタッチしてみましょう!
Unity Editor
(ここまで、作ってもまだ動きません)

追尾移動(RigidBody無し)

それでは早速、追尾を実装してみますが、いくつか方法があるので試してみたいと思います。
では、Enemy.cs の void NormalMove() に処理を追加してみましょう!

// RigidBody無しの移動
void NormalMove()
{
    Vector2 enemyPos = this.gameObject.transform.localPosition;
    float vx = mouseToWorldPos.x - enemyPos.x;
    float vy = mouseToWorldPos.y - enemyPos.y;
    float dx, dy, radian, value;

    /**
     * ターゲットとのラジアン/ベクトル
     */
    // Mathf.Atan2を使う場合
    radian = Mathf.Atan2(vy, vx); // 2つの座標から角度を計算
    dx = Mathf.Cos(radian) * speed; // Sin,Cosを使用してその方向へ移動
    dy = Mathf.Sin(radian) * speed;

    // Mathf.Atan2を使わない場合
    //value = Mathf.Sqrt( (vx * vx) + (vy * vy) ); // 2つの座標からベクトル計算
    //dx = (vx / value) * speed; // valueで割って正規化
    //dy = (vy / value) * speed;

    // 移動制御
    if(Mathf.Abs(vx) < 0.1f){
      dx = 0f;
    }
    if(Mathf.Abs(vy) < 0.1f){
      dy = 0f;
    }

    // 移動を反映
    enemyPos.x += dx;
    enemyPos.y += dy;

    this.gameObject.transform.localPosition = enemyPos;
}

2つのパターンを書きましたので、お好みで。
Mathf.Atan2を使う方法は、角度からターゲットの方向に移動させています。
Mathf.Atan2を使わない方法は、ベクトルを計算、正規化をして移動させています。
Unity Mathf.Atan2

追尾移動(RigidBody有り)

void Update() にある NormalMove() はコメントアウトしてください。

void Update()
{
    MousePositionUpdate();
    //NormalMove();
    RigidbodyMove();
    AngleUpdate();
}

RigidBodyをつけるともっと簡単に実装することができます。
では、早速処理を追加してみます。

/// Rigidbody有りの移動
void RigidbodyMove()
{
    Vector2 enemyPos = this.gameObject.transform.localPosition;
    float vx = mouseToWorldPos.x - enemyPos.x;
    float vy = mouseToWorldPos.y - enemyPos.y;

    // 追跡方向の決定
    Vector2 direction = new Vector2( vx, vy ).normalized;

    // ターゲット方向に力を加える
    GetComponent ().velocity = (direction * moveForce);

    if(Mathf.Abs(vx) < 0.1f || Mathf.Abs(vy) < 0.1f){
      GetComponent ().velocity = new Vector2 (0, 0);
    }
}

RigidBodyを使うことで、結構楽に実装できたりします。
.normalized を使うことで簡単に正規化することが可能です。

おまけ

おまけで角度もつけてみたいと思います。
今だと同じ方向を向いたままですが、角度をつけることで動きがそれっぽくなります。
(記事2がとてもわかりやすかったので使わさせていただきました!)

void AngleUpdate()
{
    // 向き調整 
    Vector3 diff = transform.localPosition - prev;
	
    if (diff.magnitude < 0.01f)
    {
      // Atan2で2つの角度を求めて計算して、Mathf.Rad2Deg で 弧度法 に変換
      float angle = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg + 90;

      // Quaternion.AngleAxis で 軸を中心にした回転をさせる
      transform.rotation = Quaternion.AngleAxis (angle, Vector3.forward);
    } 

    // 1フレーム前の座標
    prev = transform.localPosition;
}

Mathfクラスを使うと、自分で計算を書く必要がないのでとても楽ですね!
Quaternionクラスに便利なものが結構あるので、調べてみてもいいかもしれません。
Mathfクラスを使うと便利
画像をミサイル等にすると、カーソルに追尾している感じが出て味が出ると思います。
おまけはここまでです。

まとめ

今回はそれなりに簡単な追尾の仕方を書かさせていただきました。
次回は、シューティングゲームの弾で使う初歩的な数学等?書けたらと思います。(未定です)
数学が苦手な方も調べながらであれば、できるはずですので、一緒に挑戦して克服していきましょう!

記事をご閲覧頂き、ありがとうございました!

参考サイト/素材サイト

  • このエントリーをはてなブックマークに追加

記事作成者の紹介

doggy(フロントエンドエンジニア)

ゲームと犬が好き。

関連するSONICMOOVのサービス

フロントエンドエンジニア募集中!

×

SNSでも情報配信中!ぜひご登録ください。

×

SNSでも
情報配信中!
SONICMOOV Facebookページ SONICMOOV Twitter
フロントエンドエンジニア募集中!

新着の記事

mautic is open source marketing automation