C# / new keyword / class member reference - c#

So i have 2 classes (Sound class, AudioManager class)
I dont understand why we can access Sound's field without creating instance of Sound class. ()
Like what are the differences between
public Sound test1;
AND
Sound test2 = new Sound();
Its weird to me cause when i was learning c# tutorials / u could only access variables from another class only by creating instance of that class but in Unity u cas access it just by typing public Sound test1;
Sound Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[System.Serializable]
public class Sound
{
public AudioClip clip;
public string name;
public float volume;
public float pitch;
public AudioSource soundclass_source;
}
AudioManager class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class AudioManager : MonoBehaviour
{
public Sound test1;
Sound test2 = new Sound();
test1.name = "test1"; // without new
test2.name = "test2"; // with new
}

Since it is [Serializable] and the Unity Inspector of MonoBehaviour and ScriptableObject (etc) classes automatically initializes serialized fields with a default instance there is no difference between
public Sound test1;
and
public Sound test1 = new Sound();
(public fields are serialized by default if the type is serializable)
Also note that once you created an instance in Unity and the values are set via the Inspector those serialized values will always overrule the ones you hardcode here. If you later e.g. decide to pre-initliaze some values and change it to
public Sound test1 = new Sound() { name = "test1" };
it will have no effect since the value configured in the Inspector is taken instead. This coded value only is used for the very forst time the instance is created or if you hit Reset in the components context menu.
If you want later that the coded value is used instead you have to assign the new value in a method instead (see below).
No accesibility definition for a field (/class/method/property/etc) like public, protected or private in c# always means private.
If a field is private like in your case you have to initialize it "manually" otherwise the default value for classes is always null (== no instance reference).
You can do this like you did staticly in the class
private Sound test2 = new Sound();
this means the instance is already created as soon as an instance of your class is created or can do it laso later in a method like
private Sound test2;
private void Awake()
{
Sound test2 = new Sound();
}
Finally you can/should make as private as possible for capsulation but can still make them serialized => displayed in the Inspector and stored using the [SerializeField] attribute:
[SerilaizeField] private Sound test2;
You can read more about Unity's serialization here

Related

Scriptable Object is null when dragged to field inspector?

I'm a novice in programming / unity and trying to use scriptable objects, but having a hard time.
I'm trying to use SO as a way to store the base attribute data of players, monsters, etc.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New StatData", menuName = "Scriptables/StatData", order = 0)]
public class StatData : ScriptableObject
{
[SerializeField] public float[] _stats;
public StatData()
{
_stats = new float[(int)StatType.Num];
}
}
The class is simple. Only has a float array(StatType is an enum).
So I made the SO in the project window under the Resources folder,
assigned all the values via the inspector,
and dragged it into the field in my StatContainer class, which deals with all attribute changes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public enum StatType
{
MaxHP,
MaxMP,
Strength,
Defense,
Magic,
Resistance,
MoveSpeed,
Num,
}
public class StatContainer : MonoBehaviour
{
[SerializeField] protected Stat[] _stats;
[SerializeField] protected StatData _statData;
void Awake()
{
Init();
}
void Init()
{
_stats = new Stat[(int)StatType.Num];
for(int i = 0; i < _stats.Length; i++)
{
_stats[i] = new Stat((StatType)i, 0);
_stats[i]._currentValue = _stats[i]._baseValue = _statData._stats[i];
}
_currHP = _stats[(int)StatType.MaxHP]._baseValue;
_currMP = _stats[(int)StatType.MaxMP]._baseValue;
}
}
I tried debugging, and in line 35 of the StatContainer class, the _statData itself is null.
I can see that it's referenced in the field in the inspector, but it's still null in debug.
I found out that if I use Resources.Load, no problem occurs.
But if I drag - drop the SO into the inspector, it's always null.
Is this not the way to use SO? or am I doing something wrong?
If I make an instance of the SO by ScriptableObject.CreateInstance,
it means that I have to assign all the values by script, and that's not what I want,
because I thought that the convenience of assigning the values in the inspector and drag-dropping or loading the SOs were a big part of using them.
It hasn't been long since I started programming and used unity, so I would much appreciate any enlightenment.
Thanks in advance!
I would answer this as a comment, but I don't have the reputation yet, as I am also pretty new. Could it be the that object you are referencing the script on is a prefab? Sometimes if you don't hit apply on the prefab, it just doesn't accept that you've referenced it in the Inspector field. That could possibly be the issue, but I have no experience with Scriptable Objects, so I could be absolutely wrong.

