はじめに
ゲームを作っていて、必殺技の作成等を簡単に行えるようにできないだろうか?ということを考えたのが今回の発端です。そこで、ScriptableObjectにパラメーターとメソッドを定義して入れ替えるだけで挙動を変えれると簡単なのでは?と考えついて試してみたので備忘録として書きます。
上のサンプルでは、ジャンプする挙動と色が変わる挙動をScriptableObjectの入れ替えで行っています。
gifで動かしているプロジェクトは公開しているので、説明読むより触ってみたい!という方はご活用ください。
https://github.com/MizoTake/ChangeBehaviourWithScriptableObject
一連の流れ
最初に、今回の処理がどういう流れで行われているのか書きます。
まず、gifにあったCubeはSpaceキーを押すとInspectorから代入されたScriptableObjectの振る舞いを実行します。
中身は以下のようになっています。
Cubeに貼られているスクリプトから
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ControllerInput : MonoBehaviour
{
[SerializeField]
private Rigidbody _rigid;
[SerializeField]
private Behaviour _behaviour;
public Rigidbody Rigid { get { return _rigid; } }
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
_behaviour.Move(this);
}
}
}
SpaceKeyが押されるとBehaviourというクラスのMoveメソッドを実行します。
次に、そのBehaviourというクラスです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Behaviour : ScriptableObject
{
public abstract void Move(ControllerInput input);
}
public static class BehaviourExtension
{
public static void Init(this Behaviour behaviour)
{
Debug.Log("Initialize");
}
}
これは、abstractを宣言しているので基底クラスです。継承先で振る舞いを書いて欲しいメソッドを定義しています。今回は、Moveメソッドになります。
そして、ScriptableObjectクラスを継承しているので継承先のクラスをScriptableObjectにできるようになります。
また、abstractクラスには、そのまま拡張メソッドを追加できるので共通で何かを行いたい場合等に使うと便利です。(今回はTips程度に書いています。)
次にgifの最初にJumpしていたクラスです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump : Behaviour
{
public int Power;
public override void Move(ControllerInput input)
{
this.Init();
input.Rigid.AddForce(Vector3.up * Power, ForceMode.Impulse);
}
}
これはBehaviourを継承してMoveの振る舞いを定義しています。そのためgifでは、JumpクラスがInputControllerのInspectorに代入され、Moveが呼ばれるためジャンプをしていました。
ここで便利になるのはScriptableObjectを作成した後、Powerの値を変えることでジャンプの高さも変えることが容易になるという点です。
また、this.Init()で呼び出すことによりabstractの拡張メソッドであるInitが呼ばれます。
ここまでが基本的な一連の流れになります。
次からは+αで便利だと思った部分を書きます。
+α
これは最初のgifでバラバラに実行されていた、ジャンプの振る舞いと色を変える振る舞いを複合して実行しているものになります。
まず、色を変えるクラスです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeMaterial : Behaviour
{
public Material Target;
public override void Move(ControllerInput input)
{
this.Init();
input.gameObject.GetComponent<MeshRenderer>().material = Target;
}
}
ScriptableObjectにした後、InspectorでMaterialを代入しています。そして、Jump同様にInputControllerのInspctorに代入することで動作します。
次に複合させたクラスです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Composite : Behaviour
{
public Behaviour[] Behaviours;
public override void Move(ControllerInput input)
{
this.Init();
foreach (var behaviour in Behaviours)
{
behaviour.Move(input);
}
}
}
中身を見ると何てことはないと思います。配列をInspectorから入れて、for文で一気に実行するというものです。
ただ、Behaviourクラスがあるだけで振る舞いの複合やランダム実行などが簡単に定義でき、Compositeクラス自体もBehaviour継承クラスなので、InputControllerのInspectorに代入するだけで切り替えれるというのは便利だと思っています。
InputControllerに対してCharacterごとの動きを設定していけば同じControllerでも振る舞いが変わるので便利かと思います。ここからRxやDIで読みやすく扱いやすくしていくことも良いと思っています。
以上で今回の記事はすべてになります。
(…ScriptableObjectにメソッドが書けるのが個人的に驚きだった)
また、ScriptableObjectの作成に関しては、個人的に以下の記事がオススメです。
【UnityEditor】ScriptableObjectのアセットを右クリックから作れるようにするエディター拡張
右クリックから作れるようになるので簡単作成できるようになって便利でした!