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.
Related
Update: As mentioned in the comments below, Targeting target_manager; (in MakeAbility) is null. This seems to be the root of the issue, but why is it null?
===========
In the code below, I create an instance of the class PartAttack and then assign it the reference for my target manager.
The first debug log returns PartAttack. The second returns null.
public Targeting target_manager (in the first script) is assigned in the inspector to a gameobject with the Targeting script on it.
What am I doing wrong? Why is this not assigning the target manager correctly?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class MakeAbility : MonoBehaviour
{
public BlockScriptableObject block;
public IDictionary<string, IAbility> abilities_parts = new Dictionary<string, IAbility>();
public Targeting target_manager;
public PartAttack part_attack = new PartAttack();
private void Start()
{
part_attack.block_attack_damage = block.attack_damage;
part_attack.target_manager = target_manager;
Debug.Log(part_attack);
Debug.Log(part_attack.target_manager);
abilities_parts.Add("part_attack", part_attack);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class PartAttack: IAbility
{
public Targeting target_manager;
public void Use()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Targeting : MonoBehaviour
{
public int max_targets = 3;
public string current_target = null;
public List<Combatant> list_of_targetable = new List <Combatant>();
public BlockScriptableObject block; // The abilityblock
public GameObject target_clickfield_char1;
public Combatant character_slot_1;
}
TargetManager does not appear to be instantiated anywhere.
You need to set TargetManager to a memory allocation. This is done with instantiation.
You can either create a constructor for the aggregate class that will allocate this memory by instantiating the variable, or you can set target_manager to some new memory allocation in your main function.
public Targeting target_manager = new Targeting();
C# is fast. And we can't expect to see an instantiated object so soon after the pointers are declared. I understand that there is a game object elsewhere in the system that will eventually assign it, but for your debugging script, that hasn't happened yet.
You need to assign it to an empty object inside of a constructor if you intend to always have something there. Especially if you write a line of code that is going to access it immediately after declaring its existence. Don't rely on other code that exists in a different object. To the computer that code is very far away.
This wont hurt your later assignment, as reassignment isn't going to break anything, it just allocates more memory somewhere else.
If you're worried about memory leaks, dispose the object before redeclaring it.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class VictoryCountdown : MonoBehaviour
{
public float victoryCountdown = 300.0f;
public Text startText;
void Update()
{
victoryCountdown -= Time.deltaTime;
startText.text = "Survive until dawn!: " + (victoryCountdown).ToString("0");
}
}
/////////// 2nd Script below ///////////////////
using UnityEngine;
public class VictoryManager : MonoBehaviour
{
public VictoryCountdown victoryCountdown;
Animator anim;
void Awake()
{
anim = GetComponent<Animator>();
}
void Update()
{
if (victoryCountdown <= 0)
{
anim.SetTrigger("Victory");
}
}
}
Hello, I'm a beginning student so this may be an obvious error. I need to create a countdown timer to end a game. I believe that I have everything working, but ran into this issue at the final hour.
The first script creates a timer and then counts down to 0. The second script triggers the victory screen animation. Unity is returning the error:
Operator <=' cannot be applied to operands of typeVictoryCountdown'
and `int
I made some progress, but hit this hurdle and was hoping someone more experienced could tell me what I have done wrong.
You have a problematic naming convention. Also, you are trying to compare the
VictoryCountDown object with an int and that's not possible.
you can easily fix it like this. But it'll look like a mess...
if (victoryCountdown.victoryCountdown <= 0)
{
anim.SetTrigger("Victory");
}
It looks like you are defining an object of type VictoryCountdown, and you are naming that object victoryCountdown. Your comparison isn't accessing the class variable victoryCountdown, but the object. To get the variable, you have to do something like victoryCountdown.victoryCountdown, where you are accessing the variable through an instance of the class (though I would recommend changing the name of the class instance to avoid this confusion).
I would also note that the variable you want is a float and 0 is an int, so that comparison might not work as well (I'm not as familiar with how C# handles this kind of type disparity)
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?
How can I add a variable to a GameObject?
GameObjects have a series of variables (name, transform, ...) that can be accessed and modified from any script. How could I add a variable such as for example "color" or "type" that could be accessed or modified from other scripts.
Thanks.
If GameObject wasn't a sealed class then inheritance could have been the solution to your problem. In this ideal situation you could try the following.
public class ExtraGameObject : GameObject
{
// Logically you could make use of an enum for the
// color but I picked the string to write this example faster.
public string Color { get; set; }
}
A solution to your problem could be to declare your class like below:
public class ExtraGameObject
{
public string Color { get; set; }
public GameObject GameObject { get; set; }
}
I am aware of this that this is not exactly that you want, but that you want I don't think that it can be done due to the sealed nature of GameObject.
As every object in c# GameObject class inheritance Object class. If you right click on GameObject and select go to definition you will see that below.
In order to reach one object's variables you need a reference(not considering static variables). So if you want to change variables of one GameObject from other you need a ref.
On Unity inspector you can assign values to public variables of a script. Below you can see Obj end of the arrow. Now assign cube(is a GameObject ) to Obj.
After drag and drop
Now you can reach every subComponent of cube object. In red rectangle I reached it's Transform component and changed it's position.
I hope I understood your question correct.GL.
Christos answer is correct. You cannot add a variable to the GameObject datatype because it was built into the language as a base. To add variables to premade data type, extend off of them. For more information on inheritance see
https://msdn.microsoft.com/library/ms173149(v=vs.100).aspx
You can add a script to the prefab from which an object is created using the Instantiate method. In this script, you declare public variable.
Then you can change and get the value of this variable using the GetComponent method.
The code in script file added to the prefab (prefab name is thatPrefab, script file name is IdDeclarer) as a component:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IdDeclarer : MonoBehaviour
{
public int id;
}
Declaring an object's characteristics (ID in example):
GameObject newObject = Instantiate(thatPrefab, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
newObject.GetComponent<IdDeclarer>().id = 99;
Debug.Log(newObject.GetComponent<IdDeclarer>().id);
Output: 99
I hope I helped you
I am trying to get data from an int variable in Unity using C# code.
Below is the C# code I am using to get the int.
using UnityEngine;
using System.Collections;
public class endGameMessage : MonoBehaviour {
public static int score2;
void Start () {
GameObject thePlayer = GameObject.FindWithTag("Player");
gameScript game = thePlayer.GetComponent<gameScript>();
score2 = game.score;
}
// Update is called once per frame
void Update () {
Debug.Log (score2);
}
}
Below is the code from the other script I am trying to pull the data from.
using UnityEngine;
using System.Collections;
public class gameScript : MonoBehaviour {
//score
public int score = 0;
void OnTriggerEnter(Collider other) {
if(other.gameObject.tag =="hammer"){
GameObject.FindGameObjectWithTag("pickUpMessage").guiText.text = ("Picked Up A Hammer");
Destroy(other.gameObject);
Debug.Log("collision detected hammer");
audio.PlayOneShot(gotHit);
score = score+10;
}
}
}
I can get the the int value to come across to the other script but its always 0 even if the int was meant to be 10.
My question is how would i keep the value across the scripts? Any help is appreciated.
try this
public static int score2
{
get
{
return GameObject.FindWithTag("Player").GetComponent<gameScript>().score;
}
}
You have a lot of possibilities.
The first one is to set your Score as a static argument for you gameScript.
So you can access it anywhere just like that :
int myScore = gameScript.Score ;
And the declaration should be :
public static int score;
The second possibilities is far better if you want to save a lot of differents values from differents script.
In this case, you need to define a gameContext singleton.
If you don't know what is this, you should take a look at singleton in C# :
[https://msdn.microsoft.com/en-us/library/ff650316.aspx]
Singleton will allow you to have a single instance of your gameContext.
In your case, your singleton will have a Score attribute.
And you will be able to get the value from any scene and any scripts.
This is the best way so far.
score2 is read once at start and then never again. int is an integral type in C# and thus passed by value i.e. it receives a copy. There several ways to solve this problem.
The easiest solution is to access the gameScript.score directly - it provides read/write access to everyone anyway. To encapsulate it you may choose to define a property.
A better way could be to define a new class GameStatus which holds all relevant things. This can be implemented as singleton for example.