I have a System.Serializable class
[System.Serializable]
public class _answersData
{
public string word;
public string wordmeaning;
public string wordmeaning2;
public GameObject result;
}
I then have a monobehaviour class where an array of _answerData class is declared
public class gamePlay : MonoBehaviour
{
public _answersData[] answersData;
}
I am then trying to add data to _answerData array from another script using below method
public class challengeScript: MonoBehaviour
{
void Start()
{
gameplayScript = gameObject.GetComponent<gamePlay>();
populate();
}
void populate()
{
answersCount = int.Parse(snapshot.Child("Answers").Child("Count").Value.ToString());
gameplayScript.answersData = new _answersData[answersCount];
for (int i = 0; i < answersCount; i++)
{
string positionNode = "Answer" + (i + 1).ToString();
string _word = snapshot.Child("Answers").Child(positionNode).Child("Word").Value.ToString();
gameplayScript.answersData[i].word = _word;
}
}
}
but I am getting NullReferenceException at 'gameplayScript.answersData[i].word = _word' line
Here's the error:
NullReferenceException: Object reference not set to an instance of an object
Can someone guide what I am doing wrong here?
Just because it is [Serializable] doesn't mean the elements are automatically created once you create the array. By default all reference type elements will still be null.
Only within Unity the Inspector itself automatically creates instances of [Serializable] types and initializes the array itself.
In code you have to actually create your element instances yourself like e.g.
var answerData = new _answerData();
answerData.word = _word;
gameplayScript.answersData[i] = answerData;
Related
Problem
I have a script that is taking avatar data from an animator in unity to automatically assign bone transforms to classes that will be used for posing; the issue is, even though all the data is there and can be used, the script somehow isn't assigning the data.
The scripts
HandAutoPoser.cs
This is called by editor scripts to grab the finger bone transforms and save them to the script, but it's not doing that expected behavior, even though all the HumanBodyBones are returning a transform, and there are no exceptions being thrown in the console.
public class HandAutoPoser : MonoBehaviour
{
private List<HumanBodyBones> _leftFingers = new List<HumanBodyBones>()
{
HumanBodyBones.LeftThumbProximal,
HumanBodyBones.LeftThumbIntermediate,
HumanBodyBones.LeftThumbDistal,
HumanBodyBones.LeftIndexProximal,
HumanBodyBones.LeftIndexIntermediate,
HumanBodyBones.LeftIndexDistal,
HumanBodyBones.LeftMiddleProximal,
HumanBodyBones.LeftMiddleIntermediate,
HumanBodyBones.LeftMiddleDistal,
HumanBodyBones.LeftRingProximal,
HumanBodyBones.LeftRingIntermediate,
HumanBodyBones.LeftRingDistal,
HumanBodyBones.LeftLittleProximal,
HumanBodyBones.LeftLittleIntermediate,
HumanBodyBones.LeftLittleDistal
};
private List<HumanBodyBones> _rightFingers = new List<HumanBodyBones>()
{
HumanBodyBones.RightThumbProximal,
HumanBodyBones.RightThumbIntermediate,
HumanBodyBones.RightThumbDistal,
HumanBodyBones.RightIndexProximal,
HumanBodyBones.RightIndexIntermediate,
HumanBodyBones.RightIndexDistal,
HumanBodyBones.RightMiddleProximal,
HumanBodyBones.RightMiddleIntermediate,
HumanBodyBones.RightMiddleDistal,
HumanBodyBones.RightRingProximal,
HumanBodyBones.RightRingIntermediate,
HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal,
HumanBodyBones.RightLittleIntermediate,
HumanBodyBones.RightLittleDistal
};
private List<Transform> _fingers;
public enum HandSide
{
Left,
Right
}
public HandSide Side;
public HandPoseData ClosedFist;
public HandPoseData OpenHand;
[Space]
public PlayerModelHand Hand = new PlayerModelHand();
private Animator PlayerModelAnimator => GetComponentInParent<Animator>();
private void Start()
{
// Start is here for when this component is added to a player model during runtime.
Initialize();
}
public void Initialize()
{
// Initialize is called by editor scripts while developing.
Debug.Log("Initialize hand poser.");
GetFingers();
}
private void GetFingers()
{
_fingers = new List<Transform>()
{
Hand.Thumb.Proximal,
Hand.Thumb.Intermediate,
Hand.Thumb.Distal,
Hand.Index.Proximal,
Hand.Index.Intermediate,
Hand.Index.Distal,
Hand.Middle.Proximal,
Hand.Middle.Intermediate,
Hand.Middle.Distal,
Hand.Ring.Proximal,
Hand.Ring.Intermediate,
Hand.Ring.Distal,
Hand.Little.Proximal,
Hand.Little.Intermediate,
Hand.Little.Distal
};
switch (Side)
{
case HandSide.Left:
GetLeftFingers();
break;
case HandSide.Right:
GetRightFingers();
break;
default:
break;
}
}
private void GetLeftFingers()
{
for (int i = 0; i < _fingers.Count; i++)
{
Transform finger = PlayerModelAnimator.GetBoneTransform(_leftFingers[i]);
_fingers[i] = finger;
}
}
private void GetRightFingers()
{
for (int i = 0; i < _fingers.Count; i++)
{
Transform finger = PlayerModelAnimator.GetBoneTransform(_rightFingers[i]);
_fingers[i] = finger;
}
}
}
These scripts are used to hold transform data.
PlayerModelHand.cs
[System.Serializable]
public class PlayerModelHand
{
public HandFinger Thumb = new HandFinger();
public HandFinger Index = new HandFinger();
public HandFinger Middle = new HandFinger();
public HandFinger Ring = new HandFinger();
public HandFinger Little = new HandFinger();
}
HandFigner.cs
[System.Serializable]
public class HandFinger
{
public Transform Proximal;
public Transform Intermediate;
public Transform Distal;
}
I think I understand now what I was getting confused with; the _fingers array wasn't containing a reference to the transforms' fields, instead it was just copying the values to a new allocation of data, and when I attempted to write the returned transforms to that array, it was writing to the array it self, not the 'assumed' references I thought the array was going to contain.
Here's the fixed script
public class HandAutoPoser : MonoBehaviour
{
private List<HumanBodyBones> _leftBones = new List<HumanBodyBones>()
{
HumanBodyBones.LeftThumbProximal,
HumanBodyBones.LeftThumbIntermediate,
HumanBodyBones.LeftThumbDistal,
HumanBodyBones.LeftIndexProximal,
HumanBodyBones.LeftIndexIntermediate,
HumanBodyBones.LeftIndexDistal,
HumanBodyBones.LeftMiddleProximal,
HumanBodyBones.LeftMiddleIntermediate,
HumanBodyBones.LeftMiddleDistal,
HumanBodyBones.LeftRingProximal,
HumanBodyBones.LeftRingIntermediate,
HumanBodyBones.LeftRingDistal,
HumanBodyBones.LeftLittleProximal,
HumanBodyBones.LeftLittleIntermediate,
HumanBodyBones.LeftLittleDistal
};
private List<HumanBodyBones> _rightBones = new List<HumanBodyBones>()
{
HumanBodyBones.RightThumbProximal,
HumanBodyBones.RightThumbIntermediate,
HumanBodyBones.RightThumbDistal,
HumanBodyBones.RightIndexProximal,
HumanBodyBones.RightIndexIntermediate,
HumanBodyBones.RightIndexDistal,
HumanBodyBones.RightMiddleProximal,
HumanBodyBones.RightMiddleIntermediate,
HumanBodyBones.RightMiddleDistal,
HumanBodyBones.RightRingProximal,
HumanBodyBones.RightRingIntermediate,
HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal,
HumanBodyBones.RightLittleIntermediate,
HumanBodyBones.RightLittleDistal
};
public enum HandSide
{
Left,
Right
}
public HandSide Side;
public PlayerModelHand Hand = new PlayerModelHand();
public HandPoseData ClosedFist;
public HandPoseData OpenHand;
public void AssignBones()
{
Hand.Thumb.Proximal = GetBone(0);
Hand.Thumb.Intermediate = GetBone(1);
Hand.Thumb.Distal = GetBone(2);
Hand.Index.Proximal = GetBone(3);
Hand.Index.Intermediate = GetBone(4);
Hand.Index.Distal = GetBone(5);
Hand.Middle.Proximal = GetBone(6);
Hand.Middle.Intermediate = GetBone(7);
Hand.Middle.Distal = GetBone(8);
Hand.Ring.Proximal = GetBone(9);
Hand.Ring.Intermediate = GetBone(10);
Hand.Ring.Distal = GetBone(11);
Hand.Little.Proximal = GetBone(12);
Hand.Little.Intermediate = GetBone(13);
Hand.Little.Distal = GetBone(14);
}
private Transform GetBone(int index)
{
Animator animator = GetComponentInParent<Animator>();
switch (Side)
{
case HandSide.Left:
return animator.GetBoneTransform(_leftBones[index]);
case HandSide.Right:
return animator.GetBoneTransform(_rightBones[index]); ;
default:
break;
}
return null;
}
}
Moral of the story is, don't assume things will work as simple as you may assume.
everyone!
Tried to make saver of my List with help of combination JSON and PlayerPrefs but stucked with the problem. It don't save my list and don't load. It only giving me my list with zero elements.
Code:
private List<Toggle> _listActiveToggles = new List<Toggle>();
public void Awake()
{
Load();
foreach (Toggle toggle in _listActiveToggles)
{
toggle.isOn = true;
}
}
public void ChooseUsableBuffs(Toggle toggle)
{
int level = PlayerPrefs.GetInt("HDD") / 10;
if (_listActiveToggles.Any(x => toggle))
{
_listActiveToggles.Remove(toggle);
Debug.Log(_listActiveToggles.Count);
return;
}
if (_listActiveToggles.Count >= level)
{
_listActiveToggles[0].isOn = false;
_listActiveToggles.RemoveAt(0);
}
_listActiveToggles.Add(toggle);
Save();
Debug.Log(_listActiveToggles.Count);
}
public void Save()
{
PlayerPrefs.SetString("Key" ,JsonUtility.ToJson(_listActiveToggles));
}
public void Load()
{
_listActiveToggles = JsonUtility.FromJson<List<Toggle>>(PlayerPrefs.GetString("Key"));
}
Tried to check what JSON contains and it showed only: {}
The JsonUtility can not directly serialized array and list types.
You rather need a wrapper class like
[Serializable]
public class ToggleData
{
public List<Toggle> _listActiveToggles = new List<Toggle>();
}
public ToggleData data;
Which now you can serialize
var json = JsonUtility.ToJson(Data);
and deserilialize
JsonUtility.FromJsonOverwrite(json, data);
However, in your specific case I guess it will not work anyway. UnityEngine.UI.Toggle is of type MonoBehaviour and you can not simply deserilialize these from JSON!
You can only store their settings and loading settings into existing toggles.
I guess actually it should be enough to store a list of bool to JSON and iterate over your toggles and the list and do e.g.
using System.Linq;
...
[Serializable]
public class ToggleData
{
public List<bool> ToggleStates = new List<book>();
}
public ToggleData data;
[SerializeField] private List<Toggle> _listActiveToggles = new List<Toggle>();
And then save via
data.ToggleStates = _listActiveToggles.Select(t => t.isOn).ToList();
var json = JsonUtility.ToJson(Data);
and deserilialize via
JsonUtility.FromJsonOverwrite(json, data);
for(var i = 0; i < Mathf.Min(data.ToggleStates.Count, _listActiveToggles.Count); i++)
{
_listActiveToggles[i].isOn = data.ToggleStates[i];
}
Note however that this means you should reference all Toggles you want to save the state for in your list via the Inspector and can not simply remove or add entries on runtime!
I have this script that get the properties by names from a shader :
At the bottom I'm changing one of the properties values by giving the property name: "Vector1_570450D5"
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.PlayerLoop;
public class ChangeShaders : MonoBehaviour
{
public Material material;
public float duration = 1;
private List<string> propertiesNames = new List<string>();
private void Awake()
{
material = GetComponent<Renderer>().material;
var propertiesCount = ShaderUtil.GetPropertyCount(material.shader);
for(int i = 0; i < propertiesCount; i++)
{
propertiesNames.Add(ShaderUtil.GetPropertyName(material.shader, i));
}
}
void Update()
{
var currentValue = Mathf.Lerp(-1, 1, Mathf.PingPong(Time.time / duration, 1));
material.SetFloat("Vector1_570450D5", currentValue);
}
}
but instead typing manual the property name I want to create a class for each property name so I will be able to type inside the SetFloat something like :
material.SetFloat(myProperties.Vector1_570450D5, currentValue);
In this case there are 5 properties so I want to be able to do :
material.SetFloat(myProperties.Vector1_570450D5, currentValue);
Or
material.SetFloat(myProperties.Color_50147CDB, currentValue);
So I thought to make this script with the attribute executeallways to create a editor script only for getting the properties and then to use the properties in this mono script like in the examples I gave.
Just to your title: You have a List<string>, not an array ;)
Before filling it with values you would need to initialize it. You can also do that together with the field declaration:
public List<string> names = new List<string>();
Even simpler would then be a proper constructor and e.g. using
public class MyClass
{
public List<string> names = new List<string>();
public MyClass(List<string> initialNames)
{
// This creates a new List<string> copying all elements of initialNames
names = new List<string>(initialNames);
}
}
And then use it like
var myClass = new MyClass(names);
If I understand correct you are further looking for a property like e.g.
public string this[int index] => names[i];
which would allow you to access a specific entry directly via
myClass[i];
instead of
myClass.names[i];
I am making a card game, and I have several scripts that control card behavior.
public class BasicCardModel : Draggable {
public int hitPoints;
public GameObject cardObject;
public static string cardName = "Basic Card";
public CardStatus cardStatus = CardStatus.None;
public void copyAttributes(BasicCardModel bcm) {
Debug.Log("Calling basic copy attributes");
hitPoints = bcm.hitPoints;
}
...
}
I have several specialized cards similar to below:
public class AttackCardModel : BasicCardModel {
public int attackStrength;
public AttackType attackType;
public void copyAttributes(AttackCardModel acm) {
base.copyAttributes(acm);
attackType = acm.attackType;
Debug.Log("Attack strength = " + acm.attackStrength);
attackStrength = acm.attackStrength;
}
}
I have a controller object that generates these cards:
public class GameController : MonoBehaviour {
public GameObject basicCard, attackCard, defenseCard, eventCard, masterCard;
public GameObject topPlayerDeck, topPlayerHand, topPlayerField;
public GameObject bottomPlayerDeck, bottomPlayerHand, bottomPlayerField;
public GameObject eventDeck;
// Use this for initialization
void Start () {
// Link controller to game objects
topPlayerDeck = GameObject.Find("TopPlayerDeck");
topPlayerHand = GameObject.Find("TopPlayerHand");
topPlayerField = GameObject.Find("TopPlayerField");
bottomPlayerDeck = GameObject.Find("BottomPlayerDeck");
bottomPlayerHand = GameObject.Find("BottomPlayerHand");
bottomPlayerField = GameObject.Find("BottomPlayerField");
eventDeck = GameObject.Find("EventDeck");
CardCollection cards = generateCards();
foreach (BasicCardModel card in cards.cards) {
if(card is AttackCardModel) {
createCard<AttackCardModel>(topPlayerHand, card as AttackCardModel, Player.Top, CardStatus.Hand);
createCard<AttackCardModel>(bottomPlayerHand, card as AttackCardModel, Player.Bottom, CardStatus.Hand);
}
else if(card is DefenseCardModel) {
createCard<DefenseCardModel>(topPlayerHand, card as DefenseCardModel, Player.Top, CardStatus.Hand);
createCard<DefenseCardModel>(bottomPlayerHand, card as DefenseCardModel, Player.Bottom, CardStatus.Hand);
}
else {
createCard<BasicCardModel>(topPlayerHand, card as BasicCardModel, Player.Top, CardStatus.Hand);
createCard<BasicCardModel>(bottomPlayerHand, card as BasicCardModel, Player.Bottom, CardStatus.Hand);
}
}
/*
for(int i = 0; i < 2; i++) {
createCard<AttackCardModel>(topPlayerHand, Player.Top, CardStatus.Hand);
createCard<AttackCardModel>(bottomPlayerHand, Player.Bottom, CardStatus.Hand);
}
for (int i = 0; i < 2; i++) {
createCard<DefenseCardModel>(topPlayerHand, Player.Top, CardStatus.Hand);
createCard<DefenseCardModel>(bottomPlayerHand, Player.Bottom, CardStatus.Hand);
}
*/
}
// Update is called once per frame
void Update () {
}
// For testing, have a CardCollection passed in later
public CardCollection generateCards() {
CardCollection cards = new CardCollection();
//AttackCardModel testcard = new AttackCardModel(4, AttackType.SQLInjection, 3);
cards.cards.Add(new AttackCardModel(4, AttackType.SQLInjection, 3));
cards.cards.Add(new DefenseCardModel(5, AttackType.SQLInjection, 1));
//Debug.Log(testcard.attackStrength + "is attack strength");
return cards;
}
public void createCard<T>(GameObject whereToPut, T objectToCopy, Player player, CardStatus cardStatus) where T : BasicCardModel {
GameObject new_card = Instantiate(basicCard);
new_card.transform.SetParent(whereToPut.transform, false);
Destroy(new_card.GetComponent<BasicCardModel>());
new_card.AddComponent<T>();
--->new_card.GetComponent<T>().copyAttributes(objectToCopy); <---
new_card.GetComponent<T>().linkModelToCardObject(new_card);
new_card.GetComponent<T>().setUpCard<T>(player, cardStatus);
}
}
I am having trouble with this line (near the end of the controller object):
new_card.GetComponent<T>().copyAttributes(objectToCopy);
Instead of calling the child method for copyAttributes, it calls the parent method, and thus does not copy the attributes that I want.
In my previous question about this, one person suggested using dynamic typing as a solution. However, even though GetComponent<ParentClass> should get the child types, it doesn't work for some reason, so I need to use generics.
How can I force it to call the child method instead of the parent method?
Make your BasicCardModel's copyAttributes method virtual.
public class BasicCardModel : Draggable {
// ...
public virtual void copyAttributes(BasicCardModel bcm) {
// ...
}
// ...
}
Then override it AttackCardModel, and cast the card model to the derived type before copying additional attributes:
public override void copyAttributes(BasicCardModel bcm) {
base.copyAttributes(acm);
var acm = bcm as AttackCardModel;
if (acm != null) {
attackType = acm.attackType;
Debug.Log("Attack strength = " + acm.attackStrength);
attackStrength = acm.attackStrength;
}
}
You need to reflect the hierarchy in your types - one way is to create an interface and add a constraint for it in your Create method:
public interface ICopyableFrom<T>
{
void CopyAttributes(T src);
}
public void createCard<T>(GameObject whereToPut, T objectToCopy, Player player, CardStatus cardStatus) where T : BasicCardModel, ICopyableFrom<T>
{
GameObject new_card = Instantiate(basicCard);
new_card.transform.SetParent(whereToPut.transform, false);
Destroy(new_card.GetComponent<BasicCardModel>());
new_card.AddComponent<T>();
new_card.GetComponent<T>().CopyAttributes(objectToCopy);
new_card.GetComponent<T>().linkModelToCardObject(new_card);
new_card.GetComponent<T>().setUpCard<T>(player, cardStatus);
}
which you then need to implement in your classes:
public class AttackCardModel : BasicCardModel, ICopyableFrom<AttackCardModel>
{
public void CopyAttributes(AttackCardModel src)
{
}
}
I am trying to declare an array of buildings but the floors associated with each building are never initialised. I want to provide values to the data members for each instance of floor that is associated with each instance of building:
class Floor
{
public int number;
public int rooms;
//constructor
Floor()
{
rooms = 5;
number= 0;
}
}
class Building
{
public Floor[] floors= new Floor[6];
}
public partial class frmF2 : Form
{
Building[] x = new Building[7];
...
}
But it is very ugly.
class Building
{
Building()
{
floors = new Floor[6];
for(int i=0; i<6;++i)
floors[i] = new Floor();
}
public Floor[] floors;
}