Unity ScriptableObject, UnityEvent & GenericObject usage - c#

I would like to combine ScriptableObject along with UnityEvent and GenericObject usage. My ultimate goal is to create generic event and listener and then use ScriptableObject to create specific events e.g. GameObject, int and etc. and handle these with respective listeners.
Here is the code I have so far:
EventTemplate.cs
using System.Collections.Generic;
using UnityEngine;
public class EventTemplate<T> : ScriptableObject {
private List<ListenerTemplate<T>> listeners = new List<ListenerTemplate<T>>();
public void Raise(T go) {
for (int i = listeners.Count - 1; i >= 0; i--) {
listeners[i].OnEventRaised(go);
}
}
public void RegisterListener(ListenerTemplate<T> listener) {
listeners.Add(listener);
}
public void UnregisterListener(ListenerTemplate<T> listener) {
listeners.Remove(listener);
}
}
ListenerTemplate.cs
using UnityEngine;
using UnityEngine.Events;
[System.Serializable]
public class ResponseEvent<T> : UnityEvent<T> { }
public class ListenerTemplate<T> : MonoBehaviour {
//[SerializeField]
public EventTemplate<T> gameEvent;
//[SerializeField]
public ResponseEvent<T> response;
private void OnEnable() {
gameEvent.RegisterListener(this);
}
private void OnDisable() {
gameEvent.UnregisterListener(this);
}
public void OnEventRaised(T go) {
response.Invoke(go);
}
}
Now, when I have both generic types, I created one Event and one Listener for int type.
These are two files:
EventInt.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Event Template", menuName = "Stage Management/Event Templates/Event Int")]
public class EventInt : EventTemplate<int> {
}
and ListenerInt.cs
using UnityEngine;
using UnityEngine.Events;
[System.Serializable]
public class ResponseInt : ResponseEvent<int> { }
public class ListenerInt : ListenerTemplate<int> {
}
then my expectation was, once I add ListenerInt.cs to specific game component via Editor, I will able to access gameEvent and response in the same fashion I can access them as if I define UnityEvent for int type.
However, the reality is that I cannot see / access neither gameEvent nor response via the Editor.

Unity serialization doesn't work on generics T.
you would need to explicitely create an inherited non-generic type for everything you want to serialize in the Inspector. You would need e.g. a
[Serializable] public class IntEvent : UnityEvent<T> { }
in order to be able to serialize it.
In order to do what you want (kind of) I would do this:
First use an interface like
public interface IEventListener<in T>
{
void OnEventRaised(T value);
}
Then make your ListenerTemplate
public abstract class ListenerTemplate<T> : MonoBehaviour, IEventListener<T>
{
// These have to be provided by the inheritor
public abstract UnityEvent<T> unityEvent { get; }
public abstract EventTemplate<T> gameEvent { get; }
private void OnEnable()
{
gameEvent.RegisterListener(this);
}
private void OnDisable()
{
gameEvent.UnregisterListener(this);
}
public void OnEventRaised(T value)
{
unityEvent.Invoke(value);
}
}
As you can see any class inheriting from ListenerTemplate<T> will have to somehow provide both the UnityEvent<T> and the EventTemplate<T>.
So e.g.
// The specific scriptable object doesn't change it just inherits
[CreateAssetMenu(fileName = "New Event Template", menuName = "Stage Management/Event Templates/Event Int")]
public class EventInt : EventTemplate<int>{ }
and
// Specific override for the UnityEvent
[Serializable] public class IntUnityEvent : UnityEvent<int> { }
public class ListenerInt : ListenerTemplate<int>
{
[SerializeField] private EventInt eventInt;
[SerializeField] private IntUnityEvent intUnityEvent;
// override and populate the two abstract properties
// with the references from the serialized fields
public override UnityEvent<int> unityEvent => intUnityEvent;
public override EventTemplate<int> gameEvent => eventInt;
}
This at least reduces the implementation overhead to these two fields for every inheritor and according specific implementations of EventTemplate and UnityEvent.
Finally the EventTemplate<T> just has to use a list of IEventListener instead
public abstract class EventTemplate<TValue> : ScriptableObject
{
private readonly List<IEventListener<TValue>> listeners = new List<IEventListener<TValue>>();
public void Raise(TValue go)
{
// actually why iterate backwards?
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventRaised(go);
}
}
public void RegisterListener(ListenerTemplate<TValue> listener)
{
listeners.Add(listener);
}
public void UnregisterListener(ListenerTemplate<TValue> listener)
{
listeners.Remove(listener);
}
}

