store a reference to a script to attach at runtime - c#

Another best practice question. I have a list of ScriptableObjects now (thanks to LearningCocos2d) which defines a list of sprites I can load at runtime.
I followed this tutorial: http://www.jacobpennock.com/Blog/?p=670
In order to drive some custom behaviour, I want different scripts applied to my various in-game objects when I instantiate them. What's the best way to store the references to the desired scripts I want to apply?
[Edit] Some more details:
My scriptable object is a simple list of serializable objects. I have a series of defined scripts that I want to attach to the objects I define. Unity however does not seem to allow me to store a reference to the script using the below method.
public class TestList : ScriptableObject {
public List<MotionSpriteData> MotionSprites;
}
[System.Serializable]
public class MotionSpriteData {
public Component motionPath;
}

Create a class that loads all the scripts into an array. This class does not necessarily have to be a MonoBehaviour, but for this example it will be.
You have two options:
Drag and drop the scripts into the array via the Editor.
Or put all the scripts in the Resources/Scripts/ folder so that they can be loaded at run time.
public class ScriptManager : MonoBehaviour
{
public Object [] list;
void Awake()
{
// Comment this line if you used step 1 above.
list = Resources.LoadAll("Scripts");
gameObject.AddComponent(list[0].name);
}
}
Now you can use your own logic to determine which GameObject gets which Script, but that should be trivially easy for you.

Just coming back to this question, since my original answer is actually incorrect as someone pointed out. Monoscript stores references to individual scripts (strongly typed), but Monoscript is only valid in the UnityEditor namespace.
Since my question is about best practice, I offer what I have done as a proposal, but since I'm still only 2 months into Unity dev, I'm interested in other opinions. What I ended up doing is building prefabs for each object type, which leverages the power of the Unity WYSIWYG interface. Thus, storing references to scripts becomes irrelevant, since the prefab contains all the behaviours I need.
Since I still need a data layer for my game to drive the game play, my scriptable objects have hence become simpler. The problem of referencing the script becomes instead the problem of referencing the prefab which contains the scripts.
FunctionR has described in his answer how you can use Resources.Load to load content at runtime. However, my question is about how to reference the individual script I want. What I have done is simply store a path to the resource (ie: prefab) I want loaded, as a string.

Unity script assets are of type "MonoScript". Simply declare a variable as this type and it will work.
http://docs.unity3d.com/ScriptReference/MonoScript.html

Related

Is there a way to find first component of type?

Ok so basically I have a script which highlights gameobjects with a specific tag if your mouse is pointing at.
After it's highlighted you need to press a specific key and you will execute a public function inside the interactable object. Now the problem is when I want to search a specific component instead of using it's name, any help is expected. :)
Script isn't a type, but Component is the base type for anything that can be attached to a game object (hence GetComponent having the name it does). Components include things like Transform, MeshFilter, etc. Most scripts that you'll write inherit MonoBehaviour, so you totally could do something like GetComponent<MonoBehaviour>() but then you'll get the one (or all, if you use GetComponents) of the MonoBehaviour scripts attached to the game object.
Since you're just blindly getting any script without knowing its type in advance, you're going to wind up with some big if/else chain where you keep trying to downcast the object to a concrete type that you can actually do something with.
The solution is to use an interface. If multiple classes can all do the same thing, then make an interface that encapsulates that functionality. In your case you might have an IUseKeystrokes interface for all the kinds of classes you could make that would use your keystroke sequence technique.
public interface IUseKeystrokes
{
void Use(char keystroke);
}
Then you add that to any class you're writing and you'll get a compile error if you don't implement the Use(char) method.
public class MyThing : MonoBehaviour, IUseKeystrokes
{
public void Use(char keystroke);
// and other stuff for the class
}
and now finally you can call
IUseKeystrokes useKeystrokes = targetGameObject.GetComponent<IUseKeystrokes>();
and now it actually doesn't matter what class it is, no need to downcast, you just call
useKeystrokes.Use(keystroke);

Can you force MonoBehavior scripts automatically include other components if they don't already exist?

I've seen a few places where Unity has the ability to "link" or otherwise require component dependencies that are added (at edit-time) and cannot be removed until the dependent component itself is removed.
Is there a way to use this behavior from custom scripts?
I know that you can create components at runtime using ObjectFactory, but it's expensive and doesn't solve the problem of being able to tweak parameters on the dependency components manually.
Yes - the [RequireComponent(typeof(SomeDependencyComponent))] attribute does exactly this.
For example:
[RequireComponent(typeof(AudioSync))]
public class BehaviorPlayer : MonoBehaviour
{
private AudioSync audioSync;
public void Start() {
audioSync = GetComponent<AudioSync>();
}
}
This automatically creates an AudioSync component (in this case, a custom script in my project) that is guaranteed to be present
at runtime, barring any programmatic removals.
It also shows up as an editor component on the game object that can be manually tweaked:
Further, if I try to remove the required AudioSync component, Unity spits out an error:

Unity/C# what's wrong with the way I think about this problem

I have an abstract parent class called Item
From this I inherent to Equipment and from here to Weapon and Armor. Since I want my weapons to have varying behavior, I want my logic to operate on the classes. From my Item class I also inherit to a Resource class, which should be static. My Equipment class shouldn't and should be instansiable , since an individual sword eg. Can be sharpened or damaged, to change it from its base class without changing all other swords. So I need to clone these class instances. But since cloning a class is a very non forward thing to do, I was wondering if I am doing something wrong in my architecture, and thinking about the problem in the wrong way? Do anyone have any thoughts? /Mikael
I think you should consider setting the 'stats' for items in a file (xml or such) and then create a Factory class that can give you a file with all the stats already set.
i.e.
public abstract ItemFactory{
public static Sword GetSword(){
var sword = new Sword();
//Set stats for sword from file
return sword;
}
}
Why use cloning when you can just use referencing? If I understand your problem correctly, I would utilize ScriptableObjects for defining your item objects, which makes them Assets in your project. Which then can be assigned via the inspector.
The general class layout could look something like this.
class Equipment : ScriptableObject
{
public int goldCost;
}
class Weapon : Equipment
{
public intDefaultSharpness;
}
These are your definition classes, remember they are to define the general properties of each item category. So you might have a Sword, but not a "Broadsword of fiery Dragonkiller". If you check the link about ScriptableObjects you will find code that allows you to instantiate new Assets from these definition classes. Now you can create your Broadsword and ofc many more. Now your objects just become assets like Textures and 3D models. You can easily edit them in the inspector (and probably want it to make it even nicer with custom editors)
Similar to a texture or 3D Model we don't copy it every time we use it, instead we reference it. So when your player character holds a weapon you have a sub GameObject that just keeps a reference to the original object and any additional "local" data.
class PlayerWeapon : GameObject
{
public Weapon template;
public int durability;
}
The nice thing is, the template Weapon is assignable via inspector in the editor, as it is just a normal asset. And the durability property is local to this playerWeapon and the GameObject could be used multiple times.
Of course, this can be expanded on as much as you want. I would probably add getter/setter to hide additional calculation based on buffs/debuffs. Something like
public int AttackStrength
{
get
{
return owner.strength + template.strength
}
}

Comparing variables in Lists of two different types

