Unity, Scene and ScriptableObject - c#

I tried using ScriptableObject to store data about levels, currently, there are these properties:
public int ID;
public string Name;
public int Score;
public bool IsCompleted;
public bool IsLocked;
public Scene Scene;
As you can see there's Scene reference. In Unity Editor this looks like this:
It seems that I can't (or can, maybe I just don't know it yet) reference Scene in there, instead, I have some sort of Handle property, I couldn't find any reference to this in Unity Scripting API.
What's this? How can I store reference to the Scene in there?
Thanks!

Ok, it seems that's impossible to do. And here's the right way to do it:
https://docs.unity3d.com/ScriptReference/SceneAsset.html

Related

Best way to declare class properties in Unity3D

I am new to Unity 3D development and I wanted to know what is the best way to declare attributes of a class to be assigned later from the editor.
I have seen mainly two:
public class MyObject : MonoBehavour {
public GameObject relatedObect;
}
and
public class MyObject : MonoBehavour {
[SerializeField]
private GameObject relatedObect;
}
I understand that it is of the second form because the encapsulation is maintained, but I am not sure.
Please consider that in the example you declared class fields, and not properties.
They are both members of a class, but they are quite different.
For the sake of this answer I'll focus on your example, so I'll talk about fields.
Both public and [SerializeField] private let you inject a value in the Unity Inspector, but they act differently in the scripts.
1 - public field
You declare a public field when you want it to be used or set from another class. This choice goes against encapsulation, because other script may access it. It still might be an intended behaviour if you require the field to be accessible.
2 - [SerializeField] private field
You use this when you want to set the item in the inspector, but you do not want the other classes to see it. This option is better for encapsulation, because in this way no other script may access or change this field.
Second is the way to go if you want to do things "properly". But unfortunately there is a "bug" that throws warning on every private you set via the editor. I saw on github that it is finally getting fixed, but in the meantime if you want to remove the warning you can put your variables between pragma warning disable 0649 and pragma warning restore 0649
In addition to Jack Mariani's answer, I want to mention RangeAttribute and HideInInspectorAttribute.
[Range (min, max)]
If there's one, the inspector will show a slider instead of just a number. You move a slider and the value changes in specified range.
[SerializeField] [Range (0, 5)] private float _speed;
Documentation
[HideInInspector]
You should use it when you do not want a public variable to show up in the inspector (they are shown by default):
[HideInInspector] public float X;
Documentation

Object Reference Not Set to an Instance - Unity2D

I have this simple code to change the sprite of an image everytime I click a button.
using UnityEngine;
using UnityEngine.UI;
public class SampleChange : MonoBehaviour {
public Sprite sampleSprite;
public Image sampleImage;
public void Start()
{
sampleImage = GetComponent<Image>();
}
public void changeColor()
{
sampleImage.gameObject.GetComponent<Image>();
sampleImage.sprite = sampleSprite;
}
}
I attached this script to an EmptyGameObject and Loaded the function on the Button that is parented on a Canvas alongside the Image. I already also placed the Image and Sprite objects in the inspector:
Inspector Settings
When I run the game and click the Button, it gives me this error:
NullReferenceException: Object reference not set to an instance of an object
SampleChange.changeColor () (at Assets/Scripts/SampleChange.cs:18)
The cs:18 is the sampleImage.sprite = sampleSprite;. I really don't know why it's not working.
OK simple,
public Image sampleImage;
that means
you will set "sampleImage" variable in the inspector, in the editor, before you hit Play
But this one ..
sampleImage = GetComponent<Image>();
means
you will set "sampleImage" variable in code when the scene is running.
You have to sort it out and do it "one way or the other".
Suggest you use the first method while U learning.
(If you do use the second method, the "Image" must actually be on the game object which is holding the script in question. If you struggle with that, I would urge you to ask a separate question, or just study up on the basics using Unity tutorials.)
Cheers
It appears that you have no constuctor defined for your class and that you are just trying to call the changeColor() method as if it were a static function of the class. You need to construct objects of your class and then call the methods you defined on those objects, not on the class itself.

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.

seting universal spawn prefab

