浏览代码

Removed ScriptableObjects array from StateMachine. Adjusted test scene. Single line methods now use brackets, properties still use lambda when possible.

Removed the 'override' feature that was implemented as it was adding unnecessary complexity.
Adjusted test scene to reflect the changes:
- Added `ChaseComponent.cs`. It takes `Transform _target`, `float _speed` and has a `public void Chase()` function that moves the gameObject towards the target.
- `ChaseAction` now simply calls `ChaseComponent.Chase()` in its `OnUpdate()`.
- `ChaseComponent` also contains a public getter `Target => _target`.
- `CloseToTargetCondition` gets `transform` from the `StateMachine` and the transform of the target from `ChaseComponent`, the `Statement()` remains the same. Another way to implement it would be to have a public getter `bool IsCloseToTarget` or `float DistanceToTarget` in `ChaseComponent` and evaluate that in the `Statement`.
- Removed `ChaseData.cs`.
- How much each sphear eats is now contro...
/main
DeivSky 4 年前
当前提交
b7f0951d
共有 20 个文件被更改,包括 211 次插入130 次删除
  1. 138
      UOP1_Project/Assets/Scenes/StateMachines.unity
  2. 2
      UOP1_Project/Assets/Scripts/StateMachine/Core/StateAction.cs
  3. 2
      UOP1_Project/Assets/Scripts/StateMachine/Core/StateCondition.cs
  4. 34
      UOP1_Project/Assets/Scripts/StateMachine/Core/StateMachine.cs
  5. 2
      UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateActionSO.cs
  6. 2
      UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateConditionSO.cs
  7. 4
      UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateSO.cs
  8. 32
      UOP1_Project/Assets/Scripts/StateMachineTest/ChaseActionSO.cs
  9. 29
      UOP1_Project/Assets/Scripts/StateMachineTest/CloseToTargetConditionSO.cs
  10. 19
      UOP1_Project/Assets/Scripts/StateMachineTest/EatActionSO.cs
  11. 2
      UOP1_Project/Assets/Scripts/StateMachineTest/HungerActionSO.cs
  12. 17
      UOP1_Project/Assets/Scripts/StateMachineTest/HungerComponent.cs
  13. 6
      UOP1_Project/Assets/Scripts/StateMachineTest/HungerConditionSO.cs
  14. 15
      UOP1_Project/Assets/Scripts/StateMachineTest/TimerConditionSO.cs
  15. 2
      UOP1_Project/Assets/Scripts/StateMachineTest/ChaseComponent.cs.meta
  16. 17
      UOP1_Project/Assets/Scripts/StateMachineTest/ChaseComponent.cs
  17. 8
      UOP1_Project/Assets/ScriptableObjects/StateMachinesTest/Data.meta
  18. 10
      UOP1_Project/Assets/Scripts/StateMachineTest/ChaseDataSO.cs
  19. 0
      /UOP1_Project/Assets/Scripts/StateMachineTest/ChaseComponent.cs.meta

138
UOP1_Project/Assets/Scenes/StateMachines.unity


m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 233317031}
m_LocalRotation: {x: 0.18159129, y: -0.3483003, z: 0.068970375, w: 0.91703576}
m_LocalRotation: {x: 0.1815913, y: -0.34830034, z: 0.06897038, w: 0.91703576}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:

m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &291513498 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8065985805251957531, guid: 2ae670cd29a6f4843aa75201aa44ef31,
type: 3}
m_PrefabInstance: {fileID: 1531195266}
m_PrefabAsset: {fileID: 0}
--- !u!114 &291513499
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 291513498}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96a840f8cd2f43e47a32dc87d494422f, type: 3}
m_Name:
m_EditorClassIdentifier:
_target: {fileID: 474047189}
_speed: 10
--- !u!1 &308908774
GameObject:
m_ObjectHideFlags: 0