I am using Unity3D to create an RPG game similar to Final Fantasy and such. In c# of course.
For entities in the game, there are two separate classes: Character and Enemy.
For my battle code, I have two lists, one List called Party, and one List called Enemies.
I often need to compare and modify int values of ALL Characters and Enemies together, which was difficult to do because they are in separate lists. They have the same variables with the same names, e.g. (int Reflex, int Attack, int Strength, int Defense, etc. ) but they are a different type, so I don't know to do, for example, get the Character OR Enemy with the highest Reflex out of both lists for example.
My immediate inclination was to use them as derived classes, have both Character and Enemy inherit from a base class Entity with their overlapping variables contained in that. Then I could have one list of Entity in the battle to use. SO much easier, BUT damn Unity3D does not support polymorphism for its serialization process which all of my saving and loadings currently depends on.
I am hoping there is an easier answer for this; otherwise, I will have to painfully rewrite all of my saving and loading code, somehow, to not use Unity's serialization...
So basically, can I compare these variables things easily? And if so, how?
EDIT: Answered my own question
This might be kind of ridiculous so soon after having asked the question, but I just unexpectedly came across a very nice blog post describing the same problems that I was having:
http://www.archmagerises.com/news/2015/9/22/tips-on-game-world-state-data-serialization-in-unity-c
Following his example, I implemented sharpSerializer in my game. It works great, and I returned to my initially planned system of polymorphic classes.
Now I don't have to try to force my code to work around Unity's awful built-in serializer.
I wanted to post this because there don't seem to be many posts either on the Unity forums or here which address this issue, so anyone has the same problems as me can find a nice fix.
Unity's built-in serializer could use a lot of work, but I've really only run into real-huge headaches while doing editor scripting. Like, I very much sympathize with this guy's rage. Run-time serialization is a bit different, it's a bit easier to work with, especially with the help of an external tool like JsonDotNet for Unity, or I see you've mentioned elsewhere SharpSerializer. There's also FullSerializer and it's asset store offshoot FullInspector, which is intended specifically to help deal with those uniquely frustrating editor scripting headaches.
A couple things to note: Unity does support polymorphism for UnityEngine.Object derived classes, and this of course includes MonoBehaviour. As for custom classes, you can implement the ISerializationCallbackReciever interface if these classes have properties (such as Dictionary) which unity cannot by default serialize. Otherwise you just add the [Serializeable] tag to the class to let unity know that you want that data to be saved. There are a few other caveats you should familiarize yourself with, see : https://blogs.unity3d.com/2014/06/24/serialization-in-unity/
Using your use case as an example, the usual structure might look something like this:
[Serializeable]
public Class ActorProperties{
public int CurrentHealth;
public int MaxHealth;
public int Range;
}
public Class Actor : MonoBehaviour{
[SerializeField] protected ActorProperties _actorProperties
public ActorProperties ActorProperties{
get{ return _actorProperties;}
set{_actorProperties = value;}
}
}
public Class Character : Actor{
// Character specific code
}
public Class Enemy : Actor{
// Enemy specific Code
}
public Class GameManager : MonoBehaviour{
private List<Actor> enemies;
private List<Actor> characters;
public List<Actor> AllActors{
get{
List<Actor> returnList = new List<Actor>(characters);
returnList.AddRange(enemies);
return returnList;
}
}
public Actor GetActorWithHealth(float healthCheck){
Actor actor = AllActors.Find(x => x.ActorProperties.CurrentHealth == healthCheck);
return actor;
}
}
Aside from Non-Serialized DataTypes, the example below illustrates most common instances when custom serialization is needed.
// Even though Properties is marked as Serializeable, it's 'data' property
// won't get serialized if we're serializing a reference to an ActorProperties.
// No native support for polymorphic serialization of custom classes.
[Serializable]
public class Properties{
public float data;
}
[Serializeable]
public class ActorProperties : Properties{
// Here we have a recursion problem because Unity cannot serialize
// null values for custom classes. Unity will try to serialize this ActorProperties field, which in turn starts the serialization over again,
// with an iteration depth of 7. Killer if it were a List<ActorProperties> .
public ActorProperties EnemyProperties;
public int CurrentHealth;
public int MaxHealth;
public int Range;
// Since Unity treats custom classes like structs, the following field wont be serialized
// as a pointer to an existing object, but as a unique instance of it's class.
public CustomClass SharedReference;
}
Serialization solutions like Json.NET for Unity et. al. go a long way towards making the serialization process easier, but regardless of what solution you use, it's extremely important to pay attention to precisely how your data is going to be serialized.
Personally I would recommend Json.Net for Unity, but the only other assets I've had some experience with is FullSerializer and FullInspector. I've not used SharpSerializer.
Why not abstract away the data for Characters and Enemies by using a Plain Old C# class with an ID field? (maybe called something like "CreatureData" "ActorData" or "CharacterData"). Each instance of this abstract class could then reference this new ID field that is contained within both Characters and Enemies.
Instances of the new C# class could then be stored in another collection of some kind like a dictionary or a list and easily iterated through later.
Unless I'm mistaken, as long as you make this new class serialize-able, Unity should still be able to handle it.

How can I access a class that is inherited from monobehaviour

My problem is a bit more complicated than the title:
I've got a GUIController class, which controls the GUI and I want to create an in-game buy menu. In this menu I can choose from weapons and I can buy them if I've got enough money. I have to access their data, like price, description or name.
Problems:
-To access their data, I have to instantiate an object from the class (start function has to run before accessing the data).
-The main Weapon class is inherited from MonoBehaviour, so I can't instantiate an object from the class.
Possible solution:
-I could instantiate a gameobject which contains the class as a script component and then I could access its data. I think it's not a nice solution though.
Question:
Should I store the information somewhere else? In a local db or something? I thought storing the information inside the class is a good idea, but I'm not that sure anymore.
Thank you for your answers in advance!
EDIT: Given your class hierarchy the static variable suggestion clearly won't work. Edited to give another possible solution.
You could create a new class for Weapon properties and make instances of it for each weapon type. These could be populated from code, a database, config files, etc. When constructing an instance of a specific Weapon you could give it the properties object and copy across the values of all properties. This could be done using reflection to save you having to write an assignment for each property.
The weapon properties objects could be made and accessed in many ways. One suggestion would be to have a repository which could be used to get a reference to the object (if they are singletons) or return a new one.
Incidentally, this is quite a broad question and the 'correct' answer may depend on how much data you have and how you intend to use it, or may be a matter of best practice. As such it's possibly better suited for programmers.stackexchange.com or gamedev.stackexchange.com.

Categories