Unity
Unity2D

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

More than 3 years have passed since last update.


はじめに

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;
}
}