- Home /
The question is answered, right answer was accepted
How to store a javascript in a variable in the editor then call a function from it?
Well that's a long winded question now isn't it. Let me try and clarify what I am trying to do. I am creating an RPG where the player will be able to walk up to objects in the game and "use" them. I expect the number of things that an object could do will grow quite large so instead of trying to put all the behavior on one script I want to have several individual scripts that can be referenced depending on which behavior I want the object to have.
For example a door would contain a "BaseObject" script component, which has a variable called "InteractScript". Developers could then drag a "door" javascript into the InteractScript inspector. the When the player uses the door in the game "BaseObject" the will call "InteracScript.Activate()"
All interact scripts will all have an Activate() function, but what that function does will very greatly. In this way the "Door" InteracScript would open and close when the Activate() function was called, but the furnace would open a crafting menu.
If you can't tell already I started my programming career in Unrealscript, so I am used to extending classes and passing functions down from inheritance. I know this can be done in Unity too I just haven't got it figured out yet.
Thanks in advance for any help you can provide : )
P.S. please answer with java-script if possible.
Answer by CiberX15 · Sep 30, 2013 at 01:18 AM
Ok, I finally got this working exact how I wanted it. What I had to do first was make my base class extend from monobehavior as such:
class InteractBase extends MonoBehaviour
{
function Activate(User : GameObject, Self : GameObject)
{
Debug.Log("=============== DEFALUT ACTIVATE =======================");
}
}
Then I could create new classes that extended from that one and overrode the Aactivate() function as such:
class Door extends InteractBase
{
private var Open = false;
function Activate(User : GameObject, ThisObject : GameObject)
{
Debug.Log("======================== OPEN THE DOOR HAL! ===============================");
if(Open)
{
//CLOSE DOOR
ThisObject.animation[animation.clip.name].speed = -1;
ThisObject.animation[animation.clip.name].normalizedTime = 1.0;
ThisObject.animation.Play();
Open = false;
Debug.Log("Close Door");
}
else
{
//OPEN DOOR
ThisObject.animation[animation.clip.name].speed = 1;
ThisObject.animation.Play();
Open = true;
Debug.Log("Open Door");
}
}
function InteractHit()
{
//Debug.Log("DOOR HIT");
}
}
Then in my script that runs on all of my objects I created a variable to store an instance of my custom InteractBase class script. Then when the player used the item, the main script would check if the variable had anything in it, and if so, call Activate() on that object as such:
var InteractScript : InteractBase;
//called when the player presses interac with this item
function Interact(User : GameObject)
{
if(InteractScript != null)
{
InteractScript.Activate(User, gameObject);
}
}
Then finally to make it all work, I would attach the script in question, in this case "Door," and then drag the script component in the inspector into the main scripts InteractScript variable which would be attached to the same object.
Once all of this was done, when the player hit use on the object, the main code checked if its InteractScript had anything in it, and if so called the Activate() function on it. In this case causing the door to toggle open and closed.
This was a massive issue that has been holding me back in unity for months. I hope that this helps anyone else trying to do the same thing. : )
Answer by whydoidoit · Sep 29, 2013 at 05:57 AM
It can be done in Unity - but the question is probably more "should it be done" in Unity. My contention would be that using inheritence to perform these kind of actions ties you in knots. I would always do one of these things:
SendMessage (BroadcastMessage)
Use interfaces (and maybe create a base class that does the core implementation for me)
Use .NET events / delegates
Your easiest method for something as simple as you suggest is to just use SendMessage - it is perfectly fast enough for this kind of interaction.
SendMessage("Activate")
And any scripts on the object with an Activate function will perform it. This is cool because you could attach two scripts that do different things, one opens the door, the other turns on a Red Light above it. To make that just attach the two scripts. You could also use BroadcastMessage and if the door had children with relevant scripts they would be activated too.
Now the interface method is slightly trickier, but it's faster so if you needed something to happen every frame then you'd go this way perhaps. An example of where I use interfaces is the weapon systems on my AI military vehicles. I can attach any number of weapons to a vehicle, each of which implements an interface that returns it's capacity, firepower, availability etc and provides the method to fire. I need to query this data often, so SendMessage is not applicable (it's really for infrequent events).
So you might say - why don't you make all of your weapons inherit from a base Weapon class? The answer is long bloody experience, where you get a weapon that doesn't fit the base class model and ends up carrying around a load of rubbish that makes no sense to it. Sure it might be fine - but you will end up refactoring thinking "oh right I need a new intermediary class don't I, to describe that group of weapons - right". It all takes too long for my liking. By using an interface I can still create a base Weapon class that most weapons use, but my special cases just don't bother inheriting from it and provide their own implementation.
In fact in that way you can get great mix and match - so a weapon could implement more than one interface to enable simple sub behaviours (like being intefered with electronically or being rechargable etc etc).
The real benefit of this kind of approach really fits with Unity's model. Don't build massively complicated classes and scripts, build simple ones and get them to work together to build a much wider array of effects and behaviours.
The final method would be to use a .NET event or a set of delegates. Using that approach your "Activate" call on a well known script (Activatable.cs?) would raise an event. Other scripts attached to the game object could listen for that event by adding and destroying handlers for it. Events are pretty much as fast as interfaces (meaning it's the same as calling a normal method).
using System;
using System.Collections;
using UnityEngine;
public class Activatable : MonoBehaviour
{
public event Action OnActivated = delegate {};
public event Action<float> OnDamaged = delegate {};
public void Damaged(float damage) { OnDamaged(damage); }
public void Activated() { OnActivated(); }
}
//Door.cs
public class Door : MonoBehaviour
{
void OnEnable() { GetComponent<Activatable>().OnActivated += Activated; }
void OnDisable() { GetComponent<Activatable>().OnActivated -= Activated; }
void Activated()
{
}
}
Oh sorry, that last example was C# - interfaces and events do work in JS. I'd stick to C# myself though - it's a matter of preference, but it's a more flexible language although slightly more verbose for some things that Unity Script.