m_Father: {fileID: 0}
m_RootOrder: 8
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!4 &474047189 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 3341179906418240731, guid: 0fa393e1e37bc9e4e829c25a9452bcd3,
type: 3}
m_PrefabInstance: {fileID: 659246790}
m_PrefabAsset: {fileID: 0}
--- !u!1 &526256409
GameObject:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 2ae670cd29a6f4843aa75201aa44ef31, type: 3}
--- !u!4 &654477362 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 3341179906418240731, guid: 0fa393e1e37bc9e4e829c25a9452bcd3,
type: 3}
m_PrefabInstance: {fileID: 1502309802}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &659246790
PrefabInstance:
m_ObjectHideFlags: 0

m_Father: {fileID: 0}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &923580480 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8065985805251957531, guid: 2ae670cd29a6f4843aa75201aa44ef31,
type: 3}
m_PrefabInstance: {fileID: 1926130911}
m_PrefabAsset: {fileID: 0}
--- !u!114 &923580481
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 923580480}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96a840f8cd2f43e47a32dc87d494422f, type: 3}
m_Name:
m_EditorClassIdentifier:
_target: {fileID: 474047189}
_speed: 10
--- !u!1 &955504286
GameObject:
m_ObjectHideFlags: 0

m_Father: {fileID: 0}
m_RootOrder: 12
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1392770825 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8065985805251957531, guid: 2ae670cd29a6f4843aa75201aa44ef31,
type: 3}
m_PrefabInstance: {fileID: 557383924}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1392770826
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1392770825}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96a840f8cd2f43e47a32dc87d494422f, type: 3}
m_Name:
m_EditorClassIdentifier:
_target: {fileID: 654477362}
_speed: 10
--- !u!1 &1424964852
GameObject:
m_ObjectHideFlags: 0

objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 2ae670cd29a6f4843aa75201aa44ef31, type: 3}
--- !u!1 &1538479374 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8065985805251957531, guid: 2ae670cd29a6f4843aa75201aa44ef31,
type: 3}
m_PrefabInstance: {fileID: 1184947906}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1538479375
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1538479374}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96a840f8cd2f43e47a32dc87d494422f, type: 3}
m_Name:
m_EditorClassIdentifier:
_target: {fileID: 654477362}
_speed: 10
--- !u!1 &1542425560 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8065985805251957531, guid: 2ae670cd29a6f4843aa75201aa44ef31,
type: 3}
m_PrefabInstance: {fileID: 987108458}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1542425561
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1542425560}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96a840f8cd2f43e47a32dc87d494422f, type: 3}
m_Name:
m_EditorClassIdentifier:
_target: {fileID: 1709899635}
_speed: 10
--- !u!1 &1550893637 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8065985805251957531, guid: 2ae670cd29a6f4843aa75201aa44ef31,
type: 3}
m_PrefabInstance: {fileID: 8065985805952354429}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1550893638
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1550893637}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 96a840f8cd2f43e47a32dc87d494422f, type: 3}
m_Name:
m_EditorClassIdentifier:
_target: {fileID: 1709899635}
_speed: 10
--- !u!1 &1559010558
GameObject:
m_ObjectHideFlags: 0

m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1961065787}
m_LocalRotation: {x: 0.17952347, y: -0.3484549, z: 0.068184994, w: 0.91744286}
m_LocalRotation: {x: 0.17952348, y: -0.3484549, z: 0.068184994, w: 0.91744286}
m_LocalPosition: {x: 3.213417, y: 6, z: -23.211498}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []

m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2049112419}
m_LocalRotation: {x: 0.17952347, y: -0.3484549, z: 0.068184994, w: 0.91744286}
m_LocalRotation: {x: 0.17952348, y: -0.3484549, z: 0.068184994, w: 0.9174428}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:

2
UOP1_Project/Assets/Scripts/StateMachine/Core/StateAction.cs


