Adding a "are you sure you want to do this?" confirmation box to gui and scripts

Hi all,

I’m new to coding (I’m sure you’ve read that a few hundred times).

I’ve been working though a tutorial series on a popular video upload site.

and am up to the point where items in bag can now be deleted by clicking the x in the icons top right corner.

the tutorial does not cover adding a gui box which asks the user to confirm they want the item gone forever, I’ve tried almost 15 different videos, none of which cover what I want but I tried to make alterations to the code to achieve my goal, non worked, all it did was throw errors in different ways dependant on what I did.

I’ve created a parent object, this has the text i need and the child buttons for confirm or cancel, I just can’t seem to build it into the existing code.

using UnityEngine;
using UnityEngine.UI;

public class InventorySlot : MonoBehaviour
{
    public Image icon;
    public Button removeButton;

    Item item;

    public void AddItem (Item newItem)
    {
        item = newItem;

        icon.sprite = item.icon;
        icon.enabled = true;
        removeButton.interactable = true;
    }

    public void ClearSlot ()
    {
        item = null;

        icon.sprite = null;
        icon.enabled = false;
        removeButton.interactable = false;
    }

    public void OnRemoveButton ()
    {
        Inventory.instance.Remove(item);
      
    }

}

the OnRemoveButton is where I’d like the gui confirmation to trigger, before the item is removed, probably with an if statement? no matter what I do I can’t get it working.

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

public class Inventory : MonoBehaviour
{
    #region Singleton

    public static Inventory instance;
    private void Awake()
    {
        if (instance != null)
        {
            Debug.LogWarning("More than one instance of Inventory found!");
        }

        instance = this;
    }
    #endregion

    public delegate void OnItemChanged();
    public OnItemChanged onItemChangedCallback;

    //amount of bag slot
    public int space = 20;

    public List<Item> items = new List<Item>();

    public bool Add (Item item)
    {
        if (!item.isDefaultItem)
        {
            if (items.Count >= space)
            {
                Debug.Log("Not enough room in Bag");
                return false;
            }
            items.Add(item);

            if (onItemChangedCallback != null)
                onItemChangedCallback.Invoke();
        }
        return true;
    }

    public void Remove (Item item)
    {
        items.Remove(item);

        if (onItemChangedCallback != null)
            onItemChangedCallback.Invoke();
    }
}

It’s a challenge for myself really but I just can’t get it working, please could someone help?

thank you

I recommend making a prefab for the box you want to spawn for confirmation. Then, make a new script and add it to that prefab. That script will handle what happens when yes/no is clicked on the confirmation box. So, when you Instantiate a new instance of that prefab in “OnRemoveButton”, instead of immediately removing the item, you get the confirmation script and tell it what item to delete if the user clicks yes.

In unity to make something disappear or appear you call gameObject.SetActive(false or true). Reference your dialog from that script and activate it. Add black image to cover whole screen so player can’t click anything else unitil he closes your dialog.

So the real challenge here is that you have a function that needs to execute, but you have to wait for user input to do so, so that code can’t just be in the function where it’s called. There are two common options for executing delayed code, the second of which is better:

  1. Coroutines
    Have Remove() kick off a coroutine that opens the confirmation dialog, waits for a response, checks to see if that response is “yes”, and if so, does the thing. This is okay, but if you’re going to want more confirmation dialogs, you’ll have to write this whole rigamarole every time, and it’ll get tedious.

  2. Delegates
    You can actually make a fully reusable confirmation dialog pretty easily and cleanly using delegates and lambda functions. Your existing Remove() would only have to change this much:

   public void Remove (Item item)
    {
ConfirmationDialog.Show("Are you sure you want to remove this item?", () => {
        items.Remove(item);

        if (onItemChangedCallback != null)
            onItemChangedCallback.Invoke();
    }
}
}

And on the UI side, that’s it - you will be able to reuse that code for any other function you need a confirmation dialog for. So how do you implement it on the other side? Something like this:

