Script to highlight a selected object

So I currently have a script that highlights different portions of a building’s model as you mouse over them. I’ve done this by making 2 versions of the model - one with normal textures and one with glowing textures, and activating them and deactivating them on mouse over with the following script:

public GameObject current; //non highlighted object
public GameObject replace; //highlighted object

void OnMouseEnter()
{
	current.SetActive (false);
	replace.SetActive (true);
}

void OnMouseExit ()
{
	replace.SetActive (false);
	current.SetActive (true);
}

As another aspect of the program, clicking the highlighted portion of the model brings up a panel with details about that area. I would like to make it so that upon clicking, the highlighted portion remains highlighted until either you click another area, or you click the button that closes the information panel.

this is the script to bring up the information panels:

public GameObject infoPanel;

void OnMouseUp()
{
	infoPanel.SetActive(true);
}

and then this is the script I use for the button that closes the panels

public GameObject infoPanel; //gameobject for info panel

public void ToggleButton()
{
	infoPanel.SetActive (false);
}

How can I edit these scripts so that areas are highlighted on mouse over, but remain highlighted when their info panel is open and become unhighlighted when the info panel’s close button is clicked? (i have a script that makes it so only the most recent info panel is open)

Hey @TempestInATeacup

EDIT: Referencing GameObjects/Components

NOTE! This is NOT an exhaustive list. For more information on Accessing and Referencing GameObjects see this. The following is just another way of explaining the same concept with different examples.

You asked the following:

So I’ll have the isPanelUp variable in
my script that handles highlighting,
but because the button scripts are
separate, I can’t use them to set the
variable to true or false. What can I
do to fix this? I’m new to coding,
sorry.

No need to be sorry! here’s the answer.

There are couple ways to reference the following:

  • GameObjects (e.g. Player, Button, etc… those you see in the hierarchy)
  • And/or specific components that belong to GameObjects (e.g. your button script, player movement script, Transform component, Rigidbody component, etc…)

By reference, I mean having the GO/Component in your script as a temporary variable /permanent field(class variable) so you could access it whenever needed.

Generally speaking, once you referenced a GameObject, you have access to any component on it.

Alright now let’s look at some different ways of referencing, alongside their respective rules and sample code:

1. Referencing GameObjects/Components from Inspector:

Description

  • Field’s (class variable’s) access visibility must be public.

  • Must be of type GameObject if you want to reference the entire GO. Example from the image: GameObject1 which value is AwesomeGO.

  • Must be of the type of the component you want to reference – if you don’t need to reference the entire GameObject but rather a specific component it has. Example from the image: Script 2 which value is of type MySecondScript and is a component on AwesomeGO.

  • Drag and drop the desired GameObject to their corresponding location in the inspector as in the image below.

Image

51682-ua-ref-ex.png

Code

Simply enough, this is the code of the script attached to the GameObject highlighted in the image:

using UnityEngine;
using System.Collections;

public class NewScript : MonoBehaviour
{
    // Fields also known as Member Variables or Data Members
    public GameObject gameObject1;
    public MySecondScript script2;
    public Light light;
    public Transform myTransform;

    // Other logic e.g. Methods also known as Member Functions or Member Behaviors
    // For example:

    // Called even before void Start()
    void Awake()
    {
    }

    // Use this for initialization
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
    }
}

2. Referencing GameObjects/Components using GameObject’s methods

Description

Code

using UnityEngine;
using System.Collections;

public class NewScript : MonoBehaviour
{
    // Fields for referencing GameObjects
    private GameObject myPanel;
    // Default access visibility is private, so you don't have to type it
    GameObject myPlayer;
    GameObject[] enemies;
    GameObject myPlayerLeftLeg;

    /// Fields for referencing Components
    /// it is better to cache them to consequently minimize number of times to call GetComponent()
    PlayerInventory playerInventory;

    /// It's a better practice to cache strings (preferrably with const for more performance)
    /// For two reasons: 
    ///  1. You won't have to go over every mention of the string when you want to change something in it
    ///  2. Avoid making typos (if you made a typo, a compiler error will show up e.g. if you typoed playerTah instead of playerTag)
    const string playerTag = "Player", enemyTag = "Enemy";

    ///  By default, fields (member variables) are initialized to their defaults
    ///  --> Value types:
    ///    -- Are initalized to their default (e.g. for "int": "Default(int)" which equals to zero)
    ///    -- Int/Float/Double... is initialized to ZERO
    ///    -- bool is initialized to false
    ///    -- Vector3 is initalized to Vector3.zero 
    ///  --> Reference types:
    ///    -- Are initalized to null;
    ///    -- string/GameObject/Transform etc... are all initalized to null
    // SO if we don't initialize powerUpDamage to 50 then the value will be 0 (unless initialized in Start() or somewhere else)
    int powerUpDamage = 50;

    // Use this for initialization
    void Start()
    {
        // Referencing a GameObject
        myPlayer = GameObject.FindWithTag(playerTag);

        // Referencing a Component
        playerInventory = myPlayer.GetComponent<PlayerInventory>();


        /// Note:
        /// This method (and other expensive methods like "FindObjectOfType" which is VERY SLOW) should be used in "void Start()" or "void Awake()" 
        /// And then cached in some permanent member variable
        /// As what we're donig here:

        myPanel = GameObject.Find("MyAwesomePanel_Name_In_Hierarchy");

        // Traverse hierarchy like a tree
        myPlayerLeftLeg = GameObject.Find("Player/Body/Legs/LeftLeg");

    }

