unityでオブジェクト指向な作り方ってなんなの\(^o^)/ その2.5(GameChara,SpaceShipクラスの修正)


概要

前回、unity公式チュートリアルの2Dシューティングゲームの第四回でSpaceshipクラスを作り、それを自分なりにGameCharaクラスを継承してそれっぽいオブジェクト指向な作りにしました。

unityでオブジェクト指向な作り方ってなんなの\(^o^)/ その2

しかし、継承を意識したせいでunityの本来の特性を生かした、コンポーネントの作成ができておりませんでした。
その後、@mizoguche さんから
「継承による差分プログラミングをするよりも、ひとつのGameObjectに複数の(小さめの)MonoBehaviourをアタッチする方が、再利用しやすく、保守性が高くなる」

というアドバイスをいただき、さらにオブジェクト指向設計についての参考サイトや、今まで作ったクラスの参考例等、様々な助言をいただきました!(本当にありがとうございます!)

そのため、一旦自分のスクリプトを整理しようと思い、その3というよりも復習で2.5にしました。

クラスの修正

MoveObjectクラス(GameCharaクラスから名前変更)

csharp.MoveObject.cs

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(SpriteRenderer))]
public class MoveObject : MonoBehaviour {
    [SerializeField]
    public float x_speed;
    [SerializeField]
    public float y_speed;

    public float speed;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }

    public void Move(Vector2 direction){
        direction.Set (direction.x * x_speed, direction.y * y_speed);
        GetComponent<Rigidbody2D> ().velocity = direction;
    }
}

あくまでもキャラが動くだけのコンポーネントとして使うクラスです。
x_speedとy_speedをなるべくクラス内で完結するように、Move関数を変更しました。
Vector2クラスのSet関数を使ってますが、もっとマシな書き方ありそう…

変更後のSpaceshipクラス

csharp.Spaceship.cs
using UnityEngine;
using System.Collections;

public class SpaceShip : MonoBehaviour {

    //弾の発射間隔
    public float shotDelay;
    //弾を打てるか
    public bool canShot;
    //次の弾の
    private bool isRunning = false;

    public GameObject bulletPrefab;

    public GameObject explosionPrefab;

    public IEnumerator Shot(Transform origin){
        //弾を打てるか
        if (!canShot)
            yield break;
        //弾の発射準備中か
        if (isRunning)
            yield break;
        isRunning = true;
        Instantiate (bulletPrefab, origin.position, origin.rotation);
        yield return new WaitForSeconds(shotDelay);
        isRunning = false;
    }
}

ここもあまり変更がないですが、Shot関数をpublicにしました。

変更後のプレイヤークラス

csharp.Player.cs

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MoveObject))]
[RequireComponent(typeof(SpaceShip))]
public class Player : MonoBehaviour {

    private MoveObject moveObject;
    private SpaceShip spaceShip;

    void Awake() {
        moveObject = GetComponent<MoveObject> ();
        spaceShip = GetComponent<SpaceShip> ();
    }

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {
        float x = Input.GetAxisRaw ("Horizontal");

        float y = Input.GetAxisRaw ("Vertical");
        //移動する向きを求める
        Vector2 direction = new Vector2 (x, y);

        moveObject.Move (direction);

        if (Input.GetKey (KeyCode.Z)) {
            //プレイヤーと同じ位置/角度で発射
            StartCoroutine(spaceShip.Shot(transform));
        }
    }
}

こちらはしっかりコンポーネントを使うような形にしました。

変更後の敵クラス

csharp.Enemy.cs

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MoveObject))]
[RequireComponent(typeof(SpaceShip))]
public class Enemy : MonoBehaviour {

    private MoveObject moveObject;
    private SpaceShip spaceShip;

    void Awake() {
        moveObject = GetComponent<MoveObject> ();
        spaceShip = GetComponent<SpaceShip> ();
    }

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {
        Vector2 direction = new Vector2 (1, 1);
        moveObject.Move (direction);
        StartCoroutine(spaceShip.Shot(transform));
    }
}

これもプレイヤークラスと同じような形になりました。

変更後の弾クラス

csharp.Bullet.cs

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MoveObject))]
public class Bullet : MonoBehaviour {

    private MoveObject moveObject;

    void Awake() {
        moveObject = GetComponent<MoveObject> ();
    }

    // Use this for initialization
    void Start () {
        Vector2 direction = new Vector2 (1, 1);
        moveObject.Move (direction);
    }

    // Update is called once per frame
    void Update () {

    }
}

これも(ry

スクリプトを修正した後、プレハブやスクリプトをアタッチし直して、挙動が変わらずに動かすことが出来ました。

まとめ(感想と次回に向けて)

まだ初期段階にアドバイスをもらえたのが幸いで、比較的楽に修正が出来ました!
修正してて感じたこととしては、細かくアタッチしていくとそれはそれで覚えきれなくなりそう…?とも思ったのですが、その辺はバランスなのでしょうか?
でも確実に一つ一つコンポーネントを切り離して考えることはできるので、それは良いですね。(動くクラスとか弾を撃つクラスとか)

次回はようやく当たり判定ですが、これは動く物体として扱うとなるとMoveObjectクラスに書こうかなーと思っているところです。

この回は以上です。
この時点でアドバイスがあればお願い致します!