I'm using state pattern for my unity game. I have three states; HappyState, SurprisedState and SadState. HappyState is default state. I want the character to jump and enter surprised state by clicking left-mouse. Entering surprised state, a few jobs need to be done which I defined as a void to be subscribed to an event, But it doesn't work!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class CapsuleScript : MonoBehaviour
{
public event Action SliderAction;
public Slider slider;
public Text stateText;
public GameObject hands;
public Rigidbody rb;
public CapsuleBasicStates currentstate;
public readonly HappyState happyState = new HappyState();
public readonly SadState sadState = new SadState();
public readonly SurprisedState surprisedState = new SurprisedState();
public SpriteRenderer renderer;
public Sprite happysprite, sadsprite, surprisedsprite;
// Start is called before the first frame update
void Start()
{
slider.value = 0;
renderer = GetComponentInChildren<SpriteRenderer>();
rb = GetComponent<Rigidbody>();
TransitionToState(happyState);
}
public void OnCollisionEnter(Collision other) {
currentstate.OnCollisionEnter(this);
}
// Update is called once per frame
void Update()
{
currentstate.Update(this);
}
public void SetSprite(Sprite sprite)
{
renderer.sprite = sprite;
}
public void TransitionToState(CapsuleBasicStates state)
{
currentstate = state;
currentstate.EnterState(this);
}
public IEnumerator SliderHandler()
{
yield return new WaitForSeconds(1f);
slider.value =1;
StartCoroutine(SliderHandler());
}
public void IEHandler()
{
StartCoroutine(SliderHandler());
}
}
Here is SurprisedState script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SurprisedState : CapsuleBasicStates
{
public override void EnterState(CapsuleScript player)
{
player.SetSprite(player.surprisedsprite);
player.stateText.text = player.surprisedState.ToString();
player.slider.gameObject.SetActive(true);
player.SliderAction = player.IEHandler;
}
public override void OnCollisionEnter(CapsuleScript player)
{
//player.TransitionToState(player.happyState);
}
public override void Update(CapsuleScript player)
{
if(player.slider.value ==20)
{
player.SliderAction -= player.IEHandler;
player.TransitionToState(player.happyState);
}
}
}
HappyState script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HappyState : CapsuleBasicStates
{
public override void EnterState(CapsuleScript player)
{
player.SetSprite(player.happysprite);
player.stateText.text = player.happyState.ToString();
player.hands.SetActive(false);
}
public override void OnCollisionEnter(CapsuleScript player)
{
}
public override void Update(CapsuleScript player)
{
if(Input.GetButton("Fire1"))
{
player.rb.AddForce(Vector3.up * 150f);
player.TransitionToState(player.surprisedState);
}
if(Input.GetButton("Fire2"))
{
player.rb.AddForce(Vector3.up *250f);
player.TransitionToState(player.sadState);
}
}
}
CodePudding user response:
Few points before I post my solution:
- Try to post all scripts related to the question you are asking.
- Comment your code or try to explain what each part of your script is doing.
BaseSate.cs
[System.Serializable]
public abstract class BaseState
{
public enum PlayerStates { HappyState = 0, SurprisedState = 1 }
public abstract PlayerStates PlayerState { get; }
public abstract void EnterState(PlayerScript playerScript);
public abstract void UpdateState();
public abstract void ExitState();
}
HappySate.cs
using UnityEngine;
[System.Serializable]
public class HappyState : BaseState
{
public override PlayerStates PlayerState => PlayerStates.HappyState;
private PlayerScript _playerScript;
public override void EnterState(PlayerScript playerScript)
{
_playerScript = playerScript;
Debug.Log($"Entered {PlayerState}");
}
public override void UpdateState()
{
if (Input.GetButtonDown("Fire1"))
{
_playerScript.ChangeState(new SurprisedState());
}
}
public override void ExitState()
{
Debug.Log($"Exited {PlayerState}");
}
}
SurprisedState.cs
using UnityEngine;
[System.Serializable]
public class SurprisedState : BaseState
{
public override PlayerStates PlayerState => PlayerStates.SurprisedState;
private PlayerScript _playerScript;
public override void EnterState(PlayerScript playerScript)
{
_playerScript = playerScript;
// subscribe to sliderAction on enter
playerScript.playerSliderAction = OnSliderChange;
Debug.Log($"Entered {PlayerState}");
}
private void OnSliderChange(float sliderValue)
{
// use Mathf.Approximately instead of == when comparing
// floating numbers.
if (Mathf.Approximately(sliderValue, 20f))
{
_playerScript.ChangeState(new HappyState());
}
}
public override void UpdateState() { }
public override void ExitState()
{
// unSubscribe to sliderAction on exit
_playerScript.playerSliderAction -= OnSliderChange;
Debug.Log($"Exited {PlayerState}");
}
}
PlayerScript.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class PlayerScript : MonoBehaviour
{
[SerializeField]
private SpriteRenderer playerRenderer;
[SerializeField]
private Sprite[] playerStateSprites;
[SerializeField]
private Slider playerSlider;
public UnityAction<float> playerSliderAction;
private BaseState _currentState;
private void Awake()
{
Initialize();
}
private void Update()
{
_currentState.UpdateState();
}
private void Initialize()
{
ChangeState(new HappyState());
// use slider onValueChanged instead of checking slider
// value every frame.
// invoke unity action when the value has been changed.
playerSlider.onValueChanged.AddListener(sliderValue =>
{
playerSliderAction?.Invoke(sliderValue);
});
}
public void ChangeState(BaseState newState)
{
// change state only when it is different
// from previous state.
if (_currentState == newState)
{
return;
}
_currentState?.ExitState();
_currentState = newState;
_currentState.EnterState(this);
playerRenderer.sprite = playerStateSprites[(int)newState.PlayerState];
Debug.Log($"Current State : {_currentState.PlayerState}");
}
}
PlayerScript inspector view
Execution Overview gif: