Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
95
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

【Unity2D】2Dゲームを作るときのMonoBehaviourを使いやすくするクラスの設計

はじめに

Unityで、一番最初にスクリプトを書き始めて気になってしまうのが、ゲームに必要な情報へアクセスするための記述が長くなってしまうことです。

例えば座標を取得するには transform にアクセスする必要があります。

  // X座標を取得する
  float x = transform.x;

さらに座標を変更する場合には、直接代入ができず一時変数を経由する必要があります。

  // X座標を代入する
  Vector3 v = transform;
  // X=20へ移動する
  v.x = 20;
  // 座標を反映する
  transform = v;

少しでもゲームライブラリを作ったり、他のゲームエンジンを使ったことがある人にとって、これはとても面倒な記述です。他のゲームエンジンであれば、たいていのゲームオブジェクトはメンバ変数に「x」などの座標変数を持っていて直接取得や書き換えができます。

Unityがコンポーネント指向を採用して汎用的な作りになっているので、これは仕方ないことなのですが、コードベースでゲームを作ることが多い人にとってこれは面倒なことです。

そこで、私が独自で作ったMonobehaivourのラッパークラスを紹介します。ただここで紹介するのは2Dゲーム専用なので3Dには使えません。またこれは実装例なので必ずこのようにしなければならないというものではなく、「こんなプロパティやメソッドがあると便利だよね」という参考程度にしてもらえればと思います。

クラス名と必須コンポーネント

クラス名は「Token」としています。ゲームキャラクターの場合「Actor」というのがよく使われる名称ですが、厨二病をこじらせているので、人があまり使わない名前を使うようにしています。

/// キャラクター基底クラス.
/// SpriteRendererが必要.
[RequireComponent (typeof(SpriteRenderer))]
public class Token : MonoBehaviour
{
  ……

ゲームキャラクターの場合、たいてい描画を行うので、SpriteRendererコンポーネントを必須としています。

プロパティ

プロパティには以下のものを用意しました。

名前 概要 説明 必須コンポーネント 補足
X float X座標 -
Y float Y座標 -
VX float 移動量(X) X方向への移動量 Rigidbody 2D
VY float 移動量(Y) Y方向への移動量 Rigidbody 2D
Direction float 移動方向 移動角度。0〜359 Rigidbody 2D getのみ
Speed float 移動速度 Mathf.Sqrt(VX^2 + VY^2) Rigidbody 2D getのみ
GravityScale float 重力加速度 Rigidbody 2D
RigidBody Rigidbody2D Rigidbody2Dの参照 Rigidboby 2D getのみ
Exists bool 生存フラグ オブジェクトが存在するかどうか -
ScaleX float スケール値(X) X方向の拡大縮小値 -
ScaleY float スケール値(Y) Y方向の拡大縮小値 -
Scale float スケール値 XYスケール値をまとめて変更 -
Visible bool 描画フラグ falseで描画しなくなる SpriteRenderer
Angle float 回転角度 回転角度(Z軸回転) SpriteRenderer
Alpha float 透過値 透過値(0.0〜1.0f) SpriteRenderer
SpriteWidth float スプライトの幅 SpriteRenderer
SpriteHeight float スプライトの高さ SpriteRenderer
SortingLayer string ソーティングレイヤー名 SpriteRenderer
SortingOrder int ソーティングオーダー番号 SpriteRenderer
Renderer SpriteRenderer SpriteRendererの参照 SpriteRenderer getのみ
CircleCollider CircleCollider2D CircleCollider2Dの参照 CircleCollider getのみ
CollisionRadius float CircleColliderの半径 CircleCollider 2D
CircleColliderEnabled bool Circle Collider2D有効フラグ 有効であればtrue Circle Collider 2D
BoxColliderWidth float BoxColliderの幅 Box Collider 2D
BoxColliderHeight float BoxColliderの高さ Box Collider 2D
BoxColliderEnabled bool Box Collider2D有効フラグ 有効であればtrue Box Collider 2D

長くなってしまいましたが、カテゴリとしては、だいたい以下の4つです。

