Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Event Action Initialization?

Discussion in 'Scripting' started by JamieRoss95, May 8, 2019.

  1. JamieRoss95

    JamieRoss95

    Joined:
    Apr 5, 2016
    Posts:
    28
    I am creating a rogue-like dungeon crawler and I have recently implemented my GameManager as a singleton that takes advantage of the C# Event/Action system. I have run into a minor issue that I can live with but would love to use the situation as a learning opportunity for myself! One of the Events (PlayerMoveEvent) is originally triggered by the Player moving as this is how traps/enemies/etc. are notified that they should also be completing their turns but for the very first move of the game, the MoveEvent says it does not exist.

    This is obviously due to the order that scripts are being called/gameObjects are being created but as far as I can tell I have it set up where this should not be an issue. As a side note, my GameManager script execution is set to the very first thing that is created in the game (-500ms).

    The order of script execution is currently like this (some code not relevant to the issue has been removed fo easier readability):

    GAME MANAGER (1st)
    Code (CSharp):
    1.     private void Awake()
    2.     {
    3.         //SINGLETON ASSIGNMENT
    4.         if (Instance == null)
    5.         {
    6.             Instance = this;
    7.             DontDestroyOnLoad(gameObject);
    8.         }
    9.         else
    10.         {
    11.             Destroy(gameObject);
    12.         }
    13.  
    14.         _dg = GetComponent<DungeonGenerator>();
    15.     }
    16.  
    17.     void Start()
    18.     {
    19.         //GENERATE FIRST LEVEL
    20.         curLevel = 1;
    21.         _dg.ChooseNextRoomLayout();
    22.     }
    DUNGEON GENERATOR (2nd)
    Code (CSharp):
    1.     public void ChooseNextRoomLayout() //Chooses which room layout according to current level
    2.     {
    3.         _roomInfo = _curRoom.GetComponent<RoomInformation>(); //Sets room info to the current room
    4.         _curTiles = _roomInfo.roomTiles; //Sets the tile list to the current room tiles
    5.  
    6.         //SET THE CUR DUNGEON INFO FOR PLAYER
    7.         _playerController.curDungeonFirstPlayTile = _roomInfo.firstPlayTile;
    8.         _playerController.curDungeonLastPlayTile = _roomInfo.lastPlayTile;
    9.         _playerController.curDungeonStartTile = _roomInfo.startTile;
    10.         _playerController.curDungeonEndTile = _roomInfo.endTile;
    11.         _playerController.moveDirHorizontal = _roomInfo.startMoveDirHorizontal;
    12.  
    13.         _playerController.StartLevel();
    14.  
    15.         GenerateDungeon();
    16.     }
    PLAYER CONTROLLER (3rd)
    Code (CSharp):
    1.     public void StartLevel()
    2.     {
    3.         transform.position = curDungeonStartTile.transform.position;
    4.         playerTargetTile = curDungeonFirstPlayTile.transform.position;
    5.  
    6.         currentPos = transform.position;
    7.  
    8.         StartCoroutine(MoveToPosition(removed as it is not important));
    9.     }
    Code (CSharp):
    1.     public IEnumerator MoveToPosition(Vector3 targetTile, float timeToMove, int tilesToTarget)
    2.     {
    3.         float time = 0f; //Reset timer
    4.  
    5.         GameManager.Instance.PlayerMove();
    6.  
    7.         timeToMove *= tilesToTarget;
    8.  
    9.         while (time < 1)
    10.         {
    11.             time += Time.deltaTime / timeToMove; //How much time has passed relatively
    12.             transform.position = Vector3.Lerp(currentPos, targetTile, time); //Change to smoothDamp
    13.             yield return null;
    14.         }
    15. }
    GAME MANAGER (4th)
    Code (CSharp):
    1.     public void PlayerMove()
    2.     {
    3.         if (PlayerMoveEvent != null)
    4.         {
    5.             PlayerMoveEvent();
    6.         }
    7.         else
    8.         {
    9.             Debug.Log("PlayerMoveEvent is null");
    10.         }
    11.     }
    For the very first Player Movement, I get the "PlayerMoveEvent is null" debug message. It is not a huge issue by any means but in my mind, the execution order should work out so that the PlayerMoveEvent is not null. Would love to learn why it is so!

    Any info or thoughts are greatly appreciated, thanks!
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Can you share where you declare the event and where your other objects subscribe to it? When an event is null, depending on how it's declared, it really means that it has no listeners.
     
  3. JamieRoss95

    JamieRoss95

    Joined:
    Apr 5, 2016
    Posts:
    28
    Ohhh! That makes more sense now. I thought that the null event meant its Invoke() couldn't be called. The other objects are subscribed to the event during the GenerateDungeon() phase aka the end of step 2. So it seems that the listeners should be subscribed before the player's first movement but I could see it happening after.

    Can an Event/Action be originally declared within a function? I just have it declared at the top of the GameManager script like this:

    Code (CSharp):
    1.     //LEVEL
    2.     [HideInInspector] public event Action EndLevelEvent;
    3.     //TURNS
    4.     [HideInInspector] public event Action PlayerMoveEvent;
     
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    No, events can only be declared at the class/struct level as far as I know. It sounds like you have some sort of race condition, like something happening in Awake while something else happens in Start, or a similar scenario. From the code you've posted, and with your new info, it looks like the issue is here in ChooseNextRoomLayout:
    Code (csharp):
    1.  
    2.         _playerController.StartLevel();
    3.  
    4.         GenerateDungeon();
    5.  
    The player's StartLevel() method starts their MoveToPosition() coroutine, which fires the event, but you said that other objects don't start listening until GenerateDungeon() is called which happens after all that.
     
  5. JamieRoss95

    JamieRoss95

    Joined:
    Apr 5, 2016
    Posts:
    28
    That was exactly what was causing it!

    Thanks for the help, and for clearing up some questions I had involving Events.