public class ConfirmationDialog : MonoBehaviour {
public static void Show(string dialogMessage, System.Action actionOnConfirm) {
instance.storedActionOnConfirm = actionOnConfirm;
instance.dialogText.text = dialogMessage;
instance.gameObject.SetActive(true);
}
private System.Action storedActionOnConfirm;
private static ConfirmationDialog instance;
public UnityUI.Text dialogText;

void Awake() {
instance = this;
gameObject.SetActive(false);
}
public void OnConfirmButton() {
if (storedActionOnConfirm != null) {
storedActionOnConfirm();
storedActionOnConfirm = null;
gameObject.SetActive(false);
}
}
public void OnCancelButton() {
storedActionOnConfirm = null;
gameObject.SetActive(false);
}
}

(*written in a forum window, there’s bound to be a syntax error or two)
Attach this to your dialog GUI object, link up the buttons to the appropriate functions and the text object to the variable, make sure the object is active at start (so that Awake() runs), and you should be just about set. When you run Remove(), that will call Show(), which will find the singleton (“instance”) and cache the function you sent as the second parameter. When the user presses the confirm button, it’ll execute that cached function.

You could also add a third parameter with another inline function with what to do if the user presses “cancel”.

1 Like

I have a function that takes the text(title, body, accept/cancel) and a callback to perform, i stuff it in a premade UI that is always in the scene(inactive unless needed) ans is setup to have a title, a message body, the cancel button simply clears it and disables it, the accept button calls the callback(and clears/disables it)

Thank you all very much for your replies, I’ve had a good read of all of them, I am currently looking at StarManta’s as he did a lot of hard work by scripting it all out.

Star - what do you mean by

public UnityUI.Text dialogText;

unit throws the error of

Assets\Scripts\ConfirmationDialog.cs(16,12): error CS0246: The type or namespace name ‘UnityUI’ could not be found (are you missing a using directive or an assembly reference?)

i even added the UnityEngine.UI; header just in case. apologies if I’m being a noob.

also i changed

ConfirmationDialog.Show("Are you sure you want to remove this item?", () => {

into
ConfirmationDialog.Show("Are you sure you want to remove this item?");

thank you

Yeah, that should have just been “public Text dialogText;” with your standard using UnityEngine.UI in the file. As I said, typing into a forum window, typos are are gonna happen :slight_smile:

The second part of that is the part that makes it work. As you must have it written now, it’s going to pop up the confirmation dialog but it will immediately delete the thing, and the confirmation dialog is meaningless.

That format of code is called a lambda function. It’s basically hybrid between a function and a variable - rather, a function that gets passed around as a variable. Lines 5 through 8 in the sample code block aren’t executed when Remove() is called (you don’t want them to be); rather, those lines get sort of bundled up and passed into the .Show() function like a variable.

As I’m describing this and rereading the code now, I just realized I goofed and forgot a bit of syntax. That code should have looked like this: (note the parenthesis and semicolon on line 9)

   public void Remove (Item item)
    {
ConfirmationDialog.Show("Are you sure you want to remove this item?", () => {
        items.Remove(item);

        if (onItemChangedCallback != null)
            onItemChangedCallback.Invoke();
    });
}
}

The Show() function call doesn’t actually end until line 9; everything within () => {} is the second parameter of the Show() function.

Let me show you an alternative way to write the same code; this accomplishes almost the same thing (one exception, described below) but it’s a little bit more cumbersome. It should make it a little easier to understand what’s going on.

   public void Remove (Item item)
    {
ConfirmationDialog.Show("Are you sure you want to remove this item?", RemoveCallback);
}
public void RemoveCallback() {
        items.Remove(item);
        if (onItemChangedCallback != null)
            onItemChangedCallback.Invoke();
    }

}

In this form, it’s easier to see that the second parameter is the function itself (in this case, named “RemoveCallback”). This function gets stored, and the button will later call back to it.

Now I said there’s one exception, and if you plug in that code, the compiler will tell you it doesn’t know what “item” is. That’s what makes the inline version (the first one) so handy: you can use the local variables from the Remove() function, even in a function that won’t get called until much later. (I don’t know the exact mechanism, but the compiler somehow bundles up all the necessary variables for the inline function and they sort of travel with it wherever it goes.) To actually do this with the explicitly named function, you’d have to store “item” somewhere.

1 Like

Boom! it works perfectly, you sir are amazing. thank you so much.

1 Like

Absolutely amazing answer… Exactly what I was looking for. Thank you so much, you have no idea how much you helped me! Thank you!