Forum rules - please read before posting.

Customizing the HotSpot Icons

edited March 2023 in Technical Q&A

Heya! I wanted to do something very specific but was struggling a little with customizing the UI.

My settings are --
Interaction Method: Choose Hotspot then interaction
Select interactions by "Clicking Menu"
See interactions with "Click on Hotspot"

For the Hotspot buttons I want to--

  1. Have a base graphic upon which the icon sits on. This way I can have a border around the icons I can update independently of the icon itself.

example: https://imgur.com/fKLEwr6

  1. Have the border graphic change display based on whether or not the player has seen the interaction or not. [for example: Gold flashing border around unseen interactions. Grey border around seen interactions or when no more interactions are left in the sequence] Important to show that a hotspot has a sequence of interactions to go through in order to see them all. This also needs to be saved in the save data.

example: https://imgur.com/ehoA3aR

  1. And last I wanted it to have animations on mouseover, and when clicked on.

Any advice on the best way to do this within this engine?

For the cursor I want to--

  1. Have the cursor change based on whether the interactable hotspot is a person or an environmental object. Right now it appears it can only be one thing.

Comments

  • Welcome to the community, @Jhano.

    Have a base graphic upon which the icon sits on. This way I can have a border around the icons I can update independently of the icon itself.

    If you set your Interaction menu's Source to Unity Ui Prefab, then you'll be able to decorate your Interaction element with an additional border Image in its Hierarchy. This mode will also be necessary for the other topics you mention.

    Using Unity UI with AC menus can be a little trickier that AC's built-in option, but it offers much more control when it comes to styling and custom behaviour. For details, see the Manual's "Unity UI menus" chapter, as well as this tutorial.

    The default Interaction menu will work with Unity UI, which you can restyle by opening up the linked InteractionUI canvas prefab.

    Have the border graphic change display based on whether or not the player has seen the interaction or not.

    This'll require some scripting.

    You'll first need a script, attached to a Hotspot, that records which "Use" buttons have been run by the player. This is possible by hooking into the OnHotspotInteract custom event. Custom events are a way of running custom code when AC performs common tasks - a tutorial on their usage can be found here.

    If you also have this script derive from AC's Remember class, you can have it store its button-usage record within save-game files. A tutorial on saving custom scene data can be found here.

    Here's a script (RememberHotspotChoices.cs) that you can attach to your Hotspots to perform the above:

    using System.Collections.Generic;
    using UnityEngine;
    using AC;
    
    public class RememberHotspotChoices : Remember
    {
    
        private List<int> runUseButtonIDs = new List<int> ();
    
        private void OnEnable () { EventManager.OnHotspotInteract += OnHotspotInteract; }
        private void OnDisable () { EventManager.OnHotspotInteract -= OnHotspotInteract; }
    
        private void OnHotspotInteract (Hotspot hotspot, AC.Button button)
        {
            if (hotspot.gameObject == gameObject && hotspot.GetButtonInteractionType (button) == HotspotInteractionType.Use)
            {
                if (!runUseButtonIDs.Contains (button.iconID))
                    runUseButtonIDs.Add (button.iconID);
            }
        }
    
        public bool HasInteractionBeenSeen (int useID)
        {
            return runUseButtonIDs.Contains (useID);
        }
    
        public override string SaveData ()
        {
            HotspotChoicesData data = new HotspotChoicesData();
            data.objectID = constantID;
            data.savePrevented = savePrevented;
    
            data.useIDs = runUseButtonIDs.ToArray ();
    
            return Serializer.SaveScriptData <HotspotChoicesData> (data);
        }
    
    
        public override void LoadData (string stringData)
        {
            HotspotChoicesData data = Serializer.LoadScriptData <HotspotChoicesData> (stringData);
            if (data == null) return;
            SavePrevented = data.savePrevented; if (savePrevented) return;
    
            runUseButtonIDs.Clear ();
            foreach (int useID in data.useIDs) runUseButtonIDs.Add (useID);
        }
    
    }
    
    [System.Serializable]
    public class HotspotChoicesData : RememberData
    {
    
        public int[] useIDs;
    
        public HotspotChoicesData () { }
    
    }
    

    The second script you'll need is one that is attached to your Interaction menu prefab, and updates the background graphic based on the first script's HasInteractionBeenSeen function. This too relies on a custom event - this time, OnMenuTurnOn, which will fire when a menu is turned on.

    This one's a bit more complicated, as you'll need to update it's Inspector with details of each Interaction button and its border Image component:

    using UnityEngine;
    using AC;
    
    public class DynamicInteractionBorders : MonoBehaviour
    {
    
        [SerializeField] private Canvas canvas = null;
        [SerializeField] private UseButton[] useButtons = new UseButton[0];
        [SerializeField] private Sprite normalSprite;
        [SerializeField] private Sprite seenSprite;
    
        private void OnEnable () { EventManager.OnMenuTurnOn += OnMenuTurnOn; }
        private void OnDisable () { EventManager.OnMenuTurnOn -= OnMenuTurnOn; }
    
        private void OnMenuTurnOn (AC.Menu menu, bool isInstant)
        {
            if (canvas && menu.RuntimeCanvas == canvas)
            {
                Hotspot hotspot = menu.TargetHotspot;
                RememberHotspotChoices hotspotChoices = hotspot ? hotspot.GetComponent<RememberHotspotChoices> () : null;
    
                foreach (var useButton in useButtons)
                {
                    bool isSeen = false;
    
                    if (hotspotChoices)
                    {
                        MenuInteraction interaction = (MenuInteraction) menu.GetElementWithGameObject (useButton.button.gameObject);
                        for (int i = 0; i < interaction.uiSlots.Length; i++)
                        {
                            if (useButton == interaction.uiSlots[i].uiButton)
                            {
                                int iconID = interaction.GetIconIDAtSlot (i);
                                if (hotspotChoices) isSeen = hotspotChoices.HasInteractionBeenSeen (iconID);
                                break;
                            }
                        }
                    }
    
                    useButton.border.sprite = isSeen ? seenSprite : normalSprite;
                }
            }
        }
    
        [System.Serializable]
        private class UseButton
        {
    
            public UnityEngine.UI.Button button;
            public UnityEngine.UI.Image border;
    
        }
    
    }
    

    And last I wanted it to have animations on mouseover, and when clicked on.

    Animated interaction icons can be handled in a couple of ways:

    1. If the icon, as defined in the Cursor Manager, has Animate? checked, you can specify how many frames the supplied texture has, and the icon will be used by the Menu automatically.
    2. When using Unity UI, you can set the UI Button component's Transition setting to Animation, which will then let you supply animations via a regular Unity Animator component for the Button's Normal, Selected etc states.

    Have the cursor change based on whether the interactable hotspot is a person or an environmental object. Right now it appears it can only be one thing

    When using Choose Hotspot Then Interaction mode, the Cursor Manager's "Hospot cursor" texture is fixed. However, it is possible to change this field - and any Manager field - at runtime through script. To do so, just right-click on the field's label to get an API reference to it.

    Here's a script that - when placed in the scene - will alter the texture based on whether or not the last-selected Hotspot has an NPC component or not:

    using UnityEngine;
    using AC;
    
    public class NPCCursor : Remember
    {
    
        [SerializeField] private Texture normalHotspotCursor = null;
        [SerializeField] private Texture npcHotspotCursor = null;
    
        private void OnEnable () { EventManager.OnHotspotSelect += OnHotspotSelect; }
        private void OnDisable () { EventManager.OnHotspotSelect -= OnHotspotSelect; }
    
        private void OnHotspotSelect (Hotspot hotspot)
        {
            bool isNPC = hotspot.GetComponent<NPC> ();
            AC.KickStarter.cursorManager.mouseOverIcon.ReplaceTexture (isNPC ? npcHotspotCursor : normalHotspotCursor);
        }
    
    }
    

    I appreciate there's a lot to go through here, so just let me know if you have any issues and I'll guide you through them further.

  • Thank you very much! You've really gone above and beyond in your answer here. I'll get on it and see how it goes. Fingers crossed.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.