/// Awake is called when creating a new instance. Use this method to cache the components needed for the action.
/// </summary>
/// <param name="stateMachine">The <see cref="StateMachine"/> this instance belongs to.</param>
/// <remarks>To allow overriding any default behaviour set internally or through the inspector, <see cref="StateMachine.TryGetScriptableObject{T}(out T)"/> can be used, overriding if T was found.
/// Make sure to specify the expected type of T and what exactly would get overrriden by it.</remarks>
public virtual void Awake(StateMachine stateMachine) { }
public virtual void OnStateEnter() { }

2
UOP1_Project/Assets/Scripts/StateMachine/Core/StateCondition.cs


/// Awake is called when creating a new instance. Use this method to cache the components needed for the condition.
/// </summary>
/// <param name="stateMachine">The <see cref="StateMachine"/> this instance belongs to.</param>
/// <remarks>To allow overriding any default behaviour set internally or through the inspector, <see cref="StateMachine.TryGetScriptableObject{T}(out T)"/> can be used, overriding if T was found.
/// Make sure to specify the expected type of T and what exactly would get overrriden by it.</remarks>
public virtual void Awake(StateMachine stateMachine) { }
public virtual void OnStateEnter() { }
public virtual void OnStateExit() { }

34
UOP1_Project/Assets/Scripts/StateMachine/Core/StateMachine.cs


[Tooltip("Set the initial state of this StateMachine")]
[SerializeField] private ScriptableObjects.StateSO _initialStateSO = null;
[Space]
[Tooltip("Certain Actions and Conditions allow overriding their default behaviour through the use of ScriptableObjects. Place the SOs here so they can be accessed.")]
[SerializeField] private ScriptableObject[] _scriptableObjects = null;
private List<Type> _scriptableObjectsTypes = null;
_scriptableObjectsTypes = GetObjectTypes(_scriptableObjects);
_currentState = _initialStateSO.GetState(this);
_currentState.OnStateEnter();
#if UNITY_EDITOR

private static List<Type> GetObjectTypes(object[] objects)
{
int count = objects.Length;
var types = new List<Type>(count);
for (int i = 0; i < count; i++)
types.Add(objects[i].GetType());
return types;
}
public bool TryGetScriptableObject<T>(out T sObject) where T : ScriptableObject
{
int i = _scriptableObjectsTypes.IndexOf(typeof(T));
sObject = i >= 0 ? (T)_scriptableObjects[i] : null;
return i >= 0;
}
public T GetScriptableObject<T>() where T : ScriptableObject
{
return TryGetScriptableObject<T>(out var sObject) ? sObject : throw new InvalidOperationException($"{typeof(T).Name} not found in {name}");
}
public new bool TryGetComponent<T>(out T component) where T : Component
{
var type = typeof(T);

}
public new T GetComponent<T>() where T : Component
=> TryGetComponent(out T component) ? component : throw new InvalidOperationException($"{typeof(T).Name} not found in {name}.");
{
return TryGetComponent(out T component)
? component : throw new InvalidOperationException($"{typeof(T).Name} not found in {name}.");
}
private void Update()
{

2
UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateActionSO.cs


protected abstract StateAction CreateAction();
}
public abstract class ScriptableStateAction<T> : StateActionSO where T : StateAction, new()
public abstract class StateActionSO<T> : StateActionSO where T : StateAction, new()
{
protected override StateAction CreateAction() => new T();
}

2
UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateConditionSO.cs


}
public abstract class ScriptableStateCondition<T> : StateConditionSO where T : Condition, new()
public abstract class StateConditionSO<T> : StateConditionSO where T : Condition, new()
{
protected override Condition CreateCondition() => new T();
}

4
UOP1_Project/Assets/Scripts/StateMachine/ScriptableObjects/StateSO.cs