  • transform系
  • SpriteRender系
  • Rigidbody系
  • コリジョン系

スクリプトからコリジョン系のパラメータを変更することはあまりないのですが、たまに使いたいときがあります

メソッド

メソッドは以下のものを用意しました

名前 概要 説明 必須コンポーネント 補足
AddPosition 座標を加算する -
SetPosition 座標を設定する -
SetScale スケール値を設定する -
AddScale スケール値を加算する -
MulScale スケール値を乗算する -
SetVelocity 移動速度を設定する 引数は「角度」と「速さ」 Rigibody 2D
SetVelocityXY 移動速度を設定する 引数はXYの移動量 Rigidbody 2D
MulVelocity 移動速度を乗算する Rigidbody 2D
SetSprite スプライトを設定する SpriteRenderer
SetColor スプライトの色を設定する RGBをそれぞれ0.0〜1.0で指定 SpriteRenderer
SetAlpha スプライトの透過値を指定する 0.0〜1.0 SpriteRenderer
GetAlpha スプライトの透過値を取得する 0.0〜1.0 SpriteRenderer
SetSize オブジェクトのサイズを設定する ClampScreenで使用する -
ClampScreenAndMove カメラの範囲内に座標を丸める - 移動した上で座標を丸める
ClampScreen カメラの範囲内に座標を丸める -
IsOutside カメラの範囲外に出たかどうか
GetWorldMin カメラ左下のワールド座標を取得する -
GetWorldMax カメラ右上のワールド座標を取得する -
DestroyObj オブジェクトを破棄する - Destroy(gameObject)と同じ。メモリから削除する
Vanish オブジェクトを無効化する - メモリからは破棄しない
Revive Vanishで無効化したオブジェクトを復活する -

プレハブからインスタンスを生成する

プレハブからインスタンスを生成するために GetPrefabCreateInstanceの2つの関数を用意しています。

Token.cs
  /// プレハブ取得.
  /// プレハブは必ず"Resources/Prefabs/"に配置すること.
  public static GameObject GetPrefab (GameObject prefab, string name)
  {
    return prefab ?? (prefab = Resources.Load ("Prefabs/" + name) as GameObject);
  }

  /// インスタンスを生成してスクリプトを返す.
  public static Type CreateInstance<Type> (GameObject prefab, Vector3 p, float direction = 0.0f, float speed = 0.0f) where Type : Token
  {
    GameObject g = Instantiate (prefab, p, Quaternion.identity) as GameObject;
    Type obj = g.GetComponent<Type> ();
    obj.SetVelocity (direction, speed);
    return obj;
  }

こ関数の使い方ですが、"Assets/Resources/Prefabs/Enemy"プレハブを使ってインスタンスを生成する方法は以下のようになります。

Enemy.cs
/// 敵
public class Enemy : Token {
    /// プレハブ
    static GameObject _prefab = null;

    /// インスタンス生成
    public static Enemy Add(int Id, float x, float y, float direction, float speed) {
        // プレハブ取得
        _prefab = GetPrefab(_prefab, "Enemy");
        return CreateInstance<Enemy>(_prefab, x, y, direction, speed);
    }

static関数から生成を行いたい場合に、この関数を使ってインスタンスを生成することができるようになります。

ソースコード

ではソースコードです。

Token.cs
using UnityEngine;
using System.Collections;

/// キャラクター基底クラス.
/// SpriteRendererが必要.
[RequireComponent (typeof(SpriteRenderer))]
public class Token : MonoBehaviour
{
  /// プレハブ取得.
  /// プレハブは必ず"Resources/Prefabs/"に配置すること.
  public static GameObject GetPrefab (GameObject prefab, string name)
  {
    return prefab ?? (prefab = Resources.Load ("Prefabs/" + name) as GameObject);
  }

  /// インスタンスを生成してスクリプトを返す.
  public static Type CreateInstance<Type> (GameObject prefab, Vector3 p, float direction = 0.0f, float speed = 0.0f) where Type : Token
  {
    GameObject g = Instantiate (prefab, p, Quaternion.identity) as GameObject;
    Type obj = g.GetComponent<Type> ();
    obj.SetVelocity (direction, speed);
    return obj;
  }

  public static Type CreateInstance2<Type> (GameObject prefab, float x, float y, float direction = 0.0f, float speed = 0.0f) where Type : Token
  {
    Vector3 pos = new Vector3 (x, y, 0);
    return CreateInstance<Type> (prefab, pos, direction, speed);
  }

  /// 生存フラグ.
  bool _exists = false;

  public bool Exists {
    get { return _exists; }
    set { _exists = value; }
  }

  /// アクセサ.
  /// レンダラー.
  SpriteRenderer _renderer = null;

  public SpriteRenderer Renderer {
    get { return _renderer ?? (_renderer = gameObject.GetComponent<SpriteRenderer> ()); }
  }

  /// 描画フラグ.
  public bool Visible {
    get { return Renderer.enabled; }
    set { Renderer.enabled = value; }
  }

  /// ソーティングレイヤー名.
  public string SortingLayer {
    get { return Renderer.sortingLayerName; }
    set { Renderer.sortingLayerName = value; }
  }

  /// ソーティング・オーダー.
  public int SortingOrder {
    get { return Renderer.sortingOrder; }
    set { Renderer.sortingOrder = value; }
  }

