Skip to content

Instantly share code, notes, and snippets.

@Ratstail91
Last active July 1, 2018 10:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ratstail91/d19e155b784985e94ff404dfffbf79af to your computer and use it in GitHub Desktop.
Save Ratstail91/d19e155b784985e94ff404dfffbf79af to your computer and use it in GitHub Desktop.
Reimplemented the corn monster using state machines; the goal is to have a usable state machine, not to redo the corn. This won't compile because of reasons I can't be assed to figure out.
using UnityEngine;
using States;
class Monster : MonoBehaviour {
//AI states
public UnityStateMachine<Monster> stateMachine;
//components
public Animator animator;
public Rigidbody2D rigidBody;
//movement
public Vector2 deltaForce;
public Vector2 lastDirection;
public float speed;
//behavior
public GameObject player;
public const float walkSpeed = 0.18f;
public const float runSpeed = 1.2f;
public float walkTimer = float.NegativeInfinity;
public void Awake() {
stateMachine = new StateMachine<Monster>(this, WanderState.Instance);
animator = GetComponent<Animator>();
rigidBody = GetComponent<Rigidbody2D>();
}
public void Start() {
//cache the player reference
player = GameObject.Find("player");
}
public override void Update() {
stateMachine.UpdateState();
SendAnimationInfo();
}
public void FixedUpdate() {
//HandleMove() was cargo code
rigidBody.velocity = Vector2.zero;
Vector2 impulse = deltaForce.normalized * speed;
rigidBody.AddForce (impulse, ForceMode2D.Impulse);
}
public override void OnCollisionEnter2D(Collision2D collision) {
stateMachine.OnCollisionEnter2D(collision);
}
public override void OnCollisionStay2D(Collision2D collision) {
stateMachine.OnCollisionStay2D(collision);
}
public override void OnTriggerEnter2D(Collider2D collider) {
stateMachine.OnTriggerEnter2D(collider);
}
public override void OnTriggerStay2D(Collider2D collider) {
stateMachine.OnTriggerStay2D(collider);
}
public void SendAnimationInfo() {
animator.SetFloat ("xSpeed", rigidBody.velocity.x);
animator.SetFloat ("ySpeed", rigidBody.velocity.y);
animator.SetFloat ("lastXSpeed", lastDirection.x);
animator.SetFloat ("lastYSpeed", lastDirection.y);
}
//utility, utilizing Coroutines
public void SwitchToStateAfter(UnityState<Monster> state, float delay) {
StartCoroutine(SwitchToStateAfterCoroutine(state, delay));
}
public IEnumerator SwitchToStateAfterCoroutine(UnityState<Monster> state, float delay) {
yield return new WaitForSeconds(delay);
stateMachine.ChangeState(state);
}
}
class WanderState : UnityState<Monster> {
//singleton
private static WanderState _instance = null;
public static WanderState Instance {
get {
if (_instance == null) {
_instance = new WanderState();
}
return _instance;
}
}
private WanderState() {
//EMPTY
}
//state machine
public override void Update(Monster _owner) {
if (Vector3.Distance(_owner.player.transform.position, _owner.transform.position) < 0.4f) {
//attack the player if they're too close
_owner.stateMachine.ChangeState(BuildupState.Instance);
} else {
//meander about
_owner.speed = Monster.walkSpeed;
if (Time.time - _owner.walkTimer > 2f) {
_owner.walkTimer = Time.time;
if (Random.Range (0, 2) == 0 || _owner.deltaForce == Vector2.zero) {//tend toward straight lines
_owner.deltaForce.x = Random.Range (-2, 3);
_owner.deltaForce.y = Random.Range (-2, 3);
_owner.deltaForce.Normalize ();
}
}
}
}
public override void OnTriggerEnter2D(Monster _owner, Collider2D collider) {
FireDamager fire = collider.gameObject.GetComponent<FireDamager> ();
IceDamager ice = collider.gameObject.GetComponent<IceDamager> ();
WindDamager wind = collider.gameObject.GetComponent<WindDamager> ();
if (fire != null || ice != null || wind != null) {
_owner.stateMachine.ChangeState(BuildupState.Instance);
return;
}
//HACK: fence
_owner.deltaForce = Rotate (_owner.deltaForce, 180);
}
//utilities
Vector3 Rotate(Vector3 normalisedDirection, float angle) {
return Quaternion.AngleAxis(angle, Vector3.forward) * normalisedDirection;
}
}
class BuildupState : UnityState<Monster> {
//singleton
private static BuildupState _instance = null;
public static BuildupState Instance {
get {
if (_instance == null) {
_instance = new BuildupState();
}
return _instance;
}
}
private BuildupState() {
//EMPTY
}
//state machine
public override void EnterState(Monster _owner) {
_owner.deltaForce.x = _owner.player.transform.position.x - _owner.transform.position.x;
_owner.deltaForce.y = _owner.player.transform.position.y - _owner.transform.position.y;
_owner.deltaForce.Normalize ();
_owner.speed = 0;
_owner.animator.SetTrigger ("charge");
_owner.walkTimer = float.NegativeInfinity;
_owner.SwitchToStateAfter(ChargingState.Instance, 0.6f);
}
}
class ChargingState : UnityState<Monster> {
//singleton
private static ChargingState _instance = null;
public static ChargingState Instance {
get {
if (_instance == null) {
_instance = new ChargingState();
}
return _instance;
}
}
private ChargingState() {
//EMPTY
}
//state machine
public override void OnCollisionStay2D(Monster _owner, Collision2D collision) {
_owner.deltaForce = Vector2.zero;
_owner.animator.SetTrigger ("crash");
_owner.SwitchToStateAfter(StunnedState.Instance, 0.6f);
}
}
class StunnedState : UnityState<Monster> {
//singleton
private static StunnedState _instance = null;
public static StunnedState Instance {
get {
if (_instance == null) {
_instance = new StunnedState();
}
return _instance;
}
}
private StunnedState() {
//EMPTY
}
//state machine
public override void Update(Monster _owner) {
if (Vector3.Distance (_owner.player.transform.position, _owner.transform.position) > 0.4f) {
_owner.stateMachine.ChangeState(WanderState.Instance);
}
}
}
using UnityEngine;
namespace States {
class UnityStateMachine<T> {
T owner;
UnityState<T> currentState = null;
public UnityStateMachine(T _owner, UnityState<T> _firstState) {
owner = _owner;
currentState = _firstState;
}
public void ChangeState(UnityState<T> newState) {
if (currentState != null) {
currentState.ExitState(owner);
}
if (newState != null) {
currentState = newState;
}
currentState.EnterState(owner);
}
//state interfaces
public void UpdateState() {
currentState.Update(owner);
}
public void OnCollisionEnter2D(Collision2D _collision) {
currentState.OnCollisionEnter2D(owner, _collision);
}
public void OnCollisionStay2D(Collision2D _collision) {
currentState.OnCollisionStay2D(owner, _collision);
}
public void OnTriggerEnter2D(Collider2D _collider) {
currentState.OnTriggerEnter2D(owner, _collider);
}
public void OnTriggerStay2D(Collider2D _collider) {
currentState.OnTriggerStay2D(owner, _collider);
}
}
class UnityState<T> {
public virtual void EnterState(T _owner) {}
public virtual void ExitState(T _owner) {}
public virtual void Update(T _owner) {}
public virtual void OnCollisionEnter2D(T _owner, Collision2D _collision) {}
public virtual void OnCollisionStay2D(T _owner, Collision2D _collision) {}
public virtual void OnTriggerEnter2D(T _owner, Collider2D _collision) {}
public virtual void OnTriggerStay2D(T _owner, Collider2D _collision) {}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment