[SOLVED] How to make a player have to destroy squares in a specific order?

I create a simple 2D ( C#) animation, which is a tutorial for me.

I have a PLAYER.

  1. My player moves with the mouse (he follows mouse).

  2. After colliding with a square, he destroys it.

I don’t have a problem with it, everything works ok.

However, I would like the player to destroy the squares in the right order(in right sequence): 1-2-3-4.

If he destroys them in the wrong order(wrong sequence) , for example 1-3 (first collid with number 1 and then number 3), animation end.

I know how to end animations when I touch an wrong thing, but I do not know how to make it in the right order (specific sequence).

How to make a player have to destroy squares in a specific order?

PS. Sorry for my English. If someone doesn’t understand something, I’ll try to write it in a simpler way :slight_smile:

3480086--276774--Bez tytułu.png

There is a collection called a “Queue”, it’s like a “List” but you can just ask for “the next one” rather than working with indexes etc.

Put references to the squares in such a collection and check if the one they collided with is the next one in the queue.

(Peek, DeQueue and EnQueue specifically)

1 Like

Thanks for quick answer, but it looks pretty hard for someone starting.

Do you have a link where it is explained in a simpler way?

Not sure which part was confusing or hard… so, in case this can help ya, you can paste this in at rextester to try it out.

public static void Main(string[] args)
{
   Queue<int> q = new Queue<int>();
   q.Enqueue(1);
   q.Enqueue(2);
   int n = q.Peek();
   print("peeked = " + n);
   n = q.Dequeue();
   print("dequeud = " + n);
   n = q.Peek();
   print("peek = " + n);
   n = q.Count;
   print("count = " + n);
}
static void print(string msg)
{
     Console.WriteLine(msg);
}
1 Like

I just do not know how it works, the first time I meet with something like this and I do not understand what it is used for :slight_smile:

Well, did you test the program to see?

As mentioned in another post, you could use an array/list of integers/game objects, along with an index to track which one should be next. Comparing those when a box is destroyed would tell you if the right one was done. If that makes more sense to you, just use that for now. :slight_smile:

1 Like

Yes, I checked the program and thank you very much.

My problem is that I have spent a few weeks with c #.

I’m a beginner. I can do things like collisions, movement, menu, start, ending, points, etc., but I can’t do something like that.

I wanted someone to explain what and how to do, on the example of a code with these simple four squares :slight_smile:

That’s why I created a simple situation with a collision and just simple four squares, because I think that based on this, it’ll be the easiest way to create a simple code and explain it :slight_smile:

Okay, well try creating a script and put it on an empty game object.
Then, add an integer variable and an array.
Fill the array in the inspector, with the 4 squares, placed in order.

Add a method to the script that is like: “public void GotDestroyed(GameObject go)”
In the script that the squares already have (or if they don’t , add one), create a public variable of the type of the script you made on the empty game object.

When the square enters collision/trigger, use the variable to the other script to say “otherScript.GotDestroyed(gameObject)”

// this script is on the empty game object; just one of these, and it keeps track of which game object is the right one to be destroyed next.
int index = 0;
[SerializeField]
GameObject [] gamePieces;

public void GotDestroyed(GameObject go) {
   if(gamePieces[index] == go) {
      // correct one was destroyed.
      ++index; // if index >= gamePieces.Length, it's game over.. all done.
     }
    else {
       // if we're here, the one destroyed was not the next in the list
    }
 }

I hope that kind of makes sense. If you try something like that, you can report back if it’s working or not for you. Hopefully I laid it out in understandable bits. :slight_smile:

1 Like

I still not understand this part with squares. How should the script look there?
Am I to refer ( at the moment of collision) to a script from an empty object?

But thanks for the answer, I still do not know what and how (step by step) to do, but I’m slowly beginning to understand something.

appeal to something

But thanks for the answer, I still do not know what and how (step by step) to do, but I’m slowly beginning to understand something.

At this point, I think the most sage advice that I can offer is that you take some time to go through these in order: https://unity3d.com/learn/beginner-tutorials
Let yourself learn, and practice - the knowledge and experience will help you going forward.

I hope that helps, and good luck in your journey. :slight_smile:

However, to answer your question :
The script on the squares have a reference to the script on the empty game object. In their collision/trigger method, they call the method mentioned before.

2 Likes

Ok, I need one more help :slight_smile:

I have completely changed the script, because the list, unfortunately, I do not understand.

But now I do not know how to make the game end after collision with bad square.

Anyone help?

The script attached to the squares:

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

public class Collision : MonoBehaviour {


        public int myIndexNumber;

        public void Start()
        {
            Debug.Log("Object number " + myIndexNumber + " is here");
        }
}

A script attached to an object that has a collision with squares:

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

public class List : MonoBehaviour {

    public GameObject gameOverText;
    public GameObject restartButton;

    int index =1;

    void Start (){
        gameOverText.SetActive (false);
        restartButton.SetActive (false);
    }

    public void OnCollisionEnter2D (Collision2D col)
    {
        Debug.Log("Player hit object: " + col.transform.name);
        //check if the object you hit is one of the "collisionObjects":
        if(col.transform.GetComponent<Collision>())
        {
            //check if we hit the right object: if your index is the same as the collisionObjects indexnumber
            if(index == col.transform.GetComponent<Collision>().myIndexNumber)
            {
                //destroy the object we hit:
                Destroy(col.gameObject);
                //increment your current index:
                index++;
            }
        }
    }
}

Usually, I’m doing this by collision with an object with a specific tag.

However, in this case it must be only in wrong order collid.

Just a note: it would probably be good to not name your class ‘Collision’ or ‘List’ as those are class names in Unity/C#.

Okay, so what does the end of the game entail? The route you took is valid, too. It can work when you track the index that way. What do you want to happen?
The game over text and restart button should become active and maybe the player game object is de-activated, or maybe just the script that moves it is disabled?

1 Like

The idea is that after touching the wrong square (not in order) the word gameOver has appeared or the game just has been restarted.

The problem is that I always added GAMEOVER and RESTART after colliding with an object with a specific tag.

In this case, however, the game must restart or must appear GAMEOVER after touching the wrong square :slight_smile: :slight_smile:

I tried to add something like that, but GAMEOVER appears only after touching the first square:

    int index =1;
    public GameObject gameOverText, restartButton ;


    void Start () {
        gameOverText.SetActive (false);
        restartButton.SetActive (false);
    }



    public void OnCollisionEnter2D (Collision2D col)
    {
        Debug.Log("Player hit object: " + col.transform.name);
        //check if the object you hit is one of the "collisionObjects":
        if(col.transform.GetComponent<collisionObject>())
        {
            //check if we hit the right object: if your index is the same as the collisionObjects indexnumber
            if (index == col.transform.GetComponent<collisionObject> ().myIndexNumber) {
                //destroy the object we hit:
                Destroy (col.gameObject);

                //increment your current index:
                index++;


                if (index > col.transform.GetComponent<collisionObject> ().myIndexNumber) {

                    gameOverText.SetActive (true);
                    restartButton.SetActive (true);
                    gameObject.SetActive (false);

          
            }  
                      
                      
          
        }
    }
}
}

You almost have it. Try this:

collisionObject colObj = col.transform.GetComponent<collisionObject>();
if(!colObj) return;
if(index == colObj.myIndexNumber) {
   Destroy(col.gameObject);
   index++;
   }
  // here, we know it didn't match the number. :)
else {
   gameOverText.SetActive (true);
   restartButton.SetActive (true);
   gameObject.SetActive (false);
}
1 Like

Ok, I added the script, but now GameOver also appears after hitting the first square.

https://gfycat.com/pl/gifs/detail/WhimsicalAccomplishedDogwoodtwigborer

Strange. Just check a couple of things.

  1. forgot to mention that you need to reset the index when the game is over. It must go back to 0 or 1 (whichever you use to indicate the first) when the player resets/dies.
  2. just make sure the numbers are correct on the squares you’re trying to destroy + the player’s index is the first one.

If it doesn’t work after you test that, can you just make a unity package and put it in this thread? I could look at it for ya.

1 Like

Ok, thanks for your help. Already without adding anything, it works in the form you wrote earlier. I do not know why, but it started working as I changed the sprite.

Anyway, forgive me for the problems and thanks for professional help and explanation :slight_smile:

So I’ve been getting into the habit of creating more modular reusable tools so that my artist/designer can set things like this up in scene.

And this can be done without a queue or any really complicated code. But instead follow a basic logic chain.

