概要
前回、unity公式チュートリアルの2Dシューティングゲームの第四回でSpaceshipクラスを作り、それを自分なりにGameCharaクラスを継承してそれっぽいオブジェクト指向な作りにしました。
unityでオブジェクト指向な作り方ってなんなの\(^o^)/ その2
しかし、継承を意識したせいでunityの本来の特性を生かした、コンポーネントの作成ができておりませんでした。
その後、@mizoguche さんから
「継承による差分プログラミングをするよりも、ひとつのGameObjectに複数の(小さめの)MonoBehaviourをアタッチする方が、再利用しやすく、保守性が高くなる」
というアドバイスをいただき、さらにオブジェクト指向設計についての参考サイトや、今まで作ったクラスの参考例等、様々な助言をいただきました!(本当にありがとうございます!)
そのため、一旦自分のスクリプトを整理しようと思い、その3というよりも復習で2.5にしました。
クラスの修正
MoveObjectクラス(GameCharaクラスから名前変更)
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クラス
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にしました。
変更後のプレイヤークラス
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));
}
}
}
こちらはしっかりコンポーネントを使うような形にしました。
変更後の敵クラス
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));
}
}
これもプレイヤークラスと同じような形になりました。
変更後の弾クラス
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クラスに書こうかなーと思っているところです。
この回は以上です。
この時点でアドバイスがあればお願い致します!