Related

Implementing multiple behaviors on a child class from a method on a root class

I'm looking to implement a certain behavior but I'm not sure how to implement it.
Given a base class :
public class Base
{
void Start() { }
void Update() { }
}
And these two classes which inherit it.
public class Behavior1 : Base
{
private int member;
void Start() { member = 0; }
void Update() { member++; }
}
public class Behavior2 : Base
{
private string name;
void Start() { name = "some string"; }
void Update() { if(name) { Console.WriteLine(name) } }
}
And then a final class which I wish to inherit the logic of the two sub classes.
public class Child : Base // ? Behavior1, Behavior2
{
void Start() { } // logic and members implemented but don't need to be referenced
void Update() { }
}
How would I go about having the Child class implement the two Behavior classes? I don't think you can inherit more than one class at a time so I can't do that. Is there another construct which can accomplish this?
Wihtout enter to valorate the inheritance, that probably need some think as you can read in the comments, you can do something like this if you want use both behaviors ni a class that doesn't inherith them:
public class Child : Base
{
private readonly Behavior1 _behavior1;
private readonly Behavior2 _behavior2;
public Child()
{
this._behavior1 = new Behavior1();
this._behavior2 = new Behavior2();
}
public override void Start()
{
this._behavior1.Start();
}
public override void Update()
{
this._behavior2.Update();
}
}
You can also inherith from Behavior1 and only add Behavior2 as a field:
public class Child : Behavior1
{
private readonly Behavior2 _behavior2;
public Child()
{
this._behavior2 = new Behavior2();
}
public override void Update()
{
this._behavior2.Update();
}
}
But, as I said, is probably that you find a better solution thinking about your models and their composition/inheritance.

How can I use Generics to make this FSM more flexible?

