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

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.

Related

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;

Unity ScriptableObject, UnityEvent & GenericObject usage

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);
}
}

How to send value from one class to another in C#?

I have void Start() with string snapshotJson declared inside of it, and I have private void LoadGameData() that needs to call the value of snapshotJson. Declaring snapshotJson public doesn't work, I assume because of void. From what I read I should be using getters and setters, but I have no idea how they work and every guide I've read explaining it makes it seem very simple, but they explain it so simply I don't understand how exactly I'm supposed to use it, or how I can call the value after using the get/set functions.
Could anyone explain how I can get the variable from one class to another? In my code, LoadGameData isn't able to call the value of snapshotJson, I'm not sure what I'm missing.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.IO;
using Firebase;
using Firebase.Unity.Editor;
using Firebase.Database;
using System;
public class DataController : MonoBehaviour
{
private RoundData[] allRoundData;
private PlayerProgress playerProgress;
[Serializable]
public class FirebaseStart : MonoBehaviour
{
public string snapshotJson { get; set; }
void Start()
{
// Set up the Editor before calling into the realtime database.
FirebaseApp.DefaultInstance.SetEditorDatabaseUrl("https://FIREBASEDATABASE");
// Get the root reference location of the database.
DatabaseReference reference =
FirebaseDatabase.DefaultInstance.RootReference;
FirebaseDatabase.DefaultInstance
.GetReference("allRoundData")
.GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
}
else if (task.IsCompleted)
{
// DataSnapshot snapshot = task.Result;
snapshotJson = JsonUtility.ToJson(task.Result);
}
});
}
}
// Use this for initialization
void Start ()
{
DontDestroyOnLoad(gameObject);
LoadGameData();
LoadPlayerProgress();
SceneManager.LoadScene("MenuScreen");
}
public RoundData GetCurrentRoundData()
{
return allRoundData [0];
}
public void SubmitNewPlayerScore(int newScore)
{
if (newScore > playerProgress.highestScore)
{
playerProgress.highestScore = newScore;
SavePlayerProgress();
}
}
public int GetHighestPlayerScore()
{
return playerProgress.highestScore;
}
// Update is called once per frame
void Update () {
}
private void LoadPlayerProgress()
{
playerProgress = new PlayerProgress();
if (PlayerPrefs.HasKey("highestScore"))
{
playerProgress.highestScore = PlayerPrefs.GetInt("highestScore");
}
}
private void SavePlayerProgress()
{
PlayerPrefs.SetInt("highestScore", playerProgress.highestScore);
}
public void LoadGameData()
{
GameData loadedData = JsonUtility.FromJson<GameData>(snapshotJson);
Console.WriteLine(snapshotJson);
allRoundData = loadedData.allRoundData;
}
The LoadGameData() method cannot access it from the Main() method because it is local to that function scope. However, you can pass the value from the Main() method to the LoadGameData() using the code below:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
private static void LoadGameData(String input) {
// Do something inside your input here.
}
public static void Main() {
Start();
}
public static void Start()
{
string snapshotJson = "Sample data";
// Same Class Call
LoadGameData(snapshotJson);
// Other Class Call
var otherClassInstance = new TestClass();
otherClassInstance.LoadGameData(snapshotJson);
}
}
public class TestClass {
public void LoadGameData(String input) {
// Do something inside your input here.
}
}
Assuming you refer to methods instead of classes (or rather: sharing data between objects), this is what you could do. Here's an example of a dedicated class for your Game objects, with SnaphopJson being a public property that can be accessed and changed from any other object. Changing the setter to private would ensure only it can only be read from anything but objects of this class.
public class Game
{
public string SnapshotJson { get; set; }
private void LoadGameData()
{
// load the json, e.g. by deserialization
SnapshotJson = "{}";
}
public void Start()
{
// access the content of your snapshot
var s = SnapshotJson;
}
}
If the variable snapshotJson is declared inside the body of the Start method, it will only be accessible within that method. If you want the variable to be accessible in other methods of your class, you may want to declare it as a member variable. This is how it looks like. You would need to declare it as a public property only if you need to access the value of snapshotJson outside of your class instance.
public class MyClass
{
string snapshotJson;
private void Start()
{
// Assign value to snapshotJson here
snapshotJson = "foo";
}
private void LoadGameData
{
// Use value of snapshotJson here
string s = snapshotJson;
}
}

Is there a way to organize my functions into subclasses or some form of nested container?

So i'm trying to organize my functions into nested classes so i can call them like: "Player.Trigger_Functions.TakeDamage()" rather than calling it as such: "Player.TakeDamage()". I suppose it is a less efficient way to call the functions the way I'm suggesting but it would help separate the functions into distinct categories while remaining on the same file.
Here is some test code but i can't get it to compile online to see if it works.
(some of the functions need to be able to interact with each-other despite being in separate containers which i think is a problem)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Program
{
public class meme{
public int thicc = 0;
public oof nest1 = new oof();
public watermelone nest2 = new watermelone();
public class oof : meme
{
public void here(){
thicc++;
}
public void call(){
nest2.here();
System.Console.WriteLine("oof" + thicc);
}
}
public class watermelone : meme
{
public void here(){
thicc++;
}
public void call(){
nest1.here();
System.Console.WriteLine("watermelone" + thicc);
}
}
}
public static void Main(){
meme me = new meme();
me.nest1.call();//adding 1
me.nest2.call();//adding 1
System.Console.WriteLine("here is the current thicc value of the me class:" + me.thicc);
}
}
Ok yeah so this code wouldn't work at all, i didn't put that much thought into it but you get the idea of what i'm trying to accomplish.
You can use interfaces to break up the functionality of your class into related groups.
From this:
class Person
{
void sayHello() { }
void sayGoodbye() { }
void walkForward() { }
void walkBackward() { }
}
Refactor into this:
interface ISpeak
{
void sayHello();
void sayGoodbye();
}
interface IWalk
{
void walkForward();
void walkBackward();
}
class Person : ISpeak, IWalk
{
void ISpeak.sayHello() { }
void ISpeak.sayGoodbye() { }
void IWalk.walkForward() { }
void IWalk.walkBackward() { }
}
class Program
{
static void Main(string[] args)
{
Person person = new Person();
IWalk walk = person;
ISpeak speak = person;
speak.sayHello();
walk.walkForward();
}
}

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.

Categories

Resources