Monday, 1 November 2021

Unity Implementing Observer Pattern

Observer pattern is very useful when you need some event trigered actions in your game

i mean by that when you reach some point  or archived a bonus  some other game objects  might need  this information  you will have to inform your UI or some other enemies ect.


Using it is very simple

first of all  our example is a very simple scene  we have tree cubes Red,Blue and Gold Cubes

when player reachs Blue cube score increases on red cube score dicreases

And gold cube gets this information and appiers on screen

when player reaches gold cube  100 more score adds to player  and player control willbe disabled

its a basic example for focusing on pattern;

Since we have 3 different  behaved cubes we will have three different Scripts;

=> Red Cube Event onCollisionentered Invokes redCubeReached to subscribers

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RedCubeEvent : MonoBehaviour
{
    public static event Action redCubeReached;

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Player")  // assume that you only looking for player
            redCubeReached?.Invoke();
    }
}

=> Blue Cube Event OnCollisionentered Invokes cubeReached to all subscribers

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BlueCubeEvent : MonoBehaviour
{
    public static event Action cubeReached;

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Player")  // assume that you only looking for player
        {
            Debug.Log($"cube has been breached", this.gameObject);
            cubeReached?.Invoke();
        }
    }
}

=> Gold Cube Event

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GoldCubeEvent : MonoBehaviour
{

    public static event Action goldUnlocked;

    bool isRedCubeOk;
    bool isBlueCubeOk;
    MeshRenderer mesh;
    BoxCollider collider;
    private void Start()
    {
        mesh = this.GetComponent&ltMeshRenderer&gt();
        collider = this.GetComponent&ltBoxCollider&gt();
        BlueCubeEvent.cubeReached += CubeEvent_cubeReached;
        RedCubeEvent.redCubeReached += RedCubeAdditionalEvent_redCubeReached;
        mesh.enabled = false;
        collider.enabled = false;


    }

    private void RedCubeAdditionalEvent_redCubeReached()
    {
        isRedCubeOk = true;
        CheckStatsAndFireGoldEvent();
    }

    private void CubeEvent_cubeReached()
    {
        isBlueCubeOk = true;
        CheckStatsAndFireGoldEvent();
    }

    private void CheckStatsAndFireGoldEvent()
    {
        if (isRedCubeOk && isBlueCubeOk) // i can fire my gold event
        {
            collider.enabled = true;
            mesh.enabled = true;
            
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Player")
        {
            
            goldUnlocked?.Invoke();
        }
    }

}


=> Player Controller (simple for example) 
Player controller is listening for goldUnlocked event and Subscribing it by GoldCube_goldUnlocked method  its like  reaction to an event 

       
       using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    float moveSpeed;

    bool isGameFinished;
    void Start()
    {
        isGameFinished = false;
        GoldCubeEvent.goldUnlocked += GoldCube_goldUnlocked;
    }

    private void GoldCube_goldUnlocked()
    {

        isGameFinished = true;
        Debug.Log("Player You have Reach the end of level Controls disabled now" );
        
    }

    /// 
    /// Very basic  movement system
    /// 
    void Update()
    {
        if (isGameFinished)
            return;
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 p = this.transform.position;
        p.x += h * moveSpeed;
        p.z += v * moveSpeed;
        this.transform.position = p;
    }
}

       
=> ScoreCounter 
Score Controller observes three different events with AddScore,SubstractScore,GoldEvent
as you see Score changes based on action

     using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ScoreCounter : MonoBehaviour
{
    [SerializeField]
    Text scoreText;
    int currentScore = 0;
    private void OnEnable()
    {
        // setting up observetions
        // subscriped methods
        BlueCubeEvent.cubeReached += AddScore;
        RedCubeEvent.redCubeReached += SubstractScore;
        GoldCubeEvent.goldUnlocked += GoldEvent;
    }

    private void GoldEvent()
    {
        currentScore += 100;
        Debug.Log($"This function is fired from gold Cube");
        scoreText.text = $"Score:{currentScore}";
    }

    private void SubstractScore()
    {
        Debug.Log($"This function is fired by RedcubeReached Event  The RedCube");
        currentScore--;
        currentScore = Mathf.Clamp(currentScore, 0, int.MaxValue);
        scoreText.text = $"Score:{currentScore}";


    }

    private void AddScore()
    {

        Debug.Log($"This function is fired by cubeReached Event The BlueCube ");
        currentScore++;
        scoreText.text = $"Score:{currentScore}";   
    }
    private void OnDisable()
    {
        // unsubscribe on disable  to prevent errors
        BlueCubeEvent.cubeReached -= AddScore;
        RedCubeEvent.redCubeReached -= SubstractScore;
    }
}

Observerpattern can be used in  many different games  by chaning events and combinig wiht different
scenarios  many problems may be solved easly
i hope it helps everyone 
Happy coding :)


     

No comments:

Post a Comment