  /// 座標(X).
  public float X {
    set {
      Vector3 pos = transform.position;
      pos.x = value;
      transform.position = pos;
    }
    get { return transform.position.x; }
  }

  /// 座標(Y).
  public float Y {
    set {
      Vector3 pos = transform.position;
      pos.y = value;
      transform.position = pos;
    }
    get { return transform.position.y; }
  }

  /// 座標を足し込む.
  public void AddPosition (float dx, float dy)
  {
    X += dx;
    Y += dy;
  }

  /// 座標を設定する.
  public void SetPosition (float x, float y)
  {
    Vector3 pos = transform.position;
    pos.Set (x, y, 0);
    transform.position = pos;
  }

  /// スケール値(X).
  public float ScaleX {
    set {
      Vector3 scale = transform.localScale;
      scale.x = value;
      transform.localScale = scale;
    }
    get { return transform.localScale.x; }
  }

  /// スケール値(Y).
  public float ScaleY {
    set {
      Vector3 scale = transform.localScale;
      scale.y = value;
      transform.localScale = scale;
    }
    get { return transform.localScale.y; }
  }

  /// スケール値を設定.
  public void SetScale (float x, float y)
  {
    Vector3 scale = transform.localScale;
    scale.Set (x, y, (x + y) / 2);
    transform.localScale = scale;
  }

  /// スケール値(X/Y).
  public float Scale {
    get {
      Vector3 scale = transform.localScale;
      return (scale.x + scale.y) / 2.0f;
    }
    set {
      Vector3 scale = transform.localScale;
      scale.x = value;
      scale.y = value;
      transform.localScale = scale;
    }
  }

  /// スケール値を足し込む.
  public void AddScale (float d)
  {
    Vector3 scale = transform.localScale;
    scale.x += d;
    scale.y += d;
    transform.localScale = scale;
  }

  /// スケール値をかける.
  public void MulScale (float d)
  {
    transform.localScale *= d;
  }

  /// 剛体.
  Rigidbody2D _rigidbody2D = null;

  public Rigidbody2D RigidBody {
    get { return _rigidbody2D ?? (_rigidbody2D = gameObject.GetComponent<Rigidbody2D> ()); }
  }

  /// 移動量を設定.
  public void SetVelocity (float direction, float speed)
  {
    Vector2 v;
    v.x = Mathf.Cos (Mathf.Deg2Rad * direction) * speed;
    v.y = Mathf.Sin (Mathf.Deg2Rad * direction) * speed;
    RigidBody.velocity = v;
  }

  /// 移動量を設定(X/Y).
  public void SetVelocityXY (float vx, float vy)
  {
    Vector2 v;
    v.x = vx;
    v.y = vy;
    RigidBody.velocity = v;
  }

  /// 移動量をかける.
  public void MulVelocity (float d)
  {
    RigidBody.velocity *= d;
  }

  /// 移動量(X).
  public float VX {
    get { return RigidBody.velocity.x; }
    set {
      Vector2 v = RigidBody.velocity;
      v.x = value;
      RigidBody.velocity = v;
    }
  }

  /// 移動量(Y).
  public float VY {
    get { return RigidBody.velocity.y; }
    set {
      Vector2 v = RigidBody.velocity;
      v.y = value;
      RigidBody.velocity = v;
    }
  }

  /// 方向.
  public float Direction {
    get {
      Vector2 v = rigidbody2D.velocity;
      return Mathf.Atan2 (v.y, v.x) * Mathf.Rad2Deg;
    }
  }

  /// 速度.
  public float Speed {
    get {
      Vector2 v = rigidbody2D.velocity;
      return Mathf.Sqrt (v.x * v.x + v.y * v.y);
    }
  }

  /// 重力.
  public float GravityScale {
    get { return RigidBody.gravityScale; }
    set { RigidBody.gravityScale = value; }
  }

  /// 回転角度.
  public float Angle {
    set { transform.eulerAngles = new Vector3 (0, 0, value); }
    get { return transform.eulerAngles.z; }
  }

  /// スプライトの設定.
  public void SetSprite (Sprite sprite)
  {
    Renderer.sprite = sprite;
  }

  /// 色設定.
  public void SetColor (float r, float g, float b)
  {
    var c = Renderer.color;
    c.r = r;
    c.g = g;
    c.b = b;
    Renderer.color = c;
  }

  /// アルファ値を設定.
  public void SetAlpha (float a)
  {
    var c = Renderer.color;
    c.a = a;
    Renderer.color = c;
  }

  /// アルファ値を取得.
  public float GetAlpha ()
  {
    var c = Renderer.color;
    return c.a;
  }

  /// アルファ値.
  public float Alpha {
    set { SetAlpha (value); }
    get { return GetAlpha (); }
  }