how to configurate nested object in Unity's inspector

I tried to create nested object in C# script in Unity and change the values in it.
Class of nested object
using UnityEngine;
public class Replica : ScriptableObject
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Class with nested object
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/DialogContainer", order = 1)]
public class DialogContainer : ScriptableObject {
public Replica[] Replicas; // Here is nested object
public Sprite[] Avatars;
}
And when I created the ScriptableObject I saw that:
Photo of ScriptableObject interface
Here I can only put an instance of the class there, but I cannot configure it.
But i want to change values right in inspector without creating and inserting object of class "Replica" like in InputManager where I can create one more obj in axis, open it and change values in inspector like that.
Photo of interface I want to see
If u want configurate your class in inspector, that class must be not derived from MonoBehaviour or ScriptableObject and be with tag [System.Serializable] than u can serialize it in nested class without creating instance.
Replica:
[System.Serializable]
public class Replica
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Dialog Container:
using System.Collections.Generic;
using UnityEngine;
public class DialogContainer : MonoBehaviour{
public List<Replica> Replicas;
public Sprite[] Avatars;
}
Than I saw that:Inspector screenshot
Unless you make a custom editor for DialogContainer, you can't do what you want to do. If you have a parent ScriptableObject that contains a child list of ScriptableObjects, you can't edit each child's fields when you have the parent selected in your Project View.
You will have to edit each child Replica by selecting the Replica instance in the Project View. Then the Inspector will let you edit that Replica.

Creating a list of <T> class on an object

