継承を活用したキャラの挙動を実現するスクリプトでの実装について

前回に引き続きキャラクターに関する記事ですが。今回はスクリプトについて見ていきたいと思います。

登場している敵について

キャラ紹介は以下をご覧ください

今回は挙動ベースで紹介します。

  • Walker
    • 普段は直進している
    • 接近したらこちらに突撃
  • Wing
    • 普段は直進している
    • 接近したらこちらに突撃(こちらは3次元的に移動する)
  • Gunner
    • 接近するまで何もしない
    • 接近してきたらこちらに向けて球を発射する
  • Tank
    • 接近するまで何もしない
    • 接近してきたらこちらに向けてレーザーを発射する
    • 死亡時の爆発エフェクトが特別

設計について

5年前のスクリプトを見ると各自で独立した処理をしていましたが今回はしっかり継承を生かして

設計していきたいと思います。

クラス図を簡単に書くとこうなります。

最低限の敵としての定義と挙動を持ったスクリプト「Enemy」を実装しそれを継承して各々の処理を実装していくことで似たような処理を省きつつ様々な挙動を実現することができます。

例えば、新しく敵「暗黒騎士」を実装するときは「Enemy」を継承することで、共通処理の実装を省き各々の処理の実装に専念できるようになります。

実際の実装

設計を実現させた「Enemy」は以下のようになります。

using UnityEngine;
using System.Collections;
using System;

public abstract class Enemy : MonoBehaviour
{
	// ----------------------------------------------
	// 設定項目

	[SerializeField]
	private Status m_status = null;

	[SerializeField]
	private GameObject m_hitEffect;

	// ----------------------------------------------
	// 内部メンバ変数

	// プレイヤー
	private Player m_player = null;

	// 死んだとき
	private Action m_onDestroyAction = null;

	// ----------------------------------------------
	// アクセサ

	public Status Status
	{
		get
		{
			return m_status;
		}
	}

	public GameObject HitEffect
	{
		get
		{
			return m_hitEffect;
		}
	}

	// ----------------------------------------------
	// 各自で実装してください

	/// <summary>初期化</summary>
	protected abstract void DoInit();

	/// <summary>更新</summary>
	protected abstract void DoUpdate();

	/// <summary>何かオブジェクトにヒットした</summary>
	/// <param name="collision">ヒットしたオブジェクト</param>
	protected abstract void DoOnCollisionEnter(Collision collision);

	/// <summary>
	/// 初期化
	/// </summary>
	/// <param name="player">プレイヤー</param>
	/// <param name="onDestroyAction">死亡時に呼ぶ</param>
	public void Init(Player player, Action onDestroyAction)
	{
		m_player = player;
		m_onDestroyAction = onDestroyAction;

		DoInit();
	}

	// Update is called once per frame
	void Update()
	{
		if(m_status.HP <= 0)
		{
			Destroy(gameObject, 0);
			Debug.Log(gameObject.name + " is destroy");
			GameObject obj = Instantiate(m_hitEffect, transform.position, Quaternion.identity);
			m_onDestroyAction?.Invoke();
			GameMainScore.NowScore += m_status.Point;
		}
		if(transform.position.z > 8)
		{
			Destroy(gameObject, 0f);
		}
		DoUpdate();
	}


	void OnCollisionEnter(Collision collision)
	{
		DoOnCollisionEnter(collision);
	}
}

こうすることで

新しく仮に「暗黒騎士」を実装するとして

以下の3つ処理を実装することで最低限実現可能になります。

public override class DarkKnight: Enemy 
{
	/// <summary>初期化</summary>
	protected override void DoInit()
	{
	}

	/// <summary>更新</summary>
	protected abstract void DoUpdate()

	{
	}

	/// <summary>何かオブジェクトにヒットした</summary>
	/// <param name="collision">ヒットしたオブジェクト</param>
	protected abstract void DoOnCollisionEnter(Collision collision)
	{
	}
}

まとめ

キャラクターのスクリプトはキャラクターの追加にかかわる非常な重要なものですが、

実装時間と実装自体の短縮もクオリティに影響してくるので非常に重要な設計となります。

設計自体が非常に時間のかかる作業ですが後の作業にかかわることなので設計図は書かなくてもよいですが、想像はできるようにしておきたいです。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ABOUT US
vuniformity誰でもない人
トレンドの行く末を見守っている
仮名を名乗るエンジニア

ゲーム開発は仕事であり趣味である
プログラムだけでなく多種多様なスキルを数多く持つ

ゲーム開発は
ソーシャルゲームを開発運用の経験アリ
ゲーム以外にも経験アリ
Webサービス保守開発等に携わる

ゲームプレイの主な戦場は
FGO
FEH
MTGA
マビノギ
ここでは主にunityroomで公開しているゲーム作り直しの軌跡を綴っていきます