So I am trying to create my own methods (like Update, Start etc)
Basically, something that will detect input but with a bool method. More like an "OnKeyDown".
I get no errors but nothing happens, here are my classes:
using UnityEngine;
using System.Collections;
public class BeastScript : MonoBehaviour
{
void Update()
{
#region INPUT
//KEYS
if (Input.anyKeyDown)
{
OnKeyDown(Input.inputString);
}
if (Input.anyKey)
{
OnKeyHold(Input.inputString);
}
//MOUSE
if (Input.GetMouseButtonDown(0))
{
OnMouseClick(0);
}
if (Input.GetMouseButtonDown(1))
{
OnMouseClick(1);
}
if (Input.GetMouseButtonDown(2))
{
OnMouseClick(2);
}
if (Input.GetMouseButton(0))
{
OnMouseClickHold(0);
}
if (Input.GetMouseButton(1))
{
OnMouseClickHold(1);
}
if (Input.GetMouseButton(2))
{
OnMouseClickHold(2);
}
if (Input.GetMouseButtonUp(0))
{
OnMouseRelease(0);
}
if (Input.GetMouseButtonUp(1))
{
OnMouseRelease(1);
}
if (Input.GetMouseButtonUp(2))
{
OnMouseRelease(2);
}
#endregion INPUT
}
public virtual void OnKeyDown(string key) { }
public virtual void OnKeyHold(string key) { }
public virtual void OnMouseClick(int button) { }
public virtual void OnMouseClickHold(int button) { }
public virtual void OnMouseRelease(int button) { }
}
Here is the derived
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using Beast;
using Beast.BUI;
using Beast.Extensions;
using System;
using UnityEngine.Events;
using Beast.Web;
public class Tester : BeastScript {
void Start()
{
}
void OnGUI ()
{
}
void Update ()
{
}
public override void OnMouseClick(int button)
{
base.OnMouseClick(button);
if (button == 0)
{
print("Works");
}
}
}
Make sure to call base.Update() if you are running script in the base class method Update.
void Update ()
{
base.Update();
// special Update code
}
When inheriting a class which's methods you want to override, you have to declare these methods virtual. In your example, both classes have a private void Update() (when not declaring an access modifier it is implicitly private).
This means, the inheriting class (Tester) does not even know that its base class already has an Update() method. Thus it will not override it, but hide it. Once Unity calls the Update() method of Tester, it will have absolutely no effect, as it's empty.
What you want is to have Tester's Update() method to include all the contents of BeastScript's Update() method, and optionally extend it. That can be accomplished like this:
public class BeastScript : MonoBehaviour
{
protected virtual void Update() // "protected" so subclasses can see it
{ // and "virtual" so subclasses can override it
if (Input.anyKeyDown)
...
}
}
public class Tester : BeastScript
{
protected override Update() // "protected" because overriding methods must not be less accessible than the method they override
{ // and "override" to specify that this method overrides a virtual method
// optional: do stuff before calling BeastScript.Update()
base.Update(); // this calls BeastScript.Update()
// optional: do stuff after calling BeastScript.Update()
}
}
You made an empty Update method that replaced the base Update method. Remove it in Tester class
Related
I am trying to use virtual and abstract methods to make my game architecture better.
I'm using C# and Unity for this example.
I use a ShipComponent as a base Class because I want all the child classes to do the same thing.
But sometimes I want a certain ShipComponent to do something else.
The code will make it a lot clearer:
ShipComponent.cs:
public abstract class ShipComponent : MonoBehaviour
{
[HideInInspector] public ShipControl shipControl;
public virtual void Init(ShipControl control)
{
this.shipControl = control;
}
public virtual void IsPlayer()
{
SetListeners();
}
public abstract void IsNotPlayer();
public abstract void ReEnable();
public abstract void SetListeners();
}
One of the many child classes that inherits from ShipComponent:
public class Rudder : ShipComponent
{
[Header("Settings")]
public Transform rudder;
[Header("Debug Info")]
[SerializeField] float rudderSpeed;
[SerializeField][Range(-45, 45)] int setRudderAngle = 0;
[SerializeField][Range(-45f, 45f)] float realRudderAngle = 0f;
public override void Init(ShipControl shipControl)
{
base.Init(shipControl);
rudder = transform.GetChild(0).GetChild(4);
StartCoroutine(SmoothRudderChange());
SetListeners();
}
public override void IsPlayer()
{
base.IsPlayer();
}
public override void IsNotPlayer()
{
PlayerShipControl.OnRudderChange -= SetRudder;
}
public override void ReEnable()
{
StartCoroutine(SmoothRudderChange());
SetListeners();
}
public override void SetListeners()
{
PlayerShipControl.OnRudderChange -= SetRudder;
if (!shipControl.shipWrapper.ship.IsPlayer) return;
PlayerShipControl.OnRudderChange += SetRudder;
}
void OnDisable()
{
PlayerShipControl.OnRudderChange -= SetRudder;
StopAllCoroutines();
}
The main draw back I experience with this, is that I have to copy paste all 5 or 6 methods everytime I create a new ShipComponent class.
It seems messy and theres a lot of repeating code, most of the time the only difference in each ShipComponent is the SetListeners part, and StartCoroutines if any.
Is there a way to dynamically set delegate listeners up?
So I could set them in the base class ShipComponent?
Instead of setting each component individually?
Another script that inherits from ShipComponent for completeness:
public class Guns : ShipComponent
{
IEnumerator mouseAimCycle;
public override void Init(ShipControl shipControl)
{
base.Init(shipControl);
InitCannons();
SetListeners();
}
public override void ReEnable()
{
SetListeners();
}
public override void IsPlayer()
{
base.IsPlayer();
mouseAimCycle = AimCycle();
StartCoroutine(mouseAimCycle);
SetListeners();
}
public override void SetListeners()
{
PlayerShipControl.OnFireGuns -= TryFire;
if (!shipControl.shipWrapper.ship.IsPlayer) return;
PlayerShipControl.OnFireGuns += TryFire;
}
public override void IsNotPlayer()
{
StopCoroutine(mouseAimCycle);
PlayerShipControl.OnFireGuns -= TryFire;
}
void OnDisable()
{
PlayerShipControl.OnFireGuns -= TryFire;
StopAllCoroutines();
}
Calling the ShipComponent virtual and abstract methods:
public class ShipControl : MonoBehaviour
{
// Contains Ship + Cargo + Crew and a ref to this ShipControl
public ShipWrapper shipWrapper { get; private set; }
ShipComponent[] shipComponents;
// Gather all ShipComponents and Initialize them.
public void Start()
{
shipComponents = transform.GetComponents<ShipComponent>();
foreach (ShipComponent comp in shipComponents)
{
comp.Init(this);
}
}
// Call this to check if this is players current ship and set the components accordingly.
public void UpdateIsPlayer()
{
if (!shipWrapper.ship.IsPlayer)
foreach (ShipComponent component in shipComponents)
component.IsNotPlayer();
else
foreach (ShipComponent component in shipComponents)
component.IsPlayer();
}
And PlayerShipControl, which I use for input, broadcasting the input through delegates, and the theory is that only the players currently controlled ship will be listening for this input:
public class PlayerShipControl : MonoBehaviour
{
public static event Action<Transform> SetCamToPlayerShip;
public static event Action SetShipPanelUI;
public static event Action<bool> ToggleAnchorIcon, ToggleFlagIcon, ToggleAutofireIcon, ToggleBoatsIcon;
public static event Action OnFireGuns;
public static event Action<int> OnRudderChange;
public static event Action<int> OnSailStateChange;
public static event Action<bool> OnAllAnchorsCommand;
public static event Action<bool> OnAllBoatsCommand;
bool anchor, flag, autofire, boats;
ShipControl shipControl;
void Update()
{
if (Input.GetKeyUp(KeyCode.W)) // Raise Sails SailState++
{
OnSailStateChange?.Invoke(1);
}
if (Input.GetKeyUp(KeyCode.S)) // Furl Sails SailState--
{
OnSailStateChange?.Invoke(-1);
}
if (Input.GetKey(KeyCode.D))
{
OnRudderChange?.Invoke(1);
}
if (Input.GetKey(KeyCode.A))
{
OnRudderChange?.Invoke(-1);
}
if (Input.GetKeyDown(KeyCode.M))
{
OnRudderChange?.Invoke(0);
}
// Drop All Anchors
if (Input.GetKeyDown(KeyCode.V))
{
anchor = true;
ToggleAnchorIcon?.Invoke(anchor);
OnAllAnchorsCommand?.Invoke(anchor);
}
// Haul All Anchors
if (Input.GetKeyDown(KeyCode.H))
{
anchor = false;
ToggleAnchorIcon?.Invoke(anchor);
OnAllAnchorsCommand?.Invoke(anchor);
}
// Drop All Boats
if (Input.GetKeyDown(KeyCode.B))
{
boats = true;
ToggleBoatsIcon?.Invoke(boats);
OnAllBoatsCommand?.Invoke(boats);
}
// Take In All Boats
if (Input.GetKeyDown(KeyCode.U))
{
OnAllBoatsCommand?.Invoke(false);
// TO DO When all boats are back on deck, boatIcon + boatsBoolFlag should be turned off again.
}
if (Input.GetKeyDown(KeyCode.Space))
{
OnFireGuns?.Invoke();
}
}
}
Its a long string of scripts sometimes though so I have left out all the managers and such.
Ship ship inside shipWrapper.ship is a custom data class that stores the info about the ship, not a Monobehaviour, but it holds a bool called IsPlayer aswell. Nothing else of interest I can think of.
The main draw back I experience with this, is that I have to copy paste all 5 or 6 methods every time I create a new ShipComponent class. It seems messy and there's a lot of repeating code, most of the time the only difference in each ShipComponent is the SetListeners part, and StartCoroutines if any.
In the show example you have more differences between implementations then ones described. Without seeing the full code it is hard to suggest something meaningful.
Few notes on the current code:
In Rudder you don't need to specify IsPlayer because the following:
public override void IsPlayer()
{
base.IsPlayer();
}
does not add anything extra, so you can just skip implementation in the derived class.
Based on provided examples it seems that ReEnable can be defined as virtual in base class with default implementation set to calling SetListeners (the same approach as you have with Init and IsPlayer).
PlayerShipControl.Update possibly can be improved by moving handlers to dictionary. Something along this lines:
public class PlayerShipControl : MonoBehaviour
{
// ...
Dictionary<KeyCode, Action> keyActions = new() // not sure about the type
{
{ KeyCode.W, () => OnSailStateChange?.Invoke(1) },
// ...
{ KeyCode.V, () =>
{
anchor = true;
ToggleAnchorIcon?.Invoke(anchor);
OnAllAnchorsCommand?.Invoke(anchor);
}
},
// ...
};
void Update()
{
foreach (var kvp in keyActions)
{
if (Input.GetKeyUp(kvp.Key))
{
kvp.Value();
break;
}
}
}
}
The following is attached to my player and would call upon whatever object is hit to use the objects function. Think the player controls the pointing and clicking, but the object controls whatever the object will do, such as turn on a light.
void Interact()
{
RaycastHit interactablehit;
if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out interactablehit, MaxDistance))
{
// if raycast hits, then it checks if it hit an object with the tag Interactable.
if (interactablehit.transform.tag == "Interactable")
{
object = interactablehit.transform.name;
interactablehit.transform.gameObject.GetComponent<interactablehit.transform.name>().ObjectInteract();
}
}
}
This is a job for either an interface or a common base class:
public interface IInteractable
{
void ObjectInteract();
}
and then have your different implementations like e.g.
public class ExampleInteractable : MonoBevahiour, IInteractable
{
public void ObjectInteract()
{
Debug.Log("Hello!");
}
}
or
public class ToggleLightInteractable : MonoBevahiour, IInteractable
{
[SerializeField] private Light light;
public void ObjectInteract()
{
light.enabled = !light.enabled;
}
}
and then simply do
void Interact()
{
if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out var interactablehit, MaxDistance))
{
var interactable = interactablehit.transform.GetComponent<IInteractable>();
if (interactable != null)
{
interactable.ObjctInteract();
}
}
}
Or the same with a common base class, usefull if you want/need some shared behaviour or default implementations
public class BaseInteractable
{
public virtual void ObjectInteract()
{
Debug.Log("Hello!");
// For demo reasons lets say per default this can be interacted with only once
Destroy(this);
}
}
and then have your different implementations like e.g.
public class ExampleInteractable : BaseInteractable
{
public override void ObjectInteract()
{
// completely override the default behaviour
Debug.Log("Hello World!");
// this can be interacted with forever since the default behavior is not executed
}
}
or
public class ToggleLightInteractable : BaseInteractable
{
[SerializeField] private Light light;
public override void ObjectInteract()
{
// keep the default behavior and only extend it with something additional
base.ObjectInteract();
light.enabled = !light.enabled;
}
}
and then do
void Interact()
{
if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out var interactablehit, MaxDistance))
{
if (interactablehit.transform.TryGetComponent<BaseInteractable>(out var interactable))
{
interactable.ObjctInteract();
}
}
}
If you really really for what reason ever (there shouldn't be any good one) need to stick to string you can (you shouldn't) use SendMessage like
void Interact()
{
if (Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out var interactablehit, MaxDistance))
{
interactablehit.transform.gameObject.SendMessage(interactablehit.transform.name, options: SendMessageOptions.DontRequireReceiver);
}
}
which would require your object to be called exactly like the method you want to call, regardless of how the component is called.
I've created a parent class that I expect to have all functions related to testing if the GameObject is grounded, in water, on air, etc... given that this functions will be used by the player as well as other GameObjects. However, the child class seems not to inherit properly the functions.
The parent script it's as follows:
public class CharacterPhysic : MonoBehaviour {
[SerializeField] protected Transform groundPoints;
float grounRadius;
private void Start ()
{
whatIsGround = LayerMask.GetMask("Ground");
groundRadius = 0.01f;
}
protected bool IsGrounded()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundPoints.position, groundRadius, whatIsGround);
if (colliders.Length > 0)
{
return true;
}
else return false;
}
private void FixedUpdate()
{
Debug.Log(IsGrounded());
}
}
And the children script is just:
public class ErrantMove : CharacterPhysic {
private void FixedUpdate()
{
Debug.Log(IsGrounded());
}
}
When added the first script as a component to a Gameobject (after defining the grounPoint) the Debug.Log(IsGrounded()); returns TRUE
However, when added the second script as a component to the same Gameobject (after defining the grounPoint and remove the first script) the Debug.Log(IsGrounded()); returns FALSE even in the same circumstances.
I'm expecting to be able to add movement functions into the second script but this depends on the ability to test if it is grounded.
You can properly inherit Unity's callback functions such as Awake, Start and Update like you would with a normal C# inheritance paradigm. It's very simply.
Make the all the callback functions for the base class to be virtual:
public class YourBaseClass : MonoBehaviour
{
protected virtual void Awake()
{
Debug.Log("Awake Base");
}
protected virtual void Start()
{
Debug.Log("Start Base");
}
protected virtual void Update()
{
Debug.Log("Update Base");
}
protected virtual void FixedUpdate()
{
Debug.Log("FixedUpdate Base");
}
}
For the parent class that will derive from the base class, add the callback functions too but mark them as override. If you need the base function to be called, call it with base.FunctionName before doing anything else in the parent function:
public class Parent : YourBaseClass
{
protected override void Awake()
{
base.Awake();
Debug.Log("Awake Parent");
}
protected override void Start()
{
base.Start();
Debug.Log("Start Parent");
}
protected override void Update()
{
base.Update();
Debug.Log("Update Parent");
}
protected override void FixedUpdate()
{
base.FixedUpdate();
Debug.Log("FixedUpdate Parent");
}
}
When tested in Unity version 2020.1.21f, protected callback functions like Start will automatically be executed in their child classes. There is no need to declare a function as virtual, you should only do that if you intend to override the function in a subclass.
public class ParentClass : MonoBehavior
{
protected void Start()
{
Debug.Log("Hello, my class is ");
Debug.Log(this.GetType());
}
}
public class ChildClass : ParentClass
{
//empty class
}
public class GameManager : MonoBehavior
{
//childPrefab is a prefab containing the ChildClass script as a component
Object.Instantiate(childPrefab);
}
Upon instantiating the childPrefab containing the ChildClass script, the object will print, "Hello, my class is ChildClass".
As for your example, the only thing you would need to do to fix that code is change your Start function to protected. That will ensure that Start is inherited by ErrantMove. Additionally, if you don't intend to use FixedUpdate for anything other than checking IsGrounded(), you could mark FixedUpdate as protected, and then delete it from your subclass.
If you want to add additional functionality to FixedUpdate in your subclass, then you will need follow the advice of the other answerers and use virtual, override, and base.FixedUpdate().
Your Start method will not be executed in child class. Move it into child class if you are not going to use parent class.
public class ErrantMove : CharacterPhysic {
protected void Start ()
{
whatIsGround = LayerMask.GetMask("Ground");
groundRadius = 0.01f;
}
private void FixedUpdate()
{
Debug.Log(IsGrounded());
}
}
You can notice that if you will have more than one child classes, you will be forced to initialize base class's Start method in every child class, so you can create an virtual Start method, then call it inside child classes:
public class CharacterPhysic : MonoBehaviour {
. . .
protected virtual void Start ()
{
whatIsGround = LayerMask.GetMask("Ground");
groundRadius = 0.01f;
}
protected bool IsGrounded()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundPoints.position, groundRadius, whatIsGround);
return colliders.Length > 0;
}
. . .
}
public class ErrantMove : CharacterPhysic {
protected override void Start()
{
base.Start();
}
private void FixedUpdate()
{
Debug.Log(IsGrounded());
}
}
you can go either virtual/override approach, or use new keyword to "unhide" child class's Start method (I'm not sure how new will behave in unity, so better go with first one).
I am trying to do a state machine to controll ai behaviour in Unity 3D.
My question is regarding inheritance. Im trying to set up some base logic that handles how and why states shoul be changed. But further down the inheritance line i need different kind of characters to be able to do character speicfic things. But im not able to do this with inheritnance.
Can someone confirm that my thinking is not how its done? then i know to find another solution.
PSEUDO CODE:
// STATE CONTROLLERS CONTROLL THE CHARACETER BY CHOOSING WITCH STATE THEY SHOULD BE IN
abstract class StateController {
StateBase state;
int HitPoints;
int Hunger:
abstract void Update()
{
CheckIfStateShouldChange();
state.UpdateState(this);
}
}
WolfStateController : StateController {
WolfState state;
override void Update()
{
base.Update();
state.Update(this);
}
}
SheepStateController : StateController {
SheepState state;
override void Update()
{
base.Update();
state.Update(this);
}
}
// STATES CONTAINS LOGIC FOR BEHAVIOUR IN A CERTAIN STATE
StateBase {
virtual void UpdateState( StateController controller)
{
// Does things all inheriting classes should do
}
}
WolfState : StateBase {
override void UpdateState( WolfStateController wolfstate)
{
base.UpdateState(WolfStateController wolfstate)
//Does wolf specific things that needs to be done in all WolfStates
}
}
WolfStalkAndHuntState : WolfState {
override void UpdateState( WolfStateController wolfstate)
{
base.UpdateState(WolfStateController wolfState);
//Hunts sheep and attacks on sight
}
}
SheepState : StateBase {
override void UpdateState( SheepStateController sheepState)
{
//Does sheepy things
}
}
SheepReproduceState : SheepState {
override void UpdateState( SheepStateController sheepState)
{
base.UpdateState(SheepStateController sheepState);
// Looks for mate and gets freaky
}
}
I would suggest something like this:
Controller Class:
public class SoliderController : MonoBehaviour
{
[HideInInspector] public sState currentState;
[HideInInspector] public FireState fireState;
[HideInInspector] public IdleState idleState;
[HideInInspector] public ChaseState chaseState;
private void Awake()
{
fireState = new FireState(this);
idleState = new IdleState(this);
chaseState = new ChaseState(this);
}
private void Start ()
{
currentState = idleState;
}
private void Update()
{
currentState.Update();
}
Interface:
public abstract class sState
{
public abstract void Update();
public abstract void ToChaseState();
public abstract void ToIdleState();
public abstract void ToFireState();
}
Example Class
public class IdleState : sState
{
private readonly SoliderController controller;
public IdleState(SoliderController soliderController)
{
controller = soliderController;
}
public override void Update()
{
Patrol();
//Condition to change state
if (*expresion1*)
ToChaseState();
if (*expresion2*)
ToFireState();
}
private void Patrol()
{
//Your Logic for the behvaiour wanted.
}
public override void ToChaseState()
{
controller.currentState = controller.chaseState;
}
public override void ToFireState()
{
controller.currentState = controller.fireState;
}
public override void ToIdleState()
{
Debug.LogWarning("Can't transition to same state");
}
}
This way checking for changes is way easier and also you can impletement state specific behaviours as well. Also, adding a new state goes really easy, you just implement the new Class and ToNewState method in the interface.
Hope it helped.
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.