[SerializeField] private StateTransitionSO[] _transitions = null;
public State GetState(StateMachine stateMachine)
=> GetState(stateMachine, new Dictionary<ScriptableObject, object>());
{
return GetState(stateMachine, new Dictionary<ScriptableObject, object>());
}
internal State GetState(StateMachine stateMachine, Dictionary<ScriptableObject, object> createdInstances)
{

32
UOP1_Project/Assets/Scripts/StateMachineTest/ChaseActionSO.cs


using UnityEngine;
[CreateAssetMenu(fileName = "Chase", menuName = "State Machines/Tests/Actions/Chase")]
public class ChaseActionSO : StateActionSO
{
[Tooltip("Object that specifies the speed and the target to chase. This can be overriden in the StateMachine.")]
[SerializeField] private ChaseDataSO _chaseData = null;
protected override StateAction CreateAction() => new ChaseAction(_chaseData);
}
public class ChaseActionSO : StateActionSO<ChaseAction> { }
private Transform _transform;
private Transform _chaseTransform;
private ChaseDataSO _chaseData;
public ChaseAction(ChaseDataSO chaseData) => _chaseData = chaseData;
private ChaseComponent _chaseComponent;
_transform = stateMachine.transform;
if (stateMachine.TryGetScriptableObject<ChaseDataSO>(out var chaseData))
_chaseData = chaseData;
if (_chaseData == null)
throw new ArgumentNullException(nameof(_chaseData));
if (string.IsNullOrEmpty(_chaseData.TargetName))
throw new ArgumentNullException(nameof(_chaseData.TargetName));
_chaseTransform = GameObject.Find(_chaseData.TargetName).transform;
_chaseComponent = stateMachine.GetComponent<ChaseComponent>();
_transform.position = Vector3.MoveTowards(
_transform.position,
_chaseTransform.position,
_chaseData.Speed * Time.deltaTime);
_chaseComponent.Chase();
}
}

29
UOP1_Project/Assets/Scripts/StateMachineTest/CloseToTargetConditionSO.cs


using UnityEngine;
[CreateAssetMenu(fileName = "CloseToTarget", menuName = "State Machines/Tests/Conditions/Close To Target")]
public class CloseToTargetConditionSO : StateConditionSO
{
[SerializeField] private ChaseDataSO _chaseData = null;
protected override Condition CreateCondition() => new CloseToTargetCondition(_chaseData);
}
public class CloseToTargetConditionSO : StateConditionSO<CloseToTargetCondition> { }
private Transform _chaseTransform;
private ChaseDataSO _chaseData;
public CloseToTargetCondition(ChaseDataSO chaseData) => _chaseData = chaseData;
private Transform _target;
_target = stateMachine.GetComponent<ChaseComponent>().Target;
}
if (stateMachine.TryGetScriptableObject<ChaseDataSO>(out var chaseData))
_chaseData = chaseData;
if (_chaseData == null)
throw new ArgumentNullException(nameof(_chaseData));
if (string.IsNullOrEmpty(_chaseData.TargetName))
throw new ArgumentNullException(nameof(_chaseData.TargetName));
_chaseTransform = GameObject.Find(_chaseData.TargetName).transform;
public override bool Statement()
{
return Vector3.Distance(_transform.position, _target.position) < 1f;
public override bool Statement() => Vector3.Distance(_transform.position, _chaseTransform.position) < 1f;
}

19
UOP1_Project/Assets/Scripts/StateMachineTest/EatActionSO.cs


using UnityEngine;
[CreateAssetMenu(fileName = "Eat", menuName = "State Machines/Tests/Actions/Eat")]
public class EatActionSO : StateActionSO
{
public float amount = 10f;
protected override StateAction CreateAction() => new EatAction(amount);
}
public class EatActionSO : StateActionSO<EatAction> { }
private float _amount = 10f;
public EatAction(float amount) => _amount = amount;
=> _hungerComponent = stateMachine.GetComponent<HungerComponent>();
{
_hungerComponent = stateMachine.GetComponent<HungerComponent>();
}
public override void OnStateExit() => _hungerComponent.Eat(_amount);
public override void OnStateExit()
{
_hungerComponent.Eat();
}
}

2
UOP1_Project/Assets/Scripts/StateMachineTest/HungerActionSO.cs