we've been banging our heads with this one, in theory, having a public static object to hold the prefab which is to be spawned should have solved this, but the script gets a static variable when the static keyword is not there, but still the objects get individual variables, while when I add the static keyword, the script's variable disappears. Are we doing something wrong? This is the exact definition:
public static Object prefab;
I too am still new with Unity, but will offer some personal research / findings. I too hate to explicitly have some object with public properties exposed so you can "stick" an object prefab in a scene. If trying to build a system / game components dynamically, knowing what / when to add would be your discretion. I too liked the approach of having a STATIC available so you can just go to it at any time you needed another "thing".
I found that using "Resources" could allow you to load a prefab via code without the need of having a scene object to drag controls to, such as for a master list.
In you default "Assets" folder, Create a folder "Resources" (not sure if case-sensitive). Anyhow, I put all my other stuff in respective folders under that... materials, prefabs, sprites, textures, whatever... So now, in my "Prefab" folder, I could have a prefab for a "Hero", "Laser", "Shield", whatever.
Now, how to get these pieces into your STATIC class. You could create a special method that you call to just load them via the resources class of Unity, then, create function that will return a CLONE of the item in question. This way, you leave the original alone, and have your own to work with in your game as needed.
Something like...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Collections;
namespace Assets.Scripts
{
public static class MyGameObjManager
{
private static bool alreadyLoaded;
public static GameObject myHero { get; private set; }
public static GameObject myWeapon {get; private set;}
public static void LoadObjects()
{
if (alreadyLoaded)
return;
// Load the resource which is RELATIVE to the "Resources" path...
myHero = (GameObject)Resources.Load("Prefab/Hero");
// This OTHER appears to load at a SPECIFIC Path allowing it to go to the root level ASSETS
myWeapon = (GameObject)Resources.LoadAssetAtPath("Assets/Resources/Prefab/Weapon", typeof(GameObject));
alreadyLoaded = true;
}
}
public class GameMgr2 : MonoBehaviour
{
public static GameObject GetHero()
{
MyGameObjManager.LoadObjects();
return (GameObject)Instantiate(MyGameObjManager.myHero, new Vector3(0, 0, 0), new Quaternion());
}
public static GameObject GetWeapon()
{
MyGameObjManager.LoadObjects();
return (GameObject)Instantiate(MyGameObjManager.myWeapon, new Vector3(0, 0, 0), new Quaternion());
}
}
}
So, the STATIC class CAN NOT derive from MonoBehavior which allows the "Instantiate" call to create the clone of the object and return it. So, I created a SECONDARY non-static class that IS derived from MonoBehavior and put static methods that call the static game object manager to get the prefabs I need.
AGAIN, I too am still learning, but hope this helps you some with your design considerations... As for the calling to get the hero or weapon, you could obviously parameterize it by passing in its Vector3 coordinate, or Quaternion value, but you can run with it.
STATIC vs NOT
Per your code and making it a STATIC means a single instance no matter how many of the class are put anywhere within the system. So if you have 5 prefabs on a screen and try to put each one to a different "thing", it will overwrite with whatever one was assigned last. Having the property as just "public" will allow you to have 5 prefabs on a screen and then drag/drop different "things" on each respectively and each will retain their own "thing" instance.
Sorry I can't advise more without knowing what you are trying to implement.
non-static should help within the editor by drag/drop. However, STATIC

Why does C# use [System.Serializable] for keeping instance? (Unity3D)

I wanted to make a programme if it take a TextAsset from inspector(it is placed in 'ExampleEditor'), it make a custom data instance for use blendshape animation.
At first I made my custom data class not using [System.Serializable].
public class Matrix {
public int row;
public int col;
public double[,] mat;
}
My strategy was taking a TextAsset, parsing string from TextAsset and finally making a Matrix instance to a field variable in 'Example.cs'. I thought when it was initialized and assigned first, I was able to use that variable in Example instance. However, It did not work, It threw 'NullReferenceException'.
So I found solution using '[System.Serializable]'. It did work. (and I knew that Unity3D does not provide multi-dimensional array for their serialization method.)
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class Matrix {
[System.Serializable]
public class mRow {
public List<float> aRow = new List<float>();
}
[HideInInspector]
[SerializeField]
public int row;
[HideInInspector]
[SerializeField]
public int col;
//[HideInInspector]
[SerializeField]
public List<mRow> mat = new List<mRow>();
}
Finally, I know the solution, But I don't know why this problem was happened. It is related to GC?
Your question isn't very clear, but I think to know what you are not understanding.
My strategy was taking a TextAsset, parsing string from TextAsset and
finally making a Matrix instance to a field variable in 'Example.cs'.
You didn't explain how you did that (perhaps call the parsing function from a custom inspector?).
However, despite on how you managed to initialize a field from the editor, if the field isn't serializable, it will be null when switching from editor to play mode.
That happens because when entering play mode, Unity3D serializes all C# classes populating the relative C++ classes of the engine. If a field isn't a public field of a type that Unity can serialize (or even a private field marked with a SerializeFieldAttribute, always of a type that Unity can serialize ), Unity3D won't serialize it.
So when you switch to play mode the reference will be null.
In other word, all fields that you want to initialize from the editor MUST be serializable in order to be used in play mode.
[SerializeField]
public List<mRow> mat = new List<mRow>();
The code above doesn't make much sense. If you plan to intialize a serializable field from the inspector don't initialize it by code, otherwise you could eventually lose the reference.
An example of such an error:
[SerializeField]
private List<int> aList;
public void Start()
{
aList = new List<int>();
}
The code above will cause troubles. If you had initialized aList from inspector, it will be correctly serialized but when you switch in play mode e the Start callback is called, aList will be reinitialized and you'll end up with a reference to an empty list.
Here's a nice blog post explaining details on how Unity3D handle serialization of objects. Read it!

Categories