Here are my scripts:

A simple OnTriggerEnter2D script that signals a UnityEvent. I allow for a tag mask, and 2 states based on if the ‘SignalShouldPass’ boolean toggle.

using UnityEngine;
using UnityEngine.Events;

public class t_OnTriggerEnter2D : MonoBehaviour {

   
    public string TagMask;
    public bool SignalShouldPass;
    public UnityEvent OnEnterPass;
    public UnityEvent OnEnterFail;

    public void SetIfSignalPass(bool value)
    {
        this.SignalShouldPass = value;
    }
   
    void OnTriggerEnter2D(Collider2D collision)
    {
        if (!this.enabled) return;
        if (!string.IsNullOrEmpty(TagMask) && !collision.gameObject.CompareTag(TagMask)) return;

        if (this.SignalShouldPass)
            this.OnEnterPass.Invoke();
        else
            this.OnEnterFail.Invoke();
    }

}

Then I have the result of the sequence. What should happen if the player succeeds or fails:

using UnityEngine;

public class SequenceCompleteResult : MonoBehaviour {
   
    public void OnSequenceFail()
    {
        Debug.Log("PLAYER LOST!");

        //here we can reset the puzzle
    }

    public void OnSequenceSucceeded()
    {
        Debug.Log("PLAYER SUCCEEDED!!");

        //here we can do what we do on success
    }

}

I don’t really do much, that’s up for you.

And lastly I created a very simple motion script. Mine just moves some object to the mouse position. You can have whatever motion script you want:

using UnityEngine;

public class MotionScript : MonoBehaviour {



    private void Update()
    {
        var plane = new Plane(Vector3.forward, Vector3.zero);
        var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        float dist;
        plane.Raycast(ray, out dist);
        this.transform.position = ray.GetPoint(dist);
    }

}

Then a scene. I start with a player sphere and 2 cubes that have to be hovered order in the order bottom first, then top second (note adding more entries is mostly trivial):


Then on the ‘First’ I attached my t_OnEnterTrigger2D script:

It’s configured to singal as pass because it’s the first entry, it should pass by default.
In it’s on pass event I target the ‘Second’ cube in the sequence and set it’s ‘SignalShouldPass’ to true.

Then on Second:

Here it’s set to fail by default. This way if you entered it before entering ‘First’ it’ll play the ‘Fail’ event. But if ‘First’ was already entered, it’ll be toggled, and run its ‘Pass’ event.

Then in the pass and fail we target the ‘Result’ object. In ‘Pass’ I call ‘OnSequenceSucceeded’ and in ‘Fail’ I call ‘OnSequenceFailed’.

Done.

If we wanted to insert a third entry into the sequence well we just set up the 3rd the same way 2nd is, and we make 2nd toggle the thirds ‘SignalPass’ boolean:

1 Like

Hey, no problem. Glad you got it working. Enjoy your game. :slight_smile:

1 Like

Thank you for the comprehensive reply (with pictures!). My English is not perfect, but you’ve explained everything in a very simple way. Later, I will create a new file in the Unity and I’ll practice. Well explained, so I probably won’t have a problem with understanding. Once again: Thank you very much.