I am trying to make an FSM that can be a bit more flexible by having a StateController base class that other controllers (PlayerController, AiController, etc) can derive from. So far the arguments passed have heavy dependencies with do not allow for such flexibility, hence, generics. Only thing is I cannot wrap my mind around the approach.
Here is the code for the FSM as is without the flexibility I was hoping to achieve.
StateController.cs
using UnityEngine;
public class StateController : MonoBehaviour
{
public State currentState;
public State previousState;
public State remainInState;
private void Start()
{
if (!currentState)
return;
currentState.OnEnter(this);
}
private void FixedUpdate()
{
if (!currentState)
return;
currentState.OnFixedUpdate(this);
}
private void Update()
{
if (!currentState)
return;
currentState.OnUpdate(this);
}
private void LateUpdate()
{
if (!currentState)
return;
currentState.OnLateUpdate(this);
}
public void ChangeState(State nextState)
{
if (nextState != remainInState)
{
currentState.OnExit(this);
previousState = currentState;
currentState = nextState;
currentState.OnEnter(this);
}
}
}
State.cs
using UnityEngine;
[CreateAssetMenu(menuName = ("State Controller/State"))]
public class State : ScriptableObject
{
public StateAction[] onEnter;
public StateAction[] onFixed;
public StateAction[] onUpdate;
public StateAction[] onLate;
public StateAction[] onExit;
public StateTransition[] transitions;
public void OnEnter(StateController controller)
{
ExecuteActions(controller, onEnter);
}
public void OnFixedUpdate(StateController controller)
{
ExecuteActions(controller, onFixed);
}
public void OnUpdate(StateController controller)
{
ExecuteActions(controller, onUpdate);
CheckTransitions(controller);
}
public void OnLateUpdate(StateController controller)
{
ExecuteActions(controller, onLate);
}
public void OnExit(StateController controller)
{
ExecuteActions(controller, onExit);
}
private void ExecuteActions(StateController controller, StateAction[] actions)
{
for (int i = 0; i < actions.Length; i++)
{
actions[i].Execute(controller);
}
}
private void CheckTransitions(StateController controller)
{
for (int i = 0; i < transitions.Length; i++)
{
bool result = transitions[i].condition.CheckCondition(controller);
if (result == true)
{
controller.ChangeState(transitions[i].trueState);
}
else
controller.ChangeState(transitions[i].falseState);
}
}
}
StateAction.cs
using UnityEngine;
public abstract class StateAction : ScriptableObject
{
public abstract void Execute(StateController controller);
}
StateCondition.cs
using UnityEngine;
public abstract class StateCondition : ScriptableObject
{
public abstract bool CheckCondition(StateController controller);
}
StateTransition.cs
[System.Serializable]
public class StateTransition
{
public StateCondition condition;
public State trueState;
public State falseState;
}
But I want the flexibilty to use StateController.cs as a base class and be able to derive another class from it (obviously making it abstract and using protected virutal void for functions) like a PlayerController script...
using UnityEngine;
public class PlayerController : StateController
{
//Other Variables Specific to this class
protected override void Start()
{
base.Start();
}
//Other Functions Specific to this class
}
I figured the use of Generics could help with this since I have another script which can only take a StateController as an argument...
[CreateAssetMenu(menuName = ("State Controller/Action Test"))]
public class ActionTest : StateAction
{
public override void Execute(StateController controller /*place PlayerController Here instead */)
{
Debug.Log(controller.currentState);
}
}
Generics could help but it's implementation is daunting to me. Here is an example of the above script with generics used to allow PlayerController to be passed in but would not remedy the system as State.cs would need T as an argument.
public abstract class StateAction<T> : ScriptableObject where T : StateController
{
public abstract void Execute(T controller);
}
using UnityEngine;
[CreateAssetMenu(menuName = ("State Controller/Action Test"))]
public class ActionTest : StateAction<PlayerController>
{
public override void Execute(PlayerController controller)
{
Debug.Log(controller.currentState);
}
}
I know the FSM needs to be restructured but is there anyone else who managed to rework a similar FSM with this approach?
Updated based on the comments
Try:
public abstract class StateAction : ScriptableObject
{
public abstract void Execute(StateController controller);
}
public abstract class StateAction<T> : StateAction
where T : StateController
{
public abstract void Execute(T controller);
public sealed override Execute(StateController controller)
{
if (controller is T)
{
Execute((T)controller);
}
}
}
Then you can have variables like:
StateAction[] onEnter;

Separating Unity C# code from "normal" C# code

