I don't know why my script below in unity c# always got null ?
public class WeatherControl : MonoBehaviour {
public GameObject Rain;
public int[] RainTime = new int[]{6,7,8,9,10,18,19,20,21,22,16};
int day;
System.DateTime dates;
// Use this for initialization
void Start () {
dates = System.DateTime.UtcNow;
day = (int) dates.Day;
//day = 16;
Debug.Log ("DAY : " + day);
int posRain = System.Array.FindIndex (RainTime, x => x.Equals(16));
Debug.Log ("POS RAIN : " + posRain);
if (posRain >= 0) {
Rain.SetActive (true);
} else {
Rain.SetActive (false);
}
}
}
my variable int posRain always return -1 Even at array there is value contain it. the variable day contain 16. I put it manually and type 16 too. But always return -1.
I don't know why.
I have try this too :
int posRain = System.Array.IndexOf (RainTime, day);
That's always return -1 too.
I have tested it at online C# Tester here : https://csharppad.com/
It works at it return 10.
But in unity c# editor it is different always return -1.
Could someone explain what is going on ?
Thanks
Dennis
Your array is public, so it's serialized by Unity. Unity will set the array to the value you gave it in inspector, overriding the one you declared in the code. If you don't need to access the array from the inspector, you should use [NonSerialized] attribute. If you do need to access it from inspector, you should also edit it from there.
Finally Got The ANSWER....
And finally this is a little thing we should remember. Sometimes this can make us onfused.
Once you update the array value at script. Don't forget to resetting or reattach the script that contain array value in the inspector. When you resetting or reattach the script all the value array size will update.
For example :
in script you have an array :
public int[] RainTime = new int[]{6,7,8,9,10,18,19,20,21,22};
This is not include 16 in array. Now you run the code in editor.
In the inspector you will see that array size contain value of all RainTime array value.
Now back to the script update the array value at :
public int[] RainTime = new int[]{6,7,8,9,10,18,19,20,21,22,**16**};
You have put 16 at last array. Now Run the code in editor. And see in the inspector array size value, Value 16 is not contain at array in the inspector even you have update it via script.
What you should you is to Reset The value from inspector OR reattact the script from inspector. It will update the value and now array in the inspector now contain value 16 too.
Thanks
Related
Good afternoon guys, such a question, I'm doing an array with brute force. I can't understand why assigning a value from the last element of the FrezeTree array to the BufferObject works in the Start() method, but this does not happen in Update.
So.
At the start of the scene, there is a search for all objects with the Tree tag (This is a temporary and debag solution):
FrezeTree = GameObject.FindGameObjectsWithTag("Tree");
Then, this list is passed to the array:
public GameObject[] FrezeTree;
Next, I'm trying to pass one element from the array to another object (This happens in the Start() method):
foreach (GameObject gm in FrezeTree)
{
BufferObject = gm;
}
But it turns out that only the last element is transmitted (Because of My own crookedness, since I don't know how to fix it yet). Why do I need a Buffer Object? From it I get the X position of the object, which I use for other purposes. The idea is to transfer one element from the array to the object, and when the object ceases to exist (Gets the null status), it goes to the element above / below (No matter in what order). Yes, I know that in what I have given above and I do not feel that I am trying to make a transition or skip an element from the array. I found various solutions on the great Internet, but the result was always the same, gets the last element and does not choose another one. That's why I turned here.
As I understand you want to
set one closest target object
wait until it is destroyed
go to the next closest target object
You could do this by simply checking whether the BufferObject is already set and still alive like e.g.
using System.Linq;
...
public GameObject BufferObject;
private void Update ()
{
// This means BufferObject is either not assigned yet or was destroyed
if(!BufferObject)
{
BufferObject = FindClosestTarget();
}
}
private GameObject FindClosestTarget ()
{
// Get all currently existing trees
// since this happens only once in a while it should be okey to repeat this call
return GameObject.FindGameObjectsWithTag("Tree")
// order them by distance
.OrderBy(gm => (transform.position - gm.transform.position).sqrMagnitude)
// get the first one or null if there was none
.FirstOrDefault();
}
This way it would also take trees that are spawned later into account.
If you rather want to cache this array only once you can still do this like
private GameObject [] trees;
public GameObject BufferObject;
private void Start ()
{
trees = GameObject.FindGameObjectsWithTag("Tree");
}
private void Update ()
{
// This means BufferObject is either not assigned yet or was destroyed
if(!BufferObject)
{
BufferObject = FindClosestTarget();
}
}
private GameObject FindClosestTarget ()
{
// First filter out any null or destroyed objects
return trees.Where(t => t)
// Then order the rest by distance
.OrderBy(gm => (transform.position - gm.transform.position).sqrMagnitude)
// Then pick the first one or null if there isn't any
.FirstOrDefault();
}
See
Linq Where
Linq OrderBy
Linq FirstOrDefault
implicit UnityEngine.Object.bool operator
In general, I figured out how to implement everything myself. So, the code:
public GameObject[] FrezeTree; //Array of all objects in the scene
private void Start()
{
FrezeTree = GameObject.FindGameObjectsWithTag("Tree"); //Finding all objects with a tag (I do not recommend using tags due to the lack of code navigation)
}
private GameObject FindClosestTarget()
{
return False True.Where(t => t).FirstOrDefault(); //Assigning and sorting an array with objects from the scene, thanks #derHugo
}
void Update()
{
BufferObject = FindClosestTarget(); //will be Assigned the next element of the array, in case the current will become "null"
if (BufferObject == null)
{
Debug.Log("debug for (BufferObject == null)"); //arbitrary code in the case that any object on the stage left.
}
}
P.S. This code is perfect for creating a simple AI for NPCs in your game. It's not perfect, but at least something is better than nothing :) Thanks #derHugo
I'm trying to better understand the use of properties, in particular within Unity game engine.
What I have to do is very simple: there is a class called "Enemy" that, every time it is instanciated (i.e. spawned), must access to the class GameSession and increase the value of a counter field, "numSpawnedEnemies", that however I want to be accessible through the property "NumSpawnedEnemies".
Note: GameSession is a singleton (i.e only 1 instance exists), but I omitted the related code for simplicity.
public class GameSession : MonoBehaviour
{
int numSpawnedEnemies = 0;
public int NumSpawnedEnemies
{
get
{
return numSpawnedEnemies;
}
set
{
Debug.Log("Value: " + value); // Just for debugging purpose
numSpawnedEnemies += value;
}
}
}
In the Enemy class I then do this, in the Start() method (i.e. the method automatically prompted by Unity for each instance of the Enemy class):
gameSession = FindObjectOfType<GameSession>();
// Unity function searching for the instance of GameSession
gameSession.NumSpawnedEnemies += 1;
The result, however, is not what I expected: in fact the counter "numSpawnedEnemies" gets increased every time x2 (i.e. 1, 2, 4, 8, 16, 32, 64...), rather than +1. That because, as the debug.console function shows, the value of "value" is, actually, 1, 2, 4, etc.
Setting the value to 0 (after assigning its value to numSpawnedEnemies) in the set {} doesn't change anything, but even if it would, that wouldn't explain to me this behaviour (in fact I expect that it get reset every time I access the property).
If I simply put numSpawnedEnemies as public and increment this field from the Enemy class, it works fine.
The questions then are:
How comes that "value" keeps track of a previous value? (shouldn't it be "reset" every time I access the property from some point?)
If answer to question 1 is that it is a normal behaviour, why does it get increased by x2 each time?
Thanks in advance!
You have += on both the property and the Enemy using it.
In the property replace
numSpawnedEnemies += value;
With
numSpawnedEnemies = value;
Short solution like
#Pino De Francesco said
change statement in property from
numSpawnedEnemies += value;
to
numSpawnedEnemies = value;
Explaination
You can treat the property like 'Get' and 'Set' method depend on you implenment.
So your NumSpawnedEnemies be like
void NumSpawnedEnemies(int value){
numSpawnedEnemies += value;
}
int NumSpawnedEnemies(){ return numSpawnedEnemies;}
then if you call NumSpawnedEnemies += 1
it equal you pass numberSpanwedEnemies + 1 as value
to NumSpawnedEnemies(int value)
then final equation of your statement is
numberSpawnedEnemies = numberSpawnedEnemies + value
numberSpawnedEnemies = numberSpawnedEnemies + (numberSpawnedEnemies + 1)
that why your result is double each time you called
my goal with the code is to have multiple skulls appear on screen for comparison. Depending on number of skulls on screen (1-6) they change position and size, Ive done this through the use of an array of gameObjects (the skulls) and a list that can keep track of which skull is in which "position".
The code works if you are adding, but runs into problems when you remove the object. It "works" on RemoveAt only if you remove the GameObjects in the same order that they were added. IE: The list is always removing element 0(first item in list) rather than the specific gameObject that is instigating the RemoveAt function.
While I have found many answers on how to remove specific individual gameObjects, They dont work since my gameObjects are in an array- I cant simply say remove gameObject name. Which leads me to my question of how to remove a specific gameObject[] from a list?
either remove or removeAll run these two errors:
error CS1502: The best overloaded method match for System.Collections.Generic.List<UnityEngine.GameObject>.RemoveAll(System.Predicate<UnityEngine.GameObject>)' has some invalid arguments.
Error CS1503: Argument#1' cannot convert int' expression to typeSystem.Predicate'
Here are my two scripts for all this:
Script on each bird skull named birdController:
public int myBirdID;
public int orderInList;
//bool to keep track of object active or not
public bool whoAmIReally;
//potentially redundant; allows game object to see if hes on or not
public GameObject IAmMe;
// Use this for initialization
void Start () {
//fixingIntOrder();
}
public void OnEnable(){
//when object is on check bool
whoAmIReally = true;
Debug.Log("IM ON");
}
public void OnDisable(){
//when object is turned off uncheck bool, change list value to 0
//**this wont change actual list order or value in cubeplacement script- my hope is that it might have- likely missing a step somewhere
whoAmIReally = false;
orderInList = 0;
Debug.Log("IMOFF");
}
Script on empty with all of my array and list info named cubePlacement:
public int numberSkullOnScreen;
public bool[] skullOn;
public GameObject[] allSkulls;
public GameObject listHolder;
public List<GameObject> birdsOnScreen = new List<GameObject>();
//declaring the script to avoid NullReferenceEcxeption where I reference it
public birdController _birdController;
// Use this for initialization
void Start () {
//only way I can get my list to work: have an object act as a placeholder onStart
birdsOnScreen.Add(listHolder);
}
//turning on and off the skulls
public void toggleBirdSkull(int mySkull){
if (skullOn[mySkull] == false){
//set specific gameObject active
allSkulls[mySkull].SetActive(true);
//make skull on bool true
skullOn[mySkull] = true;
//add one to the number on screen
numberSkullOnScreen++;
//reference int orderInList from Bird controller script
allSkulls[mySkull].gameObject.GetComponent<birdController>().orderInList = numberSkullOnScreen;
//add skull to list when turned on THIS WORKS YAY
birdsOnScreen.Add(allSkulls[mySkull]);
//Run function to place skulls
placementSkulls();
}
else{
allSkulls[mySkull].SetActive(false);
skullOn[mySkull] = false;
numberSkullOnScreen--;
//remove skull from list when turned off-- THIS DOESNT WORK...
birdsOnScreen.RemoveAt(allSkulls[mySkull].gameObject.GetComponent<birdController>().orderInList);
//Run function to place skulls based on the int number skulls on screen
placementSkulls();
}
}
Have you tried to remove objects using Remove method?
Like this:
birdsOnScreen.Remove (allSkulls[mySkull]);
Your birdsOnScreen.RemoveAt version also looks correct.
If you want to use RemoveAll method, you should pass predicate, which returns bool, eg:
birdsOnScreen.RemoveAll (bird => { return bird == allSkulls[mySkull]; } );
I would suggest to use Remove in your case, since you know the object you're removing.
Read more here: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.remove?view=netframework-4.7.2
Also, try to increase your numberSkullOnScreen after you assign its value to the object's property:
allSkulls[mySkull].gameObject.GetComponent<birdController>().orderInList = numberSkullOnScreen;
numberSkullOnScreen++;
This way you won't need to use a placeholder anymore, because your indices will be correct.
I guess you're looking for a similar function like in an List. You could use this:
public static void RemoveAt<T>(ref T[] arr, int index) {
for (int a = index; a < arr.Length - 1; a++)
{
arr[a] = arr[a + 1];
}
Array.Resize(ref arr, arr.Length - 1);
}
or if you know the Object:
public static T[] RemoveObjectArray<T> (this T[] arr, T ObjToRemove) {
int numIdx = System.Array.IndexOf(arr, ObjToRemove);
if (numIdx == -1) return arr;
List<T> tmp = new List<T>(arr);
tmp.RemoveAt(numIdx);
return tmp.ToArray();
}
This question already has an answer here:
Move/Shift Objects in array up then move the first element to the last index [closed]
(1 answer)
Closed 5 years ago.
Trying to using Array.Copy in the Unity Monodevelop environment, specifically what I'm trying to do is move a value from the first slot of the array into a holder variable, then move every value in the array forward one slot, then move the value from the holder variable back into the Array in the last slot. My relevant code is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class TurnController : MonoBehaviour {
//Array will hold all units (Being that they all have the EidolonClass script attached), then will be sorted by Speed, descending
private EidolonClass[] AllUnitArray;
...
void Awake(){
//Find anything with the EidolonClass and then add it to the Array
AllUnitArray = FindObjectsOfType (typeof(EidolonClass)) as EidolonClass[];
//Sort the array by speed, descending (Highest speed listed first)
Array.Sort (AllUnitArray, delegate(EidolonClass one, EidolonClass two) {
return two.Speed.CompareTo(one.Speed);
});
}
void pushArray(){
EidolonClass store = AllUnitArray [0];
for(int i=1;i<=AllUnitArray.Length;i++){
Array.Copy (AllUnitArray, i, AllUnitArray, i-1, AllUnitArray.Length-1);
}
AllUnitArray [AllUnitArray.Length] = store;
for(int i=0;i<=AllUnitArray.Length;i++) {
Debug.Log (AllUnitArray[i].name.ToString ());
}
}
void Update () {
if (Input.GetKeyDown (KeyCode.K)) {
pushArray ();
}
}
This code compiles in Monodevelop, but when I try to run this section of my script, it returns the following error:
ArgumentException: length
System.Array.Copy (System.Array sourceArray, Int32 sourceIndex, System.Array destinationArray, Int32 destinationIndex, Int32 length) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Array.cs:971)
TurnController.pushArray () (at Assets/Scripts/Battle Scripts/TurnController.cs:54)
TurnController.Update () (at Assets/Scripts/Battle Scripts/TurnController.cs:37)
Your exception occurs because you are trying to copy the same length multiple times, but with new starting offsets each time. To shift the contents of the array, a single call to Array.Copy() would suffice.
Something like this:
void pushArray(){
EidolonClass store = AllUnitArray [0];
Array.Copy (AllUnitArray, 1, AllUnitArray, 0, AllUnitArray.Length - 1);
AllUnitArray[AllUnitArray.Length - 1] = store;
for(int i=0;i<=AllUnitArray.Length;i++) {
Debug.Log (AllUnitArray[i].name.ToString ());
}
}
That said, an array doesn't seem like the best data structure here, if you want to be able to "shift" the contents. If you were using a Queue<EidolonClass> object, then you could just Dequeue() the first element and then Enqueue() the same element to put it at the back of the list.
Personally, I wouldn't bother with either. Instead of trying to shuffle your data around, just keep an index of which element's turn is current. Increment the index to move to the next element's turn. If you reach the end of the array (i.e. the index value is equal to AllUnitArray.Length), then set the index back to 0.
Actually, the Queue<T> class does exactly that internally anyway. So if you like the semantics of the Queue<T> class, it's about the same implementation-wise.
I'm new to C# and Unity, and here I am tweaking and creating my first minigame.
Here is the problem:
I've got a little cube, that moves. I've implemented a method that checks the next position before making a move.
The method receives as parameters the current cube position, and the direction:
public bool okToMove(Transform playerCurrentPosition , int directionIndex)
{
Transform playerNextPosition = playerCurrentPosition;
playerNextPosition.Translate(toDirection(directionIndex));
if (playerNextPosition.position.x > 1 ||
playerNextPosition.position.x < -1 ||
playerNextPosition.position.y > 1 ||
playerNextPosition.position.y < -1)
return false;
else
return true;
}
Then, I call the method
public void movePlayer(int directionIndex)
{
if ( okToMove(gameObject.transform, directionIndex) )
transform.Translate(toDirection(directionIndex));
}
The problem is that the cube makes 2 moves at once. This is because of
transform.Translate(toDirection(directionIndex));
and
playerNextPosition.Translate(toDirection(directionIndex));
that is called from okToMove method. Unity or C# sees playerNextPosition as the real cube, and not somekind of temporary copy that only exists inside the method.
So why is my gameObject.transform being passed as a reference and not by value? How can I make it work?
Thanks in advance and sorry for my noobiness.
You are passing reference to Transform and then moving it with translate in "okToMove", best way is to make a copy of Vector3, just change your "okToMove" like this.
public bool okToMove(Transform playerCurrentPosition , int directionIndex){
Vector3 playerNextPosition = playerCurrentPosition.position;
playerNextPosition += toDirection(directionIndex);
if (playerNextPosition.x > 1 ||
playerNextPosition.x < -1 ||
playerNextPosition..y > 1 ||
playerNextPosition.position.y < -1)
return false;
else
return true;
}
Transform is component attached to each gameObject and it holds values for position, rotation and scale, so your "playerCurrentPosition" is not copy of position but rather reference to Transform (not a copy).
Create a new GameObject that is a copy of yours original, and use its transform to make your calculations. (This answer is originally from the Unity forums). The official documentation tells me you can use Object.Instantiate to create a clone of a GameObject.
In C#, objects have always their reference passed as value, so simply reassign won't do it. See this related question.
Objects in C# are passed by reference. If you want to copy an object, implement the ICloneable interface which has the method Clone(). You will need to copy the object yourself and return it from this method.