- Home /
C#: Create a variable placeholder for an unknown script
I've seen this question asked, and the answer seems to be that it can't be done...but it's such a common thing to do in other languages I'm surprised. So, maybe I'm misunderstanding something.
I'm creating a molecular simulation game. It will have lots of atoms with basically all the same functions, but needing different parameters.
It seems the best way to do this (and avoid necessary code duplications) is to write a general "nucleus.cs" script with the action methods, which access a "characteristics.cs" script to determine these parameters.
But there seems no way to attach different (unknown or differently named) scripts to the variable because of C#'s restrictive casting.
I.e.... the plan:
(This script goes on ALL atoms)
public class NucleusScript : MonoBehaviour {
// External object references
public IdontKnowWhatToPutHere characteristics;
public void reactWithHydrogen(){
if (distance < characteristics.electronegativity){
// make boom and stuff
}
}//end class
(OxygenCharacteristic.cs)
public class OxygenCharacteristics : MonoBehaviour {
float ELECTRONEGATIVITY = 3.44;
public float electronegativity {
get {return ELECTRONEGATIVIY;}
SET {throw new error you can't do this yadda};
}
}//class
(NitrogenCharacteristic.cs)
public class NitrogenCharacteristic : MonoBehaviour {
float ELECTRONEGATIVITY = 3.04;
public float electronegativity {
get {return ELECTRONEGATIVIY;}
SET {throw new error you can't do this yadda};
}
)//class
Then, in unity editor, the nucleus script is dragged to ALL the different atom objects (I.e. Oxygen, Nitrogen, etc)...with this "characteristics" variable placeholder.
THEN, also in unity editor, the script "OxygenCharacteristic.cs" is dragged onto the "characteristics" placeholder within the nucleus script associated with Oxygen.
AND the "NitrogenCharacteristic.cs" script is dragged onto the "characteristics" spot for the nucleus script attached to the nitrogen object.
I hope this makes sense. It's late.
I'm not locked into this solution. If there's another way to accomplish this, please suggest it.
Thanks!
Try to create class with your namespace and use it in not genereal scripts. Also, u can get script from another object like this
public class NucleusScript : $$anonymous$$onoBehaviour {
// External object references
public GameObject urObj; // attach obj with script here
public nameOfScript urScr; // placeholder
public void Start (){
urScr = urObj.GetComponent(nameOfScript); // get component from attached object at start
}
public void reactWithHydrogen(){
if (distance < urScr.electronegativity){
// make boom and stuff
}
}
Hi Chariot, and thanks for the response. I thought of this, but the bug is that I can't create 5 different scripts all named "characteristics.cs" (unity wont allow it).
Which means this is no longer generic, but specific to one molecule...meaning I'm basically doing the same thing as if I re-wrote the NucleusScript 10 times.
Or am I missing something?
See your example updated below to understand what I'm saying...
(for oxygen)
public class NucleusScript : $$anonymous$$onoBehaviour {
// External object references
public OxygenObj GameObject; // attach obj with script here
// I put the characteristics for Oxygen in "OxygenCharacteristicsScript.cs"
// Unity will allow only one "OxygenCharacteristicsScript.cs"
// The cast here for "OxygenCharacteristicsScript.cs" $$anonymous$$UST BE specific to "OxygenCharacteristicsScript", as I understand it
public OxygenCharacteristicsScript characteristics;
public void Start (){
characteristics = OxygenObj.GetComponent(OxygenCharacteristicsScript); // get component from attached object at start
}
public void reactWithHydrogen(){
if (distance < characteristics.electronegativity){
// make boom and stuff
}
}
}
So, the above works for Oxygen, but since I have to use a different scriptname (such as "NitrogenCharacteristicsScript.cs") for nitrogen, and I have to change the line...
"public OxygenCharacteristicsScript characteristics;" to "public NitrogenCharacteristicsScript characteristics;"...the NucleusScript is no longer generic and I'll just have to re-write the Nucleus script for every atom. That's what I'm trying to avoid.
Or am I totally missing something?
Thanks!
I'm not sure that I understand the question, on the face of it the answer seems to be polymorphism, but that's the case in other languages too. So my simple answer may be missing the point..?
In Unity I'd probably end up with the bare bones looking something like this...
public abstract class Characteristics <TSpecificCharacteristcs>: $$anonymous$$onobehaviour
{
abstract protected float electronegativity ();
}
public class OxygenCharacteristics : Characteristics<OxygenCharacteristics>
{
float ELECTRONEGATIVITY = 3.44;
public override float electronegativity() { return ELECTRONEGATIVITY; }
}//class
And then in the Nucleus class you can have a Characteristics field which could be any of your derived types
public class NucleusScript : $$anonymous$$onoBehaviour {
// External object references
public Characteristics characteristics;
public void reactWithHydrogen(){
if (distance < characteristics.electronegativity){
// make boom and stuff
}
}//end class
Having said all that, it's not actually clear to me why you want your Characteristics class to be a $$anonymous$$onoBehaviour, it looks like something that might be better just as a plain old data type. $$anonymous$$aybe a ScriptableObject.
U need to create universal script, and in inspector create characteristics (also u can do it in prefab inspector, to instantiates with it).
For example:
public class UniversalCharacteristic : $$anonymous$$onoBehaviour {
public float ELECTRONEGATIVITY;
public float electronegativity {
get {return ELECTRONEGATIVIY;}
SET {throw new error you can't do this yadda};
}
}
Second script:
public class NucleusScript : $$anonymous$$onoBehaviour {
// External object references
public gameObj GameObject; // attach obj with script here
public UniversalCharacteristicsScript characteristics;
public void Start (){
characteristics = OxygenObj.GetComponent(UniversalCharacteristicsScript); // get component from attached object at start
}
public void reactWithHydrogen(){
if (distance < characteristics.electronegativity){
// make boom and stuff
}
}
}
In inspector set for objects:
OOOh. I already posted an answer about using inheritance, but I love your UniversalCharacteristics solution. I think I may need to go back and use it ins$$anonymous$$d! Cheers!
Answer by tanoshimi · Feb 16, 2015 at 09:26 AM
"I'm creating a molecular simulation game. It will have lots of atoms with basically all the same functions, but needing different parameters."
From the example code you've given, it seems more like you have atoms with the same properties, but with different values. As such, can you not simply have a single Nucleus script, but set the appropriate ELECTRONEGATIVITY etc. properties via the inspector and save as a prefab for each nucleus type?
Either that, or it sounds like you might want subclasses of nucleus that inherit from the generic base class. e.g.
NucleusScript.cs - use this to define the generic properties that each nuclues will have, but don't actually attach this script to an object.
using UnityEngine;
using System.Collections;
public class NucleusScript : MonoBehaviour {
protected float ELECTRONEGATIVITY;
// Define methods that can be overriden with specific implementations in derived classes
protected virtual void Start () {
}
protected virtual void Update () {
}
}
NitrogenCharacteristics.cs - attach this to a nitrogen atom to override the generic base class methods with specific functionality for the nitrogen class.
using UnityEngine;
using System.Collections;
public class NitrogenCharacteristics : NucleusScript {
// Override the Start() method defined in the base class
protected override void Start () {
ELECTRONEGATIVITY = 3.04f;
}
}
OxygenCharacteristics.cs - attach this to an oxygen atom to override the generic base class methods with specific functionality for the oxygen class.
using UnityEngine;
using System.Collections;
public class OxygenCharacteristics : NucleusScript {
// Override the Start() method defined in the base class
protected override void Start () {
ELECTRONEGATIVITY = 3.44f;
}
}
Answer by Kiwasi · Feb 16, 2015 at 08:34 AM
Are you just asking for an interface? Forgive me if I have any syntax errors
public interface IHasElectronegativity {
float electronegativity {get;}
}
public class NucleusScript : Monobehaviour {
public IHasElectronegativity characteristics;
}
public class Oxygen : MonoBehaviour, IHasElectronegativity {
public float electronegativity {
get {return 0;}
}
}
Note that interfaces can't be serialised, but there are tricks to get around this.
General inheritance may work as well. For example did you consider
public class OxygenNucleus : GenericNucleus {}
Answer by BurningKrome · Feb 16, 2015 at 12:44 PM
So, I decided on using good ol' fashioned class inheritance. It's strange to say it felt Klugy (and cumbersome, and difficult) even though some would argue that (programmatically) it's more proper. It just seems like such a builder friendly interface as Unity would have a better (easier) solution.
Maybe they can add this...?
I'm still open to better ways to do this...
---SOLUTION---
(InheritED class, AtomCharacteristics.cs)
using UnityEngine;
using System.Collections;
namespace AtomCharacteristics {
public class Characteristics : MonoBehaviour {
private float ELECTRONEGATIVIY;
public string testvar = "This is a test variable accessed directly (not through getter).";
// Constructor called when
// "Characteristics characteristics = this.gameObject.AddComponent<Characteristics>();"
// is used
public Characteristics() {
// Set vars and do stuff here if needed
}// Characteristics constructor
// Must be initialized since MonoBehaviour doesn't allow passing variables to constructors
public void Init(string atomType) {
if (atomType.IndexOf("Oxygen") > -1) {ELECTRONEGATIVIY = 3.44F;} // should prolly use another class constructor to set all this
if (atomType.IndexOf("Nitrogen") > -1){ELECTRONEGATIVIY = 3.04F;} // should prolly use another class constructor to set all this
}// Init
// Getter/Setter for electronegativity
public float electronegativity {
get { return ELECTRONEGATIVIY; }
set { ELECTRONEGATIVIY = value; }
}//electronegativity
}//class Characteristics
}// namespace AtomCharacteristics
(InheritING class, NucleusScript.cs)
using UnityEngine;
using System.Collections;
using AtomCharacteristics; // My constructor class
public class NucleusScript : Characteristics {
void Start() {// Use this for initialization ------------------------------------------
Characteristics characteristics = this.gameObject.AddComponent<Characteristics>();
characteristics.Init("Oxygen");
Debug.Log("(testCharacteristics)Electronegativity is " + characteristics.electronegativity);
Debug.Log("(testCharacteristics)testvar is " + characteristics.testvar);
}//Start
void Update() { // Update is called once per frame
}//Update
public void reactWithHydrogen(){
if (distance < characteristics.electronegativity){
// make boom and stuff
}// reactWithHydrogen
////// other fun and useful method //////
}//end class
That doesn't look at all right. Why is AtomCharacteristics a $$anonymous$$onoBehaviour when it uses no features of $$anonymous$$onoBehaviour (and does have a constructor)??
Well...part of the issue is Im new to C# (mostly I work in Python).
$$anonymous$$y assumption is that, since the child script which inherits from Atomcharachteristics will also contain methods to run objects, that I would need $$anonymous$$onoBehavior somwhere in the inheritence chain.
It does indeed FUNCTION in the expected manner...?
hariot also made the suggestion to use a Universal script with variables in the inspector...which I think is a better answer.
I really think you'll be glad later on if you take your time over this design, or it'll come back to bite you.
Seems to me that these are OO design issues as much as specifically C#/Unity ones. Generally speaking, one doesn't want to put the specialisation in the base class. So if you want to use inheritance in your main classes it would make more sense to do it the other way round, with Nucleus as your base class (derived from $$anonymous$$onoBehaviour) and then subclasses for each of the elements which add specialisation. This would model the real world better.
For example, in the real world, an atom's nucleus can change. By making the characteristics of a nucleus object deter$$anonymous$$ed by its base class, you'll make things like that difficult if you want to do them later on. I'd want the characteristics to be a property (field) of a nucleus... much more like what you were doing to start with (but with the various elements' characteristics subclasses of a simple, non-$$anonymous$$onoBehaviour Characteristics class, or maybe a ScriptableObject).
I agree with you about the Nucleus being the base class...which was my original intent. This is my Unity "learning" project and things got a bit turned around while experimenting. $$anonymous$$y original plan was just to have one script - Nucleus - with configuration files. But that didn't work out. And 2A$$anonymous$$.
Although, In what I've read about Unity I got the impression that everything had to - in one form or another - inherit from $$anonymous$$onoBehavior...otherwise things break...?
Your answer