I had thought about separating Unity C# code (code that I can only use with using UnityEngine) from "normal" C# code (int x = 3; bool isKnockMeDead = true; void KnockMeDead(); etc.)
Why? So that I could (for example) switch from Unity to Wpf without having to rewrite the whole logic. So my EngineObject is then Window instead of MonoBehviour...
I already have a few solutions to this problem:
But I'm not 100% satisfied with any of them, because...
1st solution - InheritanceSolution.cs
It's the quickest way to do this, but you doesn't have a 100% visual separation and the danger of using Unity Code in the Child class is also given.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets
{
//Unity C#
public class InheritanceSolutionParent : MonoBehaviour
{
public MusicPlayer EngineMusicPlayer { get; private set; }
public void SetObjectName(string name)
{
gameObject.name = name;
}
public void MoveObjectUp(decimal y)
{
gameObject.transform.position += new Vector3(0, (float)y);
}
}
public class MusicPlayer : MonoBehaviour, IMusicPlayer
{
}
//C#
public class InheritanceSolutionChild : InheritanceSolutionParent // => Unity
{
private IMusicPlayer _universalMusicPlayer;
private List<Sound> _sounds;
public void Start() /* => Unity */ => UniversalStart();
public void Update() /* => Unity */ => UniversalUpdate();
private void UniversalStart()
{
Initionalization();
_sounds.Add(new Sound("Toilettenspülung", "Assets/Sounds/Toilettenspülung.mp3"));
_universalMusicPlayer.Play(_sounds[0]);
}
private void Initionalization()
{
SetObjectName("PartialSolution");
_universalMusicPlayer = EngineMusicPlayer; // => Unity
_sounds = new List<Sound>();
}
private void UniversalUpdate()
{
MoveObjectUp(5);
MoveObjectUp(2);
}
}
public class Sound
{
public Sound(string soundName, string soundFileDataPath)
{
SoundName = soundName;
SoundFileDataPath = soundFileDataPath;
}
public string SoundName { get; private set; }
public string SoundFileDataPath { get; private set; }
}
public interface IMusicPlayer
{
void Play(Sound sound);
}
}
2nd solution - PartialSolution.cs
The keyword is actually used if several people want to work on a class without being disturbed and/or if autogenerated code should be separated from the user.
I just use it to separate code, which is ok, but it should not be used to be able to say that every class has 200 lines, because it simply remains one class over when compiling.
And there is the problem :/
Visual separation would be perfect, but nothing changes in the implementation and I can still use Unity code in both(one) Classes.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets
{
//Unity C#
public partial class PartialSolution : MonoBehaviour
{
[SerializeField] private MusicPlayer _engineMusicPlayer;
private void Start() => UniversalStart();
private void Update() => UniversalUpdate();
private void SetObjectName(string name)
{
gameObject.name = name;
}
private void SetUniversalMusicPlayer()
{
_universalMusicPlayer = _engineMusicPlayer;
}
private void MoveObjectUp(decimal y)
{
gameObject.transform.position += new Vector3(0, (float)y);
}
}
public class MusicPlayer : MonoBehaviour, IMusicPlayer
{
}
//C#
public partial class PartialSolution
{
private IMusicPlayer _universalMusicPlayer;
private List<Sound> _sounds;
private void UniversalStart()
{
Initionalization();
_sounds.Add(new Sound("Toilettenspülung", "Assets/Sounds/Toilettenspülung.mp3"));
_universalMusicPlayer.Play(_sounds[0]);
}
private void Initionalization()
{
SetObjectName("PartialSolution");
SetUniversalMusicPlayer();
_sounds = new List<Sound>();
}
private void UniversalUpdate()
{
MoveObjectUp(5);
MoveObjectUp(2);
}
}
public class Sound
{
public Sound(string soundName, string soundFileDataPath)
{
SoundName = soundName;
SoundFileDataPath = soundFileDataPath;
}
public string SoundName { get; private set; }
public string SoundFileDataPath { get; private set; }
}
public interface IMusicPlayer
{
void Play(Sound sound);
}
}
3rd solution - InterfaceSolution.cs
(Ey Max please separate the class GameController (big project)...
uh... gotta go run)
The solution is actually perfect, but the effort is quite high!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets
{
//Unity C#
public class InterfaceSolutionUnity : MonoBehaviour, IInterfaceSolutionUniversalEngine
{
IInterfaceSolutionUniversal _universalInstance;
[SerializeField] private MusicPlayer _musicPlayer;
public void Start()
{
_universalInstance = new InterfaceSolutionUniversal(this, _musicPlayer);
_universalInstance.UniversalStart();
}
public void Update() => _universalInstance.UniversalUpdate();
public void SetObjectName(string name)
{
gameObject.name = name;
}
public void MoveObjectUp(decimal y)
{
gameObject.transform.position += new Vector3(0, (float)y);
}
}
public class MusicPlayer : MonoBehaviour, IMusicPlayer
{
}
//C#
public interface IInterfaceSolutionUniversal
{
void UniversalStart();
void UniversalUpdate();
}
public class InterfaceSolutionUniversal : IInterfaceSolutionUniversal
{
private IInterfaceSolutionUniversalEngine _universalEngineInstance;
private IMusicPlayer _musicPlayer;
private List<Sound> _sounds;
public InterfaceSolutionUniversal(IInterfaceSolutionUniversalEngine universalEngineInstance, IMusicPlayer musicPlayer)
{
_universalEngineInstance = universalEngineInstance;
_musicPlayer = musicPlayer;
_sounds = new List<Sound>();
}
public void UniversalStart()
{
_universalEngineInstance.SetObjectName("PartialSolution");
_sounds.Add(new Sound("Toilettenspülung", "Assets/Sounds/Toilettenspülung.mp3"));
_musicPlayer.Play(_sounds[0]);
}
public void UniversalUpdate()
{
_universalEngineInstance.MoveObjectUp(5);
_universalEngineInstance.MoveObjectUp(2);
}
}
public interface IInterfaceSolutionUniversalEngine
{
void MoveObjectUp(decimal y);
void SetObjectName(string name);
}
public class Sound
{
public Sound(string soundName, string soundFileDataPath)
{
SoundName = soundName;
SoundFileDataPath = soundFileDataPath;
}
public string SoundName { get; private set; }
public string SoundFileDataPath { get; private set; }
}
public interface IMusicPlayer
{
void Play(Sound sound);
}
}
My questions:
Is it even necessary? If yes, when?
Which of the three would be the best in your opinion?
Is there a better one?
In summary, I just want to be a bit more independent from Unity, but I don't want to program a new engine right away.
You can leave up to me how I should design it in the end. (MVVM, MVC, etc.) :)
I look forward to your answers.
Please be my heroes!
I've worked on a project like this. It was great, but also a lot of headache of constantly fighting Unity. Our reason was to have a deterministic game and run an instance of it as an authoritative server without getting Unity involved.
MVC is a great for this.
I've made minimal project a while ago. Have a look.
Models contain data only.
View is a MonoBehaviour with SpriteRenderers/AudioSources that listens for events.
Events are model changes that view is interested in.
Services are a collection of methods that modify the model.
Simulator ticks the model.
Context is where it all begins.