    // Update is called once per frame
    void Update()
    {
        /// It is better to cache the results in "void Start()" 
        /// But what if you had runtime-generated enemies that change frequently?
        /// In that case then we might need to use this in "void Update()":

        enemies = GameObject.FindGameObjectsWithTag(enemyTag);

        // In our game, the player has a power up to hit all enemies at the same time! (when pressing F key)
        // isPowerUpAvailable is a bool flag
        if (Input.GetKeyDown(KeyCode.F) && playerInventory.isPowerUpAvailable)
        {
            playerInventory.isPowerUpAvailable = false;
            foreach (GameObject en in enemies)
            {
                // Getting "Enemy" script/component on each enemy, and calling "takeHit()" method
                en.GetComponent<Enemy>().takeHit(powerUpDamage);

                /// Alternatively if you wanna control the enemy HP directly 
                /// i.e. Not via a member function or a getter(property) which is not recommended
                /// then you can do something like this:
                en.GetComponent<Enemy>().hp -= powerUpDamage;
                // this is the same as saying: 
                //en.GetComponent<Enemy>().hp = en.GetComponent<Enemy>().hp - powerUpDamage;

                ///NOTE:
                /// it is RECOMMENDED to temporarily cache components if used more than once e.g.: 
                var enemy = en.GetComponent<Enemy>();
                // same as saying:
                // Enemy enemy = en.GetComponent<Enemy>();
            }
        }

    }

3. Referencing GameObjects in hierarchy using Transform’s methods

Description

  • Every GameObject has a transform field attached which can be accessed anywhere in the code.

  • This is useful for accessing nested GameObjects (parent, child, grandparent, grandchild)

  • Check out the corresponding script reference for more details. (very useful)

Code

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour
{
    GameObject myGrandparent;
    Transform playerRightLeg;
    Weapon myWeapon;

    const string playerRightLegName = "RightLeg";
    const string weaponName = "WeaponGO";

    void Start()
    {
        myGrandparent = transform.parent.parent.gameObject;

        // finds child by string name
        playerRightLeg = transform.Find(playerRightLegName);

        myWeapon = transform.Find(weaponName).GetComponent<Weapon>();

    }

    void Update()
    {
        Debug.Log("My weapon's damage is= " + myWeapon.WeaponPower.ToString());
    }
}

public class Weapon : MonoBehaviour
{
    public bool isOnRampage;
    private float weaponPower;
    private const float maxWeaponPower = 100f;
    private const float rampageDamageMultiplier = 2.5f;

    /// This is a property (getter/setter)
    /// used for keeping the field private
    /// yet being able to access it
    /// and apply operations when accessing it

    public float WeaponPower
    {
        get
        {
            if(isOnRampage)
                return weaponPower * rampageDamageMultiplier;
            return weaponPower;
        }
        set
        {

            if (weaponPower > maxWeaponPower)
            {
                weaponPower = maxWeaponPower;
            }
            else
            {
                weaponPower = value;
            }
        }
    }
}

WOW that was a long edit xD have fun :slight_smile: read my last line in the original answer ^^

4. Using static fields:

Description

  • This is a pretty cool and easy method if you have only ONE instance of what you to access/reference (e.g. the Panel in your example or player)

Code

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class MyButton : MonoBehaviour
{
    public static int numberOfButtons;

    void Start()
    {
        // Geting Unity's Button script and adding an event handlers/Listeners/ methods to execute when button is clicked
        gameObject.GetComponent<Button>().onClick.AddListener(() => { switchHighlightedRegion(); });

    }

    void switchHighlightedRegion()
    {
        // do logic for switching  highlighted region

        if (PanelHighlighter.isPanelUp)
        {
        }

    }
}

public class PanelHighlighter : MonoBehaviour
{
    public static bool isPanelUp;
    Text textField;

    void Start()
    {
        textField = GetComponent<Text>();
    }

    void Update()
    {
        if(gameObject.activeInHierarchy)
        textField.text = MyButton.numberOfButtons.ToString();
    }

    static public void ShowPanel()
    {
        // Logic for pulling up the panel

        isPanelUp = true;
    }

    public void ClosePanel()
    {
        // Logic for closing the panel

        isPanelUp = false;
    }
}

Original Answer:


First Solution

You could simply use a flag (bool field) in your class which correlates to the panel being up. This in turn brings the following modifications to your code snippets (respectively):

public GameObject current; //non highlighted object
public GameObject replace; //highlighted object
public bool isPanelUp; // by default it is 0 since it is a field/class member.

void OnMouseEnter()
{
    // if panel is NOT up then go ahead and switch sprites!
    if(!isPanelUp)
    {
        current.SetActive (false);
        replace.SetActive (true);
    }
}

void OnMouseExit ()
{
    // if panel is NOT up then go ahead and switch sprites!
    if(!isPanelUp)
    {
        replace.SetActive (false);
        current.SetActive (true);
    }
}

Next, we have to modify the script that brings up the Panel:

void OnMouseUp()
{
    infoPanel.SetActive(true);
    isPanelUp = true; // Panel is up now
}

Finally, we have to also modify the script of the button that closes the panels:

public GameObject infoPanel; //gameobject for info panel

public void ToggleButton()
{
    infoPanel.SetActive (false);
    isPanelUp = false; // Panel is not active, it closed thus it is not up anymore.
}

Second Solution

There is an alternative solution; you could also make use of the “active” state of your infoPanel (since in this case, your setting it to active or disabling it).

i.e. remove isPanelUp definition and replace each mention of isPanelUp with infoPanel.activeInHierarchy

Remark

When going for the first solution (isPanelUp bool flag) then if you have only one active panel in your game, then you could make the isPanelUp field
static so you could access it using the class name (script name) without having to reference the
script in your button script (the script of the button that closes the panel)

i.e. you could have this:

static public bool isPanelUp;

and you can access it as the following:

myAwesomeClass.isPanelUp = false;

Your question and code are neat and organized, keep up the good work :slight_smile: Sky is not your limit, your imagination is!