using UnityEngine;
[CreateAssetMenu(fileName = "EnableHunger", menuName = "State Machines/Tests/Actions/Enable Hunger")]
public class HungerActionSO : ScriptableStateAction<HungerAction> { }
public class HungerActionSO : StateActionSO<HungerAction> { }
public class HungerAction : StateAction
{

17
UOP1_Project/Assets/Scripts/StateMachineTest/HungerComponent.cs


public class HungerComponent : MonoBehaviour
{
[SerializeField] private float _eatAmount = 10f;
[SerializeField] private float _maxFullness = 100f;
[SerializeField] private float _hungerPerSecond = 1f;
[SerializeField] [Range(0.01f, 0.99f)] private float _isHungryThreshold = 0.4f;

private void Awake() => _currentFullness = _maxFullness;
private void Awake()
{
_currentFullness = _maxFullness;
}
private void Update()
{

public void Eat(float amount)
=> _currentFullness = Mathf.Min(_currentFullness + amount, _maxFullness);
public void Eat()
{
_currentFullness = Mathf.Min(_currentFullness + _eatAmount, _maxFullness);
}
public void ToggleHunger(bool value) => _getHungry = value;
public void ToggleHunger(bool value)
{
_getHungry = value;
}
}

6
UOP1_Project/Assets/Scripts/StateMachineTest/HungerConditionSO.cs


using UnityEngine;
[CreateAssetMenu(fileName = "IsHungry", menuName = "State Machines/Tests/Conditions/IsHungry")]
public class HungerConditionSO : ScriptableStateCondition<HungerCondition> { }
public class HungerConditionSO : StateConditionSO<HungerCondition> { }
public class HungerCondition : Condition
{

=> _hungerComponent = stateMachine.GetComponent<HungerComponent>();
{
_hungerComponent = stateMachine.GetComponent<HungerComponent>();
}
public override bool Statement() => _hungerComponent.IsHungry;
}

15
UOP1_Project/Assets/Scripts/StateMachineTest/TimerConditionSO.cs


private float _duration = 10f;
private float _time;
public TimerCondition(float duration) => _duration = duration;
public TimerCondition(float duration)
{
_duration = duration;
}
public override void OnStateEnter() => _time = Time.time + _duration;
public override void OnStateEnter()
{
_time = Time.time + _duration;
}
public override bool Statement() => Time.time >= _time;
public override bool Statement()
{
return Time.time >= _time;
}
}

2
UOP1_Project/Assets/Scripts/StateMachineTest/ChaseComponent.cs.meta


fileFormatVersion: 2
guid: e566d72e94cd1744d9ca5a211685aeda
guid: 96a840f8cd2f43e47a32dc87d494422f
MonoImporter:
externalObjects: {}
serializedVersion: 2

17
UOP1_Project/Assets/Scripts/StateMachineTest/ChaseComponent.cs


using UnityEngine;
public class ChaseComponent : MonoBehaviour
{
[SerializeField] private Transform _target = default;
[SerializeField] private float _speed = 10f;
public Transform Target => _target;
public void Chase()
{
transform.position = Vector3.MoveTowards(
transform.position,
_target.position,
_speed * Time.deltaTime);
}
}

8
UOP1_Project/Assets/ScriptableObjects/StateMachinesTest/Data.meta


fileFormatVersion: 2
guid: 00e26cdf15cdc1348ac5f6fa154ddb6c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

10
UOP1_Project/Assets/Scripts/StateMachineTest/ChaseDataSO.cs


using UnityEngine;
[CreateAssetMenu(fileName = "ChaseData", menuName = "State Machines/Tests/Data/Chase Data")]
public class ChaseDataSO : ScriptableObject
{
[SerializeField] private string _targetName = "Player";
[SerializeField] private float _speed = 10f;
public float Speed => _speed;
public string TargetName => _targetName;
}

/UOP1_Project/Assets/Scripts/StateMachineTest/ChaseDataSO.cs.meta → /UOP1_Project/Assets/Scripts/StateMachineTest/ChaseComponent.cs.meta

正在加载...
取消
保存