Home > front end >  Event Action Doesn't work in state Pattern
Event Action Doesn't work in state Pattern

Time:09-17

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:

  1. Try to post all scripts related to the question you are asking.
  2. 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

enter image description here

Execution Overview gif:

https://gfycat.com/grimflowerycollardlizard

  • Related