Is it possible to change a variable in another scene in unity. I have a script right now that has the user pick 5 heroes and those 5 heroes get saved to a array, but in order for the game to run how i want it, that array will be in another scene and I'm not sure how to go about saving the five heroes data to an array in another scene. I can do it all in one scene but 2 scenes would be more efficient. Here's my code:
using UnityEngine;
using System.Collections;
public class HeroChooser : MonoBehaviour {
public static GameObject Archer;
GameObject Berserker;
GameObject Rouge;
GameObject Warrior;
GameObject Mage;
GameObject MainCamera;
public int counter = 0;
public bool archerOn = false;
public bool berserkerOn = false;
public bool rougeOn = false;
public bool mageOn = false;
public bool warriorOn = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnGUI(){
if(archerOn == false){
if (GUI.Button (new Rect(50,0,50,50), "Archer")){
Archer = GameObject.Find("Archer");
MainCamera = GameObject.Find("Main Camera");
HeroArraySaver heroArraySaver = MainCamera.GetComponent<HeroArraySaver>();
heroArraySaver.array[counter] = Archer;
archerOn = true;
counter++;
}
}
Its saying that: Static member HeroArraySaver.array cannot be accessed with an instance reference, qualify it with a type name instead im not sure how to go about fixing it.
A simple way would be to create an empty GameObject and attach a script/MonoBehaviour to that which holds your data. To make it persist you would have to call DontDestroyOnLoad() on that GameObject. This will ensure your GameObject will hang around when moving to a different scene.
So something like:
GameObject myPersistentDataObject = new GameObject("myPersistentDataObject");
MyDataClass data_class = myPersistentDataObject.AddComponent<MyDataClass>();
//set your data to whatever you need to maintain
And in your Awake of your MyDataClass you'd do something like
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
Then in your other scene you can simply find your GameObject again and retrieve its data from the attached component.
Assuming you have integer IDs for the heroes, simply store them in a static variable:
public class GlobalData {
public static int[] heroIds;
}
Static variables can be accessed from any scene and will persist as long as your game runs. The same technique works for strings or enums.
Related
i am very new on unity and im trying to do my first game. i want to put the clone objects on array and then add a back button that destory the last clone that made, ive tried to make array but it let me only clone 1 object and then i cant do anything, thank you for help.
i have this code :
public class TouchSpwan : MonoBehaviour
public GameObject prefab;
public GameObject clone;
public Rigidbody rot;
public Camera mainCamera;
public FixedTouchField touchField;
public float gridsize;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 touchPos = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 9.8f));
touchPos.x = Mathf.Floor(touchPos.x / gridsize);
touchPos.z = Mathf.Floor(touchPos.z / gridsize);
Vector3 result = new Vector3((float)touchPos.x * gridsize,touchPos.y, (float)touchPos.z * gridsize);
if (touchField.Pressed == true)
{
clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
}
}
}
First of all yor GameObject clone, does not have to be a public class member. Instead of
clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
you could just say:
GameObject clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
and get rid of the declaration at the top:
public GameObject clone;
After you check if the left mouse button has been pressed you do an additional check for some touchField.Pressed, not really sure why, but if you remove it you can spawn as many clone objects in a place where your mouse pointer is as you like.
When it comes to storing these object in the array, you can just declare an array of GameObjects and add each clone after instantiating. You do need to have some sort of maxLimit for clones.
declare the arr: private GameObject[] clonesArray;
initialise it: clonesArray = new GameObject[maxNumberOfClones];
Once you've done that you can add each clone to the array after its instantiating:
GameObject clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
clonesArray[currentIndex] = clone;
currentIndex++;
You need to keep track of currentIndex, start with 0 and then increment it after you add a clone to the array. Then to destroy the clone you need to access it from the array and use Destroy(). Do not forget to decrement the index counter.
Destroy(clonesArray[currentIndex]);
currentIndex--;
I hope that helps :D
I am having a very little problem in my Unity project but can't find a proper help or way to do. I am stuck at point where I have an array of prefab GameObjects and I am trying to instantiate index 1 GameObject and when it destroyed instantiate the next index. Here is how I am doing it. I have two scripts: One to instantiate and other one to destroy it.
Scripts 1:
public class GameObjectsArray : MonoBehaviour {
public static GameObjectsArray Instance { get; set; }
public GameObject[] Objects;
public int i=0;
// Use this for initialization
void Start()
{
InstiatingMethod();
//Instantiate(Objects[i]);
}
public void InstiatingMethod()
{
Instantiate(Objects[i]);
}
}
Scripts 2:
public class CheckDestroy : MonoBehaviour {
//public GameObject[] Objects;
//int i;
// Use this for initialization
void Start () {
Debug.Log("executed");
//Objects = GameObject.FindGameObjectsWithTag("Player");
//OnMouseDown();
//Instantiate(Objects[i], transform.position, transform.rotation);
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
BoxCollider boxCollider = hit.collider as BoxCollider;
if (boxCollider != null)
{
GameObjectsArray.Instance.i++;
GameObjectsArray.Instance.InstiatingMethod();
Destroy(boxCollider.gameObject);
}
}
}
}
}
So I created a very quick project to make a good response:
Scene Image
In the scene, we will have an empty game object that will contain our script that I called "GameManager", basically this script will do everything, it's more logic to put your logic in one script.
public GameObject[] GameObjects;
private int _targetIndex = -1;
private RaycastHit _hit;
private void Start()
{
InstantiateNextGameObject();
}
public void InstantiateNextGameObject()
{
//if the index is pointing at the last game object in the array, init the index to -1
if (_targetIndex == GameObjects.Length - 1)
_targetIndex = -1;
Instantiate(GameObjects[++_targetIndex]);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out _hit))
{
BoxCollider boxCollider = _hit.collider as BoxCollider;
if (boxCollider != null)
{
InstantiateNextGameObject();
Destroy(boxCollider.gameObject);
}
}
}
}
we will have an array of gameobjects following with the targetIndex which will start from -1.
The function InstantiateNextGameObject() will simply increment targetIndex and then instantiate a gameobject from the array (++targetIndex the first time will be 0, second time 1 etc). We have to check also if the targetIndex reaches the end of the array, put it back to -1.
Then basically what you did in the update, when you click on a gameobject, instantiate the next one and destroy the current.
At the end, you will get something like that:
https://ayoub-gharbi.org/youba_docs/stackoverflow/stackoverflow01.mp4
Feel free to ask me if you didn't understand anything:)
Happy coding!
I seem to be stuck in a catch 22 situation with the OnInspectorGUI method of Unity's UnityEditor class. I want to name array elements in the inspector for easy editing, currently I'm using, as per the documentation:
public override void OnInspectorGUI()
{
J_Character charScript = (J_Character)target;
charScript.aBaseStats[0] = EditorGUILayout.FloatField("Base Health", charScript.aBaseStats[0]);
}
In my J_Character script I initialise the aBaseStats array like so:
public float[] aBaseStats = new float[35];
The problem is that whenever I try to do anything in the editor (and thus OnInspectorGUI is called) I get an index out of range error pointing to the line
charScript.aBaseStats[0] = EditorGUILayout.FloatField("Base Health", charScript.aBaseStats[0]);
I'm guessing this is because my array is initialized on game start while the editor code is running all the time while developing.
How can I get round this situation?
Many thanks.
You have to initialize aBaseStats in an function that runs only once.
The code below is BAD:
public float[] aBaseStats = new float[35];
void Start(){
}
The code below is GOOD:
public float[] aBaseStats;
void Start(){
aBaseStats = new float[35];
}
Initialize it in an Editor callback function that runs once.
EDIT:
I don't know a Start callback function that will run before the OnInspectorGUI function(). The hack below should work.
public float[] aBaseStats;
bool initialized = false;
public override void OnInspectorGUI()
{
if (!initialized)
{
initialized = true;
aBaseStats = new float[35];
}
J_Character charScript = (J_Character)target;
charScript.aBaseStats[0] = EditorGUILayout.FloatField("Base Health",aBaseStats[0]);
}
As an addition to the answer by Programmer I would like to point you to the following:
http://docs.unity3d.com/ScriptReference/ExecuteInEditMode.html
This seems to be exactly what you are looking for in terms of functionality. (it runs the method even when playmode is not active)
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class ExampleClass : MonoBehaviour {
public Transform target;
void Update() {
if (target)
transform.LookAt(target);
}
}
I have 3 kinds of GameObject, namely blueBook, redBook and greenBook. There are 2 blueBook, 7 redBook and 4 greenBook in the scene.
They are each assigned with the following example script that have 3 properties.
public class blueBook : MonoBehaviour {
public string type = "BlueBook";
public string colour = "Blue";
public float weight;
float s;
void Start () {
float weightValue;
weightValue = Random.value;
weight = Mathf.RoundToInt (700*weightValue+300);
s=weight/1000; //s is scale ratio
transform.localScale += new Vector3(s,s,s);
}
// Update is called once per frame
void Update () {
}
}
In the new class, I want to take all the variables of the GameObject (type, colour, weight) and store them inside an array. How to do this?
After they are all stored inside the array, user will input an weight. Then another class will search through all the info to delete both the array and GameObject(in the scene) with the same amount of weight.
Thank you.
UPDATE
blueBook.cs
public class blueBook: MonoBehaviour {
public string type = "blueBook";
public string colour = "Red";
public float weight;
float s;
void Start () {
float weightValue;
weightValue = Random.value;
weight = Mathf.RoundToInt (500*weightValue+100);
s=weight/1000; //s is scale ratio
transform.localScale += new Vector3(s,s,s);
Debug.Log(weight);
}
// Update is called once per frame
void Update () {
}
}
block.cs
public class block: MonoBehaviour{
public List<GameObject> players;
void Start()
{ Debug.Log(players[1].GetComponent<blueBoook>().weight);
}
// Update is called once per frame
void Update () {
}
The debug.log for block.cs displayed 0 everytime eventho it display otherwise in debug.log of bluebook.cs. It is because it displayed the initial number? I don know wat is wrong
For all blocks in one list you can create a script that has a public list and you drag all your gameobjects into the list in the inspector.
using System.Collections.Generic;
public class ObjectHolder : MonoBehaviour
{
public List<GameObject> theBooks;
// You can remove by weight e.g. like this
public void DeleteByWeight(float inputWeight)
{
for(int i = theBooks.Count - 1; i >= 0; i--)
{
if(theBooks[i].GetComponent<Book>().weight == inputWeight)
Destroy(theBooks[i]);
theBooks.RemoveAt(i);
}
}
}
}
The script on the blocks needs to be renamed to the same name for all (Book in my example). From your code that is no problem since they only differ in the value of the members.
Whats the best way to save variables like userid that is stored and reachable from different pages in WP7.
There's the querystring method, but can be kind of a pain to implement.
When navigating, pass the parameter like a HTTP querystring.
Then, on the otherside, check if the key exists, and extract the value. The downside of this is if you need to do more than 1, you need to type it in yourself, and it only supports strings.
So to pass an integer, you'd need to convert it. (And to pass a complex object, you need to take all the pieces you need to recompile it on the other side)
NavigationService.Navigate(new Uri("/PanoramaPage1.xaml?selected=item2", UriKind.Relative));
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string selected = String.Empty;
//check to see if the selected parameter was passed.
if (NavigationContext.QueryString.ContainsKey("selected"))
{
//get the selected parameter off the query string from MainPage.
selected = NavigationContext.QueryString["selected"];
}
//did the querystring indicate we should go to item2 instead of item1?
if (selected == "item2")
{
//item2 is the second item, but 0 indexed.
myPanorama.DefaultItem = myPanorama.Items[1];
}
base.OnNavigatedTo(e);
}
Here's a sample app that uses a querystring.
http://dl.dropbox.com/u/129101/Panorama_querystring.zip
A easier (and better) idea is to define a variable globally, or use a static class. In App.xaml.cs, define
using System.Collections.Generic;
public static Dictionary<string,object> PageContext = new Dictionary<string,object>;
Then, on the first page, simply do
MyComplexObject obj;
int four = 4;
...
App.PageContext.Add("mycomplexobj",obj);
App.PageContext.Add("four",four);
Then, on the new page, simply do
MyComplexObj obj = App.PageContext["mycomplexobj"] as MyComplexObj;
int four = (int)App.PageContext["four"];
To be safe, you should probably check if the object exists:
if (App.PageContext.ContainsKey("four"))
int four = (int)App.PageContext["four"];
You may use an App level variable (defined in App.xaml.cs) and access it from anywhere within your app. If you want to persist, shove it into Isolated Storage and read it on App launch/activate. There are helpers available to JSon serialize/deserialize your reads/writes from the Isolated Storage.
Check out Jeff's post (here) on tips to use Isolated Storage.
Hope this helps!
Well "best" is always subjective, however, I think an application service is a good candidate for this sort of thing:-
public interface IPhoneApplicationService : IApplicationService
{
string Name {get; set;}
object Deactivating();
void Activating(object state);
}
public class AuthenticationService : IPhoneApplicationService
{
public static AuthenticationService Current {get; private set; }
public void StartService(ApplicationServiceContext context)
{
Current = this;
}
public void StopService()
{
Current = null;
}
public string Name {get; set;}
public object Deactivating()
{
// Return an serialisable object such as a Dictionary if necessary.
return UserID;
}
public void Activating(object state)
{
UserID = (int)state;
}
public int UserID { get; private set; }
public void Logon(string username, string password)
{
// Code here that eventually assigns to UserID.
}
}
You place an instance of this in your App.xaml:-
<Application.ApplicationLifetimeObjects>
<!--Required object that handles lifetime events for the application-->
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"/>
<local:AuthenticationService Name="AuthServ" />
</Application.ApplicationLifetimeObjects>
Now you do need to tweak the App.xaml.cs:-
private void Application_Activated(object sender, ActivatedEventArgs e)
{
var state = PhoneApplicationService.Current.State;
foreach (var service in ApplicationLifetimeObjects.OfType<IPhoneApplicationService>())
{
if (state.ContainsKey(service.Name))
{
service.Activating(state[service.Name]);
}
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
var state = PhoneApplicationService.Current.State;
foreach (var service in ApplicationLifetimeObjects.OfType<IPhoneApplicationService>())
{
if (state.ContainsKey(service.Name))
{
state[service.Name] = service.Deactivating();
}
else
{
state.Add(service.Name, service.Deactivating());
}
}
}
You can now access you UserID anywhere in your app with:-
AuthenticationService.Current.UserID
This general pattern can be used to maintain seperation of key application wide services (you don't load a whole bunch of incohesive properties into your App class). It also provides the hooks for maintaining state between activations which is essential.