- Home /
Assessing variables from other scripts using an accessor seems inelegant. Please explain.
As near as I can tell based on this reference... Referencing non static variables from another script? C# ... the following code below is how I have to access variables ( members public or private ) from one script via another script.
Notice that I make another class inside my EnemyControl script to access the health from EnemyControl, even though health is public. Also I have to set my accessor method "updateHealth" to static to resolve a compiler error (An object reference is required to access non-static member).
My question is, is this the correct way to do this? It seems a little cludgy/hacky.
Here's my calling code...
// detect collisions
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Enemy")
{
// add to score
HudManager.addToScore(10);
// modify enemy health
Accessor enemyScript = collision.gameObject.GetComponent<Accessor>();
Accessor.updateHealth(-1, collision.gameObject);
// kill off bullet
Destroy (this.gameObject);
}
}
Here's the accessor code.
using UnityEngine;
using System.Collections;
public class EnemyControl : MonoBehaviour {
public int health = 100;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
public class Accessor : MonoBehaviour {
// update health
public static void updateHealth( int val, GameObject which )
{
which.GetComponent<EnemyControl>().health -= val;
Debug.Log ("Enemy takes a hit");
}
}
As an aside, HudManager.addToScore() was done using static members and declaring the method static as well. I was okay with this because HudManager is a singleton but I need EnemyControl to work on instances of the enemies as they spawn. Here's the HudManager code anyway... ( for the sake of search engines this code shows how to fire projectiles at the enemy and detect collisions with the enemy ) I'm also using NGUI for the HUDs.
using UnityEngine;
using System.Collections;
public class HudManager : MonoBehaviour {
public static int ammo = 256;
public float bulletSpeed = 11;
public static int score = 1;
public GameObject projectile;
private static UIBaseLabel ammoText;
private static UIBaseLabel scoreText;
// Use this for initialization
void Start () {
// initial update UI
ammoText = GameObject.Find ("AmmoLabel").GetComponent<UIBaseLabel>();
ammoText.text = "Ammo:" + ammo;
// initial update UI
scoreText = GameObject.Find ("ScoreLabel").GetComponent<UIBaseLabel>();
scoreText.text = "Score:" + score;
}
// Update is called once per frame
void Update () {
// check for firing
if (Input.GetMouseButtonDown(0))
{
// reduce ammo counter
if ( ammo > 0)
{
ammo -= 1;
// update ammo counter
ammoText.text = "Ammo:" + ammo;
// Instantiate the projectile at the position and rotation of this transform
GameObject gunNozzle = GameObject.Find("Nozzle");
GameObject clone = Instantiate(projectile, gunNozzle.transform.position, gunNozzle.transform.rotation) as GameObject;
// Add force to the cloned object in the object's forward direction
clone.gameObject.AddComponent<Rigidbody>();
clone.rigidbody.mass = 0.1f;
clone.rigidbody.useGravity = false;
clone.rigidbody.AddForce(clone.transform.forward * bulletSpeed);
// attach script
clone.gameObject.AddComponent("projectileController");
}
}
}
public static void addToScore(int val)
{
// increment score by amount
score += val;
// update UI
scoreText.text = "Score:" + score;
}
}
Answer by robertbu · Mar 25, 2013 at 04:09 PM
Why the "Accessor" class? Why not just put UpdateHealth() inside your EnemyControl class? UpdateHealth would not be static inside of the EnemyControlClass, and it would be tied to the instance of the class (and therefore the game object). Then you could do something like:
EnemyControl enemyScript = collision.gameObject.GetComponent<EnemyControl>();
if (enemyScript != null)
enemyScript.updateHealth(-1);
Answer by PsychoPsam · Mar 26, 2013 at 01:01 AM
Thanks for you help. Aww - man, now I understand. When I did this yesterday as you explained it gave me the error "An object reference is required to access non-static member".
It was because I called it explicitly... EnemyControl.updateHealth(-1); ... which is not allowed (not sure why).
So theoretically the HudManager should work the same. Now I get it :D
This the object reference that is needed to access a non-static member...
HudManager hudScript = GameObject.Find("Player").GetComponent(); because HudManager is sitting on Player.
So then I can call... hudScript.addToScore(10);
All sorted and now I understand - except, why do they have to be referenced and not be able to be called explicitly?
Answer by PsychoPsam · Mar 26, 2013 at 01:03 AM
Actually I think I understand now. All scripts tend to be put on an instance of an object, so you need to reference the component attached to that object to get the actual instance of the script. The only way for something to be accessed directly, ie HudManager.addToScore is to make it singleton and make all the method static.
A singleton and static variables are not the same thing. I'm out of town with only an iPad, so it is difficult for me to look up some reference for you. Google "Unity3d singleton" and you will find a number of hits to direct you. Also you can have both class methods and instance methods in a class. Take a look at the reference page for Quaternion for example of a class with both. As long as a method does not access instance data, it can be static and called by using the class rather than an instance reference.
Your answer
Follow this Question
Related Questions
C# Going Static or Going OOP 3 Answers
How to use non static field in static member? 1 Answer
Script Errors with Unity 3.0 0 Answers