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!
Related
I create a game in Unity. And I got this error:
System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource)
Here is my short code:
public class OnlineGame : MonoBehaviour
{
private List<GameObject> domino = new List<GameObject>(7);
public void Start()
{
StartGame();
}
private void StartGame()
{
for (int i = 0; i < 7; i++)
{
domino[i] = Instantiate(dominoPrefab, new Vector3(11, 0, 0), Quaternion.identity) as GameObject;
}
}
}
If you need more details, write a comment. Thanks for help
private List<GameObject> domino = new List<GameObject>(7)
new List<T>(int capacity) constructor doesn't mean the resulting list will have hold capacity objects at beginning. The Count of elements in the list will still be 0.
When you use List class in c#, it will have capacity memory reserved for the list. When you add some elements to the list and its Count reaches capacity(or close to it, I'm not exactly sure), the list will automatically increase capacity by allocating additional memory so that you can add additional element to the list.
Usually when you use new List<T>() the list will have initial capacity of 0 where program doesn't know how many Count the list can possibly have and will dynamically adjust capacity to match your need.
Using new List<T>(int capacity) is like telling the program that the list will have at most capacity number of elements and list should have that capacity ready to avoid overhead of allocating more memory. Of course, when list's Count reach capacity it will increase as well.
To fix your problem, use Add method instead of assigning to array slot.
public class OnlineGame : MonoBehaviour
{
private List<GameObject> domino = new List<GameObject>(7);
public void Start()
{
StartGame();
}
private void StartGame()
{
for (int i = 0; i < 7; i++)
{
// domino[i] = Instantiate(dominoPrefab, new Vector3(11, 0, 0), Quaternion.identity) as GameObject;
domino.Add(Instantiate(dominoPrefab, new Vector3(11, 0, 0), Quaternion.identity) as GameObject);
}
}
}
How do I restrict one random prefab to be used only once but placed randomly with a bunch of prefabs of arrays on top of other object?
using System.Collections.Generic;
using UnityEngine;
public class LevelRoomsScript : MonoBehaviour
{
[SerializeField]
private GameObject[] memberWoodArray = null;
[SerializeField]
private GameObject[] memberRoomPrefabArray = null;
void Start()
{
foreach (GameObject localWood in memberWoodArray)
{
int localNumRoomPrefab = memberRoomPrefabArray.Length;
int localRoomIndex = Random.Range(0, localNumRoomPrefab);
GameObject localRoomPrefab = memberRoomPrefabArray[localRoomIndex];
Instantiate(localRoomPrefab, localWood.transform.position, Quaternion.identity);
}
}
}
The way I understand your question is that you want to instantiate each element in memberRoomPrefabArray at most once.
You could create a temporary list that is a copy of memberRoomPrefabArray and remove each element that is instantiated before the next loop cycle.
void Start()
{
List<GameObject> temp = new List<GameObject>(memberRoomPrefabArray);
foreach (GameObject localWood in memberWoodArray)
{
int localRoomIndex = Random.Range(0, temp.Count);
Instantiate(temp[localRoomIndex], localWood.transform.position, Quaternion.identity);
temp.RemoveAt(localRoomIndex);
}
}
You might want to add some checks like if (temp.Count == 0) { break; } if it's possible for memberRoomPrefabArray to be shorter than memberWoodArray.
Edit: Changed Random.Range(0, temp.Count - 1) to Random.Range(0, temp.Count) since, apparently, it's only maximally inclusive with floats and not integers.
You rather want to "shuffle" the array once and then iterate the shuffled array e.g. using Linq OrderBy and using Random.value as order like
using System.Linq;
...
void Start()
{
if(memberRoomPrefabArray.Length < memberWoodArray.Length)
{
Debug.LogError($"Not enough prefabs available for {memberWoodArray.Length} unique spawns!", this);
return;
}
// as the method names suggest this will be a randomized array of the available prefabs
var shuffledPrefabs = memberRoomPrefabArray.OrderBy(m => Random.value).ToArray();
for (var i = 0; i < memberWoodArray.Length; i++)
{
// Since the array is already shuffled we can now go by consecutive order
Instantiate(suffledPrefabs[i], memberWoodArray[i].transform.position, Quaternion.identity);
}
}
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.
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.
I am developing a game and having problem in the following code.
for(intReps = 0; intReps <=9; intReps++)
{
final Path path = new Path(2).to(sprBalls[intReps].getX(), sprBalls[intReps].getY()).to(sprBalls[intReps].getX(), -50);
// sprBalls[intReps].registerEntityModifier(new LoopEntityModifier(new PathModifier(10, path, null, new IPathModifierListener() {
final Path path1 = new Path(2).to(fly[intReps].getX(), fly[intReps].getY()+10).to(fly[intReps].getX(), -50);
sprBalls[intReps].registerEntityModifier(new PathModifier(10, path, null, new IPathModifierListener() {
#Override
public void onPathStarted(PathModifier pPathModifier,
IEntity pEntity) {
// TODO Auto-generated method stub
}
#Override
public void onPathWaypointStarted(PathModifier pPathModifier,
IEntity pEntity, int pWaypointIndex) {
// TODO Auto-generated method stub
}
#Override
public void onPathWaypointFinished(PathModifier pPathModifier,
IEntity pEntity, int pWaypointIndex) {
// TODO Auto-generated method stub
}
#Override
public void onPathFinished(PathModifier pPathModifier,
IEntity pEntity) {
Log.e("Msg","intReps : "+intReps); // Output is 10
// TODO Auto-generated method stub
// mScene.detachChild(pEntity);
sprBalls[intReps].detachSelf(); // Error on this line.
// pEntity.detachSelf();
// sprBalls[intReps].dispose();
}
}, EaseSineInOut.getInstance()));
}
Array's length is 10. I get IndexOutOfBoundException on the line with error (sprBalls[intReps].detachSelf();)
I am running the loop from 0 to 9 but on printing the value of intReps it shows 10 that is why it generates the error. I don't understand how to clear this problem. I wan't to create an array of sprites with 10 sprites in it and want to move them from one end to other and upon path finished I want them to get cleared from memory.
You should remove your entities, using update thread:
/* Removing entities can only be done safely on the UpdateThread.
* Doing it while updating/drawing can
* cause an exception with a suddenly missing entity.
* Alternatively, there is a possibility to run the TouchEvents on the UpdateThread by default, by doing:
* engineOptions.getTouchOptions().setRunOnUpdateThread(true);
* when creating the Engine in onLoadEngine(); */
this.runOnUpdateThread(new Runnable() {
#Override
public void run() {
/* Now it is save to remove the entity! */
yourEntity.detachSelf()
}
});