0

I'm prototyping a board game which has 20 "blocks" where an enemy character jumps one block at a time (from 1-20) when spacebar is pressed. If the enemy makes it to block 20 - it's the winner.

Each block has different obstacles and the enemy can get frozen, burned, trapped, melted, teleported, etc. Some blocks can have multiple of those obstacles.

What I'm trying to do is create a separate class for each of those obstacles and add them to different blocks using .AddComponent<>. When an enemy lands on a block, the block goes through the obstacle(s) added on that block and punishes the enemy accordingly.

This is the block prefab class:

public class CompositeBlock : MonoBehaviour{
    void Start(){
        gameObject.AddComponent<ActionFreeze>();
        gameObject.AddComponent<ActionTrap>();
    }

    // This is called when enemy steps on the block
    public void ExecuteActions(Enemy enemy){
        Debug.Log("Enemy has landed on this block");
        IBlockAction[] actionArray = GetComponents<IBlockAction>();
        foreach(IBlockAction action in actionArray){
            action.Execute(enemy);
        }
    }
}

Here are the classes that can perform an "Action" on the ememy. Right now let's say blocks can only freeze or trap an enemy:

public interface IBlockAction{
    void Execute(Enemy enemy);
    void OnActionComplete();
}

public class ActionFreeze : MonoBehaviour, IBlockAction{
    public void Execute(Enemy enemy){
        Debug.Log("Freezing Enemy");
        enemy.Freeze(); // Change enemy's sprite to frozen
        Invoke("OnActionComplete", 2f); // Wait for 2 seconds
    }

    public void OnActionComplete(){
        Debug.Log("Freeze completed");
    }
}

public class ActionTrap : MonoBehaviour, IBlockAction{
    public void Execute(Enemy enemy){
        Debug.Log("Trapping Enemy");
        enemy.Trap(); // Change enemy's sprite to trapped
        Invoke("OnActionComplete", 3f); // Wait for 3 seconds
    }

    public void OnActionComplete(){
        Debug.Log("Trap completed");
    }
}

This works as expected but since ExecuteActions has a for loop, all the actions are executed at once. The enemy's "Frozen" sprite will not be visible at all because the next action is performed immediately. Here's how the log looks:

Enemy has landed on this block
Freezing Enemy
Trapping Enemy
Freeze completed
Trap completed

How can the next action's .Execute() be called only after the current action's OnActionComplete() is called so that the enemy's "Frozen" sprite is shown for 2 seconds, then the enemy's sprite is changed to "Trapped" which is then shown for 3 seconds. The log should look like this:

Enemy has landed on this block
Freezing Enemy
Freeze completed
Trapping Enemy
Trap completed

The duration (frozen or trapped) sprite is shown is not important, as instead of a sprite change, an animation could be played. No matter how the enemy (frozen or trapped) state is displayed - OnActionComplete() should be called after it's completion.

Yeti
  • 5,628
  • 9
  • 45
  • 71
  • @Fattie `CompositeBlock` will eventually be a prefab - I'm certainly not gonna copy/paste each of the 20 blocks without making them a prefab first :D. I have updated the question so that it's more clear. – Yeti Oct 23 '20 at 13:16
  • @Fattie I think this suites unity pretty well. This way I can create classes like `ActionMelt`, `ActionTeleport`, `ActionKill`, `ActionShrink` etc and just add them to different block prefabs by doing `gameObject.AddComponent()` etc. And those action(s) will be performed automatically on the Enemy when it lands on that block! – Yeti Oct 23 '20 at 13:29
  • @Fattie I think what happens inside "freezeEnemy" is irrelevant as the log would output the same sequence of logs even if that method wasn't called. – Yeti Oct 23 '20 at 13:33
  • ahh! I think i finally understand what ypiur'e asking @yeti ! it's easy in unity .. – Fattie Oct 23 '20 at 13:35
  • do note that your `enemy.Trap()` would almost certainly be a coroutine, in almost any Unity project. anything that takes time, is a coroutine! BTW you may enjoy using **Tweeng** which is a simple extension. it largely eliminates almost all simple animations (audio, position, whatever) https://stackoverflow.com/a/37228628/294884 – Fattie Oct 23 '20 at 13:57
  • @Fattie I will try adding coroutine and check out Tweeng. Thanks! I have edited the question with the details of the gameplay! – Yeti Oct 23 '20 at 14:23
  • hope it helps in some way. perhaps leave out Tweeng until you "convert" everything to coroutines anyway! it's always sort of annoying that you have to have coroutines from "top to bottom" you know? enjoy – Fattie Oct 23 '20 at 14:24

1 Answers1

2

You just wait for coroutines like this ..

Do them all at the same time:

StartCoroutine(A());
StartCoroutine(B());
StartCoroutine(C());
StartCoroutine(D());

Do them one by one, sequentially:

yield return StartCoroutine(A());
yield return StartCoroutine(B());
yield return StartCoroutine(C());
yield return StartCoroutine(D());

Here's a full example to paste in

using UnityEngine;
using System.Collections;
public class Examp : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(Test());
    }
    IEnumerator Test()
    {
        yield return StartCoroutine(A());
        yield return StartCoroutine(B());
        yield return StartCoroutine(C());
    }

    IEnumerator A()
    {
        yield return new WaitForSeconds(1f); Debug.Log("..A");
    }
    IEnumerator B()
    {
        yield return new WaitForSeconds(1f); Debug.Log("..B");
    }
    IEnumerator C()
    {
        yield return new WaitForSeconds(1f); Debug.Log("..C");
    }
}
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • I have updated the question, so that it's more clear. I do use coroutines, but I'm not sure how to make use of it in this situation. – Yeti Oct 23 '20 at 13:14
  • it's fortunately easy , I've put in the answer @yeti ! cheers – Fattie Oct 23 '20 at 13:46
  • @Yeti , notice my call `Test()` Sometimes folks bother to make a Stack (or whatever feature you like in C#) of items to be called sequentially. Really I find in most games it's better to just explicitly write it out in a routine, such as `TakeOffSequence` or `WinSequence` or whatever. Almost all games have many calls that look like "Test" to express a sequence of things that happens! Cheers – Fattie Oct 23 '20 at 14:09