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
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 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 Sky is not your limit, your imagination is!