State patterns are very useful when organized. The theory behind is grouping every behavior in separated classes and making those classes loosely coupled to the main algorithm
all game object behaviors will be grouped into some classes and can be maintained by a main Finite State Machine Manager class
State patterns can be implemented with "Observation" systems
in our example, there is a player moving to the target point and we will have 3 different states for player
1. Idle state
2. Search State
3. Found state
In our example, there is a cube and a plain when you click anywhere on-screen player moves to this point and stops
when the game starts Player is at IdleState (there must be an initial state for FSM to work)
so SearchState when you clicked somewhere on-screen
and FoundState when cube reached finish position
To set up this example create an empty scene and add a plane and add a cube
and add an EmptyGameObject => Namethis TargetAssigner and add "TargetAssigner" component to EmptyGameObject
and Add "PlayerStateManager" to Cube Object
public interface IState
{
// start executing state
void Enter();
// update in state
void Tick();
//fixed Update in state
void FixedTick();
// exiting current sate
void Exit();
}
=> And Base StateMachine Class "StateMachineBase"
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class StateMachineBase : MonoBehaviour
{
public IState CurrentState { get; private set; }
public IState _previousState;
bool _inTransition = false;
public void ChangeState(IState newState)
{
if (CurrentState == newState || _inTransition)
return;
ChangeStateRoutine(newState);
}
public void RevertState()
{
if (_previousState != null)
ChangeState(_previousState);
}
void ChangeStateRoutine(IState newState)
{
_inTransition = true;
if (CurrentState != null)
CurrentState.Exit();
if (_previousState != null)
_previousState = CurrentState;
CurrentState = newState;
CurrentState.Enter();
_inTransition = false;
}
private void Update()
{
if (CurrentState != null && !_inTransition)
CurrentState.Tick();
}
private void FixedUpdate()
{
if (CurrentState != null && !_inTransition)
CurrentState.FixedTick();
}
}
*** Those two classes can be used in any project as the main construction for
FSM
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetAssigner : MonoBehaviour
{
// delegate for moving player towards point
public Action<Vector3> MoveCommand = delegate { };
#region Fields
Ray ray;
RaycastHit hit;
#endregion
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
MoveCommand.Invoke(hit.point);
}
}
}
private void OnDrawGizmos()
{
// to see target gizmo
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(this.hit.point, 1F);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerStateManager : StateMachineBase
{
#region Unity Fields
[SerializeField]
TargetAssigner target;
[SerializeField]
public float RotateSpeed;
[SerializeField]
public float MoveSpeed;
#endregion
#region Public States
public IdleState IdleState { get; private set; }
public SearchState SearchState { get; private set; }
public FoundState FoundState { get; private set; }
#endregion
public Vector3 TargetPosition { get; set; }
#region Fields
Material stateMaterial;
Rigidbody rbd;
#endregion
private void Awake()
{
rbd = this.GetComponent<Rigidbody>();
stateMaterial = this.GetComponent<MeshRenderer>().material;
IdleState = new IdleState(this, stateMaterial, target);
SearchState = new SearchState(this, stateMaterial, rbd);
FoundState = new FoundState(this,stateMaterial,target);
}
private void Start()
{
ChangeState(IdleState);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IdleState : IState
{
PlayerStateManager _playerSM;
TargetAssigner _targetAssigner;
Material _material;
Color _stateColor=Color.yellow;
public IdleState(PlayerStateManager playerSM, Material material,TargetAssigner targetAssigner)
{
this._playerSM = playerSM;
this._material = material;
this._targetAssigner = targetAssigner;
}
public void Enter()
{
Debug.Log("Idle State Enter");
_material.color = _stateColor;
_targetAssigner.MoveCommand += OnNewTargetAcuired;
}
public void Exit()
{
_targetAssigner.MoveCommand -= OnNewTargetAcuired;
}
public void FixedTick()
{
}
public void Tick()
{
//
Debug.Log("Idle State Tick");
}
void OnNewTargetAcuired(Vector3 newPosition)
{
_playerSM.TargetPosition = newPosition;
_playerSM.ChangeState(_playerSM.SearchState);
}
// Start is called before the first frame update
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SearchState : IState
{
PlayerStateManager _playerSM;
Rigidbody _rbd;
Material _material;
Color _stateColor = Color.red;
public SearchState(PlayerStateManager playerStateMan,Material material,Rigidbody rb)
{
_rbd = rb;
_material = material;
_playerSM = playerStateMan;
}
public void Enter()
{
Debug.Log($"STATE CHANE - Search");
_material.color = _stateColor;
}
public void Exit()
{
}
public void FixedTick()
{
float distanceFromtarget = Vector3.Distance(_playerSM.TargetPosition, _rbd.position);
if (distanceFromtarget < .8F)
{
_playerSM.ChangeState(_playerSM.FoundState);
}
else
{
RotateTowardsTarget();
MoveTowerdsTarget();
}
}
void RotateTowardsTarget()
{
Quaternion lookrotation = Quaternion.LookRotation(_playerSM.TargetPosition - _rbd.position);
lookrotation = Quaternion.Slerp(_rbd.rotation, lookrotation, _playerSM.RotateSpeed * Time.deltaTime);
_rbd.MoveRotation(lookrotation);
}
void MoveTowerdsTarget()
{
// Vector3 moveoffset = _playerSM.transform.forward * _playerSM.MoveSpeed;
_rbd.MovePosition(Vector3.Lerp(_playerSM.transform.position, _playerSM.TargetPosition,_playerSM.MoveSpeed));
}
public void Tick()
{
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FoundState : IState
{
PlayerStateManager _playerStateManager;
Color foundColor = Color.green;
Material _material;
TargetAssigner _targetassigned;
public FoundState(PlayerStateManager playerStateManager, Material material, TargetAssigner targetassigned)
{
this._playerStateManager = playerStateManager;
this._material = material;
this._targetassigned = targetassigned;
}
public void Enter()
{
Debug.Log("Player is at found state");
_material.color = foundColor;
_targetassigned.MoveCommand += NewMoveCommand;
}
public void Exit()
{
_targetassigned.MoveCommand -= NewMoveCommand;
}
private void NewMoveCommand(Vector3 obj)
{
_playerStateManager.TargetPosition = obj;
_playerStateManager.ChangeState(_playerStateManager.SearchState);
}
public void FixedTick()
{
}
public void Tick()
{
}
}
No comments:
Post a Comment