This is a common question and I assure you I have done my research first. I simply cannot get a list of all of the instances of a script type on the game object.
I have tried making an array of the types and looping the contents into a list. This gives me conversion errors.
I have tried directly adding the array to the list with .AddRange. Conversion errors.
I have tried the different formats of GetComponents, and casting the output of the array into every applicable type I can think of, with no success.
I have also tried initising the list first and then running GetComponent in start.
I have tried using CharEquipGenre as both monobehaviour and non-monobehaviour.
What am I doing wrong?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class CharEquipment : MonoBehaviour
{
public List<CharEquipGenre> equipment_genres = GetComponents <CharEquipGenre>(); // I am trying to do something like this
public CharEquipGenre attack;
public CharEquipGenre defend;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharEquipGenre
{
public List<BlockScriptableObject> equipped = new List<BlockScriptableObject>();
}
// Additional code via request:
public class CharEquipment : MonoBehaviour
{
void Start()
{
equipment_genres = GetComponents<CharEquipGenre>();
}
public List<CharEquipGenre> equipment_genres = new System.Collections.Generic.List<CharEquipGenre>();
public CharEquipGenre attack;
public CharEquipGenre defend;
Well, CharEquipGenre is not a MonoBehaviour, so there are no "components" of this class.
You can find all components of a specific type with GetComponents (read more here) but it needs to be a MonoBehaviour that's attached to the GameObject
I don't know unity3d but it seems as though you are trying to initialize equipment_genres when defining it and for that the compiler would need access to something that is available at compile time.
If GetComponents is a method on the class then this will not work as the method is not static. You could use the instance by perhaps going with a method or expression body:
public List<CharEquipGenre> equipment_genres => new List<CharEquipGenre>(GetComponents<CharEquipGenre>());
Although a slightly "better" design would be exposing IEnumerable:
public IEnumerable<CharEquipGenre> EquipmentGenres => ...
// or
public IEnumerable<CharEquipGenre> GetEquipmentGenres() => ...
First of all make your class [Serializable] so you can see it in the inspector and save it.
[Serializable]
public class CharEquipGenre
{
public List<BlockScriptableObject> equipped = new List<BlockScriptableObject>();
}
And then simply instantiate a new list like
public List<CharEquipGenre> equipment_genres = new List<CharEquipGenre>();
Note that both of those will be overruled by whatever you do to them in the Inspector later. Once you added elements any change to the initialization lines is useless (unless you hit Reset in the MonoBehaviour's context menu)

CS0120 Cant set layer mask from different script

I'm making a game where i want to change which objects my player can collide with by changing the layer mask, but every time I try to change the variable in a different script it throws this error
Error CS0120: An object reference is required to access non-static
member `RaycastController.jumpableCollisionMask'
The code for where i create the variable:
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (BoxCollider2D))]
public class RaycastController : MonoBehaviour {
public LayerMask collisionMask;
public LayerMask jumpableCollisionMask;
The code for where i set the variable
using UnityEngine;
using System.Collections;
public class PlayerChanger : MonoBehaviour {
public float numberOfPlayersPerLevel;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.E)){
RaycastController.jumpableCollisionMask = 11;
}
}
}
I have tried using a setter but i couldn't get it to work. Thanks in advance and have a nice day =).
jumpableCollisionMask = 11
not
RaycastController.jumpableCollisionMask = 11
Note that you likely have another problem:
You set layer masks (which are just int) like this:
int layerMaskDogs = 1 << LayerMask.NameToLayer("Dogs");
int layerMaskFruits = 1 << LayerMask.NameToLayer("Fruits");
ok?
Never use "= 11" or any number or other value. In Unity it is now only possible to use the form 1<<LayerMask.NameToLayer("Blah")
Finally note you are using public to declare the LayerMask. That can be a bit confusing -- you only do that if you want to set it in the editor. If that's what you want to do, fine. But if you don't need to do that, just use private.
Finally note that you have this is TWO DIFFERENT SCRIPTS!!! This is the most basic problem in Unity. Fortunately it is easily solved:
-- add a public variable to your SECOND script
public class PlayerChanger : MonoBehaviour {
public RaycastController myRC;
-- IN THE INSPECTOR take your "RaycastController" and drag it to that public variable
-- Now use it like this
public class PlayerChanger : MonoBehaviour {
public RaycastController myRC;
...
...
...
//whenever you need to use it...
myRC.jumpableCollisionMask = 11;
Please read the literally 1000s of QA on this! Example, How to access a variable from another script in another gameobject through GetComponent?

Why I got empty JSON string returned while using setter and getter in the object class in Unity 5.3?

I tried to use the new JSON serialization feature in Unity 5.3, and I wrote the following code by reference the usage example provided On Unity website. The only different part that I made was creating the variables of the object class (FruitItem class in my case) by using setter and getter instead of making them pure public variables. By doing this, I only got a pair of braces without any contents inside. However, if I delete the getter and setter and make the class variables to be pure public variables, I will be able to get the correct result. Can anybody provide any hints to me why that happened? Thanks in advance for your help.
Code that works properly:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
public class testJson : MonoBehaviour {
// Use this for initialization
void Start () {
FruitItem myFruit = new FruitItem (){ name = "apple", price = 52, quantity = 53 };
string jsonString = JsonUtility.ToJson (myFruit);
Debug.Log (jsonString);
}
// Update is called once per frame
void Update () {
}
}
[Serializable]
public class FruitItem{
//using the pure public variables and the output will be:
//{"name":"apple","quantity":53,"price":52}
public string name;
public int quantity;
public int price;
}
Code that doesn't work properly:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
public class testJson : MonoBehaviour {
// Use this for initialization
void Start () {
FruitItem myFruit = new FruitItem (){ name = "apple", price = 52, quantity = 53 };
string jsonString = JsonUtility.ToJson (myFruit);
Debug.Log (jsonString);
}
// Update is called once per frame
void Update () {
}
}
[Serializable]
public class FruitItem{
//using the pure public variables and the output will be:
//{}
public string name{ get; set;}
public int quantity{ get; set;}
public int price{ get; set;}
}
Unity can not serialize properties.
http://docs.unity3d.com/ScriptReference/SerializeField.html
The serialization system used can do the following:
CAN serialize public nonstatic fields (of serializable types)
CAN serialize nonpublic nonstatic fields marked with the [SerializeField] attribute.
CANNOT serialize static fields.
CANNOT serialize properties.
Your field will only serialize if it is of a type that Unity can serialize:
Serializable types are:
All classes inheriting from UnityEngine.Object, for example GameObject, Component, MonoBehaviour, Texture2D, AnimationClip.
All basic data types like int, string, float, bool.
Some built-in types like Vector2, Vector3, Vector4, Quaternion, Matrix4x4, Color, Rect, LayerMask.
Arrays of a serializable type
List of a serializable type
Enums
Structs
List item
EDIT:
Only plain classes and structures are supported; classes derived from UnityEngine.Object (such as MonoBehaviour or ScriptableObject) are not.
https://docs.unity3d.com/ScriptReference/JsonUtility.FromJson.html

Categories