0

So I am working on a game which has both single Player as well as multiplayer functionality in it. On one hand I have a bird script which should function differently on the basis as to whether it is a single player game or a mutli-player game. But I simply can't understand why my event listener is not working as such.

So for the Single Player game I have a script called GameController attached to a gameObject called GameController. In the Start function of it I am firing an event saying that it is a single player. Below is the relevant code:

public class GameController : MonoBehaviour
{

public static Action isSinglePlayer = delegate { };

 void Start()
    {
        isSinglePlayer?.Invoke();
    }
}

So in the Bird Pigeon class I have the listener and the relevant code is like this :

public class BirdPigeon : MonoBehaviour
{
    public void Awake()
    {
        PV = GetComponent<PhotonView>();
        BattleRandomController.BirdDirectionTransfer+=BirdDirectionMultiPlayer;
        GameController.isSinglePlayer += SinglePlayer;
        BattleRandomController.isMultiPlayer+=MultiPlayer;        
    }

 void Start()
    {     
       if (isMultiPlayerGame)
        {
            if (PV.IsMine)
                IndicatorForMe.SetActive(true);
            else
                IndicatorForMe.SetActive(false);
        }

     }

  public void SinglePlayer()
    {
        Debug.Log("isSinglePlayer function is executed................");
        isMultiPlayerGame = false;
        IndicatorForMe.SetActive(false);
    }
private void MultiPlayer()
    {
        Debug.Log("MultiPlayer function is executed");
        isMultiPlayerGame = true;
        IndicatorForMe.SetActive(true);
    }
}

But when I run the code the Debug.log statement "isSinglePlayer function is executed................" doesn't get executed at all.

Similarly for the multiplayer game scene I have a gameObject called Battle Random controller attached to game object of the same name and in the Start function of that I am firing an event to indicate it is a multiplayer and still the above listener for that which is Multiplayer, the debug.log statement doesnt get executed only i.e.,"MultiPlayer function is executed"

The Relevant code for the multiplayer scene is as follows:

public class BattleRandomController : MonoBehaviour
{
 public static Action isMultiPlayer=delegate{};
 void Start()
    {
       isMultiPlayer?.Invoke();
     }
}

Earlier I had fired the event in the Awake and was listening in the Start of the bird pigeon class which somewhere I read was wrong. Now there is a listener ready in the awake function of the BirdPigeon class but I can't understand as to why the listener is not functioning .

chethanv77777
  • 480
  • 9
  • 18
  • 1
    You're assiging your `Action` to an empty `delegate {}` which is what get's executed. If you want it to be `BirdPidgeon.SinglePlayer()` then you need A) an instance of `BirdPidgeon` and B) assing the `SinglePlayer` method of the instance to the `Action` property but that would be an awful code. – Pablo Recalde Jun 07 '22 at 08:56
  • @PabloRecalde would you mind explaining that with a code or be a little more elaborate on what you told – chethanv77777 Jun 07 '22 at 09:06
  • What I mean is that you're not liking those two pieces of code anyhow. – Pablo Recalde Jun 07 '22 at 09:07
  • `public static Action isMultiPlayer=delegate{}` this is an empty anonymous method, so no matter how many times you call it, it will do nothing. `delegate {}` is an empty anonymous method. – Pablo Recalde Jun 07 '22 at 09:08
  • @PabloRecalde I have tried this same signature for my other events such as Game Over and getting Playfab friends and it has worked – chethanv77777 Jun 07 '22 at 09:38
  • in such use case why going through an event at all? You could simply have a `GameController` in every scene and have it have a simple `public bool isMultiplayer;` you can set for each scene via the inpector and checks its value using e.g. `FindObjectOfType().isMultiplayer` .... – derHugo Jun 07 '22 at 12:27

2 Answers2

1

The correct way to declare an event would be like

public event EventHandler MyEvent;

and raise it like

MyEvent?.Invoke(this, EventArgs.Empty)

You could use an arbitrary delegate in place of EventHandler or EventHandler<T>, but the most code I have seen uses EventHandler for events.

Note that you are using a static event, and there are several issues with such events. It may make thread safety more difficult, and can easily create memory leaks if you are not very through with unsubscribing. The singleton pattern might be an improvement, but I would recommend such singletons to only be global within some "context", and not for the entire application, that can make it easier to cleanly dispose of that context.

There is also the issue of timing, if you subscribe after raising the event you will obviously not get it. Easiest way to confirm this is by debugging. Add a breakpoint where you raise the event, and check that it is not null, that should also allow you to see if any breakpoints inside your event handler is hit.

Also, using events to determine the type of game seem fragile to me. You should ideally have some abstraction layer to isolate any multiplayer specific parts.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • I mean even my way of using Action , which is nothing but an event which is void , is nothing wrong. Even yours is correct, no doubt about that. For some other places such as GameOver, passing Playfab friend , coillisions it works fine with Actions , but here I can't understand whats the issue all about. – chethanv77777 Jun 07 '22 at 09:42
  • @chethanv77777 could be that you raise the event before subscribing to it. I would recommend some debugging to see what is going on. – JonasH Jun 07 '22 at 09:49
  • @chethanv77777 also, there are some rules with regards to multicast delegates. I don't have them memorized, but I know using the `event` keyword works fine. I would not use your syntax without reading the language specification to confirm that it does what you intend it to do. – JonasH Jun 07 '22 at 09:51
  • _"you are using a static event...and can easily create memory leaks"_ - its a problem regardless of instance vs static but anything that is a CLR host can easily destroy `static`s, something that Unity does anyway. Using `EventHandler`s and straight-up `delegate`s can lead to memory leaks because of their **heavy-weight references** as explained [here](https://stackoverflow.com/a/4526840/585968) and [here](https://stackoverflow.com/questions/2227762/can-delegates-cause-memory-leaks) respectively so they're generally a hot potato. They're not recommended for use in gaming for this reason. –  Jun 07 '22 at 11:36
  • _"....you are using a static event...It may make thread safety more difficult..."_ - pretty sure that applies to anything that doesn't have any form of thread-safety in the form of locking around it. So I don't know if you know Unity but Unity `MonoBehaviour`s run in Unity's **Main Thread** - think the main GUI thread in desktop apps such as WinForms/WPF. _"There is also the issue of timing"_ - as above, N/A in this discussion. When it could be a problem as in Unity's built-in async I/O, Unity does a good job of performing _thread marshalling_. –  Jun 07 '22 at 11:38
  • @MickyD I'm not familiar with Unity, but I agree with your points. I'm not sure what you mean by "destroy statics", any statics should have the same lifetime as the app domain. – JonasH Jun 07 '22 at 12:01
  • _"any statics should have the same lifetime as the app domain"_ - correct and Unity is a native app that hosts the CLR (much like SQL Server and IIS). [Whenever a new scene is loaded (or when pressing Play in the Editor), Unity performs a GC, managed threads are terminated, destroys the Primary App Domain (and any statics contained within) and creates a new Primary App Domain](https://docs.unity3d.com/Manual/ConfigurableEnterPlayModeDetails.html) along with other bits and bobs. –  Jun 07 '22 at 12:21
0

maybe you should try to replace your action by this :

public class GameController : MonoBehaviour
{

public event Action isSinglePlayer;

 void Start()
    {
        isSinglePlayer?.Invoke();
    }
}
Simon
  • 69
  • 6