  /// サイズを設定.
  float _width = 0.0f;
  float _height = 0.0f;

  public void SetSize (float width, float height)
  {
    _width = width;
    _height = height;
  }

  /// スプライトの幅.
  public float SpriteWidth {
    get { return Renderer.bounds.size.x; }
  }

  /// スプライトの高さ.
  public float SpriteHeight {
    get { return Renderer.bounds.size.y; }
  }

  /// コリジョン(円).
  CircleCollider2D _circleCollider = null;

  public CircleCollider2D CircleCollider {
    get { return _circleCollider ?? (_circleCollider = GetComponent<CircleCollider2D> ()); }
  }
  // 円コリジョンの半径.
  public float CollisionRadius {
    get { return CircleCollider.radius; }
    set { CircleCollider.radius = value; }
  }
  // 円コリジョンの有効無効を設定する.
  public bool CircleColliderEnabled {
    get { return CircleCollider.enabled; }
    set { CircleCollider.enabled = value; }
  }

  /// コリジョン(矩形).
  BoxCollider2D _boxCollider = null;

  public BoxCollider2D BoxCollider {
    get { return _boxCollider ?? (_boxCollider = GetComponent<BoxCollider2D> ()); }
  }

  /// 矩形コリジョンの幅.
  public float BoxColliderWidth {
    get { return BoxCollider.size.x; }
    set {
      var size = BoxCollider.size;
      size.x = value;
      BoxCollider.size = size;
    }
  }

  /// 矩形コリジョンの高さ.
  public float BoxColliderHeight {
    get { return BoxCollider.size.y; }
    set {
      var size = BoxCollider.size;
      size.y = value;
      BoxCollider.size = size;
    }
  }
  // 箱コリジョンのサイズを設定する.
  public void SetBoxColliderSize (float w, float h)
  {
    BoxCollider.size.Set (w, h);
  }
  // 箱コリジョンの有効無効を設定する
  public bool BoxColliderEnabled {
    get { return BoxCollider.enabled; }
    set { BoxCollider.enabled = value; }
  }

  /// 移動して画面内に収めるようにする.
  public void ClampScreenAndMove (Vector2 v)
  {
    Vector2 min = GetWorldMin ();
    Vector2 max = GetWorldMax ();
    Vector2 pos = transform.position;
    pos += v;

    // 画面内に収まるように制限をかける.
    pos.x = Mathf.Clamp (pos.x, min.x, max.x);
    pos.y = Mathf.Clamp (pos.y, min.y, max.y);

    // プレイヤーの座標を反映.
    transform.position = pos;
  }

  /// 画面内に収めるようにする.
  public void ClampScreen ()
  {
    Vector2 min = GetWorldMin ();
    Vector2 max = GetWorldMax ();
    Vector2 pos = transform.position;
    // 画面内に収まるように制限をかける.
    pos.x = Mathf.Clamp (pos.x, min.x, max.x);
    pos.y = Mathf.Clamp (pos.y, min.y, max.y);

    // プレイヤーの座標を反映.
    transform.position = pos;
  }

  /// 画面外に出たかどうか.
  public bool IsOutside ()
  {
    Vector2 min = GetWorldMin ();
    Vector2 max = GetWorldMax ();
    Vector2 pos = transform.position;
    if (pos.x < min.x || pos.y < min.y) {
      return true;
    }
    if (pos.x > max.x || pos.y > max.y) {
      return true;
    }
    return false;
  }

  /// 画面の左下のワールド座標を取得する.
  public Vector2 GetWorldMin (bool noMergin = false)
  {
    Vector2 min = Camera.main.ViewportToWorldPoint (Vector2.zero);
    if (noMergin) {
      // そのまま返す.
      return min;
    }

    // 自身のサイズを考慮する.
    min.x += _width;
    min.y += _height;
    return min;
  }

  /// 画面右上のワールド座標を取得する.
  public Vector2 GetWorldMax (bool noMergin = false)
  {
    Vector2 max = Camera.main.ViewportToWorldPoint (Vector2.one);
    if (noMergin) {
      // そのまま返す.
      return max;
    }

    // 自身のサイズを考慮する.
    max.x -= _width;
    max.y -= _height;
    return max;
  }

  /// 消滅(メモリから削除).
  public void DestroyObj ()
  {
    Destroy (gameObject);
  }

  /// アクティブにする.
  public virtual void Revive ()
  {
    gameObject.SetActive (true);
    Exists = true;
    Visible = true;
  }

  /// 消滅する.
  public virtual void Vanish ()
  {
    gameObject.SetActive (false);
    Exists = false;
  }
}

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
95
Help us understand the problem. What are the problem?