How can I create a List of classes with a generic type parameter in c#?

I'm trying to create my own state machine but ran into some trouble regarding lists of classes with generic type. My code is as below.
State.cs:
using UnityEngine;
using System.Collections;
public abstract class State<T> where T:StateMachine
{
public T sm;
public State()
{
}
public virtual void OnEnter()
{
sm.currentState = sm.futureState;
}
public abstract void OnExit();
public abstract void OnLoop();
}
StateMachine.cs:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public abstract class StateMachine : MonoBehaviour
{
public List<State<T>> stateList = new List<T>>();
public int currentState = -1;
public int futureState;
protected virtual void Start()
{
foreach (State<T> s in stateList)
{
s.sm = this;
}
}
protected virtual void Update()
{
if (currentState != futureState)
{
stateList[futureState].OnEnter();
}
stateList[currentState].OnLoop();
if (currentState != futureState)
{
stateList[currentState].OnExit();
}
}
}
TestStateMachine.cs:
using UnityEngine;
using System.Collections;
public class TestStateMachine : StateMachine
{
public enum StateNames:int
{
State1,
State2,
};
public KeyCode kc;
// Use this for initialization
protected override void Start ()
{
stateList.Add(new TestStateMachineFirstState());
stateList.Add(new TestStateMachineSecondState());
base.Start();
}
}
public class TestStateMachineFirstState : State<StateMachine>
{
public override void OnEnter()
{
Debug.Log("SM1 OnEnter");
base.OnEnter();
}
public override void OnLoop()
{
Debug.Log("SM1 OnLoop");
if (Input.GetKeyDown(sm.kc))
{
sm.futureState = (int)TestStateMachine.StateNames.State2;
}
}
public override void OnExit()
{
Debug.Log("SM1 OnExit");
}
}
public class TestStateMachineSecondState : State<StateMachine>
{
public override void OnEnter()
{
Debug.Log("SM2 OnEnter");
base.OnEnter();
}
public override void OnLoop()
{
Debug.Log("SM2 OnLoop");
if (Input.GetKeyDown(sm.kc))
{
sm.futureState = (int)TestStateMachine.StateNames.State1;
}
}
public override void OnExit()
{
Debug.Log("SM2 OnExit");
}
}
I get error CS0246: Type or namespace name T cannot be found (or something that sounds similar).
My state machine "functions" if I replace all State<T> and State<TestStateMachine> with State<StateMachine> and the if (Input.GetKeyDown(sm.kc)) with (Input.GetKeyDown(KeyCode.A)).
But that is not ideal as I would not be able to get variables from the children state machines. Is there a way to keep this structure(as bad as it might be), or should I try another approach in doing state machines?
One might note that if you look at the compiler error message, it will specify the source file and the line number at which the error was detected. That usually helps identify the problem.
The problem is this:
public abstract class StateMachine : MonoBehaviour
{
public List<State<T>> stateList = new List<T>>();
...
T has no meaning in this class as it's not an generic class or method. Hence, the compiler has no idea what to do with State<T> or List<T.
A second problem is that
public List<State<T>> stateList = new List<T>>();
wouldn't compile even if in a suitable generic class or method: List<State<T>> is not a compatible type with List<T>.
The reason you get this compilation error is because you are using type parameter T of type State in the StateMachine class. You could employ the curiously recurring template pattern:
class State<T> where T : StateMachine
class StateMachine<T> where T : StateMachine
class RealStateMachine : StateMachine<RealStateMachine>
However, this might be very confusing. If you are able, you should consider a design where State is a nongeneric abstract class or interface.

How to override a method in a custom library class

I have two projects: ClientProj and ServerProj, which both share a SharedLibrary containing the basics of my game.
Inside this library I have the class GameObject which is the base class from which many other game items inherit.
Inside GameObject is a SetPosition() method.
Here's my problem: When I run SetPosition() on the client, I wish to add some additional code / override the method completely. The code I wish to add however relates to classes that are only present in the ClientProj namespace, which the SharedLibrary knows nothing about.
Is there any clean way to override or extend the library methods?
Updated: Note that the instances of GameObject and all things that inherit it are defined, contained and handled all within the SharedLibrary namespace. For the most part the ClientProj and ServerProj only handle networking, users and input/output.
You can use the Proxy pattern and have the game objects inherit from the proxy class instead of the real class:
SharedLibrary:
public class GameObject
{
public virtual void SetPosition() { ... }
}
public class DelegatingGameObject : GameObject
{
public GameObject Inner;
public override void SetPosition() { Inner.SetPosition(); }
}
public class Tree : DelegatingGameObject
{
}
ClientLibrary:
class ClientGameObject : GameObject
{
public override void SetPosition()
{
if (isMonday) base.SetPosition();
}
}
var tree = new Tree { Inner = new ClientGameObject() };
tree.SetPosition();
SharedLibrary:
public class GameObject
{
public virtual void SetPosition() { Console.WriteLine("GameObject.SetPosition"); }
public static event Func<GameObject> Factory;
internal static GameObject CreateBase() { var factory = Factory; return (factory != null) ? factory() : new GameObject(); }
}
internal class GameObjectBase : GameObject
{
private readonly GameObject baseGameObject;
protected GameObjectBase() { baseGameObject = GameObject.CreateBase(); }
public override void SetPosition() { baseGameObject.SetPosition(); }
}
internal class Tree : GameObjectBase
{
public override void SetPosition()
{
Console.WriteLine("Tree.SetPosition");
base.SetPosition();
}
}
public static class Game
{
public static void Start()
{
new Tree().SetPosition();
}
}
ClientLibrary:
internal class ClientGameObject : GameObject
{
public override void SetPosition()
{
Console.WriteLine("ClientGameObject.SetPosition Before");
base.SetPosition();
Console.WriteLine("ClientGameObject.SetPosition After");
}
}
internal static class Program
{
static void Main(string[] args)
{
GameObject.Factory += () => new ClientGameObject();
Game.Start();
}
}
Make SetPosition method virtual and use override keyword to override its behaviour in ClientProj.
You can do it virtual in base class, override in derived, and in overriden method call your methods and after base class method.
A psudocode can look like this:
public class GameObject
{
public virtual void SetPosition()
{
//do something here
}
}
public class Derived: GameObject
{
public override void SetPosition()
{
// do something specific to Derived
base.SetPosition(); // CALL BASE CLASS METHOD AFTER
}
}

Categories

Resources