I hope you are doing well.
So I am creating a VR with XR game in which I have a list of items as Scriptable Objects. Now I want to put one item in my hand when I press a button but I run into problems because my function is not doing the right thing. This is how it looks like:
public void PutItemInHand()
{
for (int i = 0; i < inventoryItems.Count; i++)
{
if (inventoryItems[i].name == "Sword")
{
Instantiate(inventoryItems[i], handSpawn.position, handSpawn.rotation);
}
else if (inventoryItems[i].name == "Gun")
{
Instantiate(inventoryItems[i], handSpawn.position, handSpawn.rotation);
}
}
}
Also it says: "Cannot instantiate a ScriptableObject with a position and rotation".
Do you guys have an idea? I would be grateful for any help.
Kind regards
It is right, you can not instantiate a scriptable object with a position or rotation, simply because ScriptableObjects don't have components nor Transforms.
what you want to do is instantiate a prefab of your item, so you can do something like Instantiate(inventoryItems[i].itemPrefab, handSpawn.position, handSpawn.rotation);
Related
The project is made in Unity 2D.
I am working on a unity Project where I respawn my items, after the player ran through the level. When he does not have all the emeralds he will respawn at the start of the level and I want Items to respawn for him.
So for now I am using:
void OnCollisionEnter2D (Collision2D col)
{
if (col.gameObject.CompareTag("Life"))
{
col.gameObject.SetActive(false);
}
}
The Objects are inside an empty Object for clarification. I make them the child of the empty Object "Items".
if (ManagerVariables.IsRespawning)
{
if (item!= null)
{
for (int a = 0; a < item.transform.childCount; a++)
{
item.transform.GetChild(a).gameObject.SetActive(true);
}
}
else
{
Debug.LogError("No Objects Over Class defined");
}
I already checked for the variable IsRespawning and it
is set to true when the player respawns or dies, but the
objects do not reappear.
As well I want to ask about object with multiple child objects how to do that
e.g. Enemy with FirePoint attached
The code should be working generally.
My suggestion is to check whether item has a value. Maybe item is always null and your loop never gets executed.
If item is not null I would check the child count and make sure it is larger than 0.
Regarding your question with multiple child objects, you could either disable every child individually or disable the parent, then all the children also disabled.
I have a Unity script in c# where a public list of sprites is declared and then populated manually in the inspector.
The list consists of 19 sprites of cars.
These cars are available only if the player completed each level.
For example: When you open the game for the first time, you only have 1 available car but if you complete level 1, you unlock another car, when you complete level 2, you get another and so on.
My question is, being that I manually populated the list, how do I go about re-adding the same sprite back when a level is completed?
I was going to use cars.RemoveRange(1, 18) to remove them from the list but do not know a way to add them back without calling every sprite back manually. I believe there is a simpler, better way that I do not know about.
This is the script:
public class CarSelection : MonoBehaviour
{
public List<Sprite> cars; //store all your images in here at design time
public Image displayImage; //The current image thats visible
public Button nextCar; //Button to view next image
public Button previousCar; //Button to view previous image
private int i = 0; //Will control where in the array you are
void OnEnable()
{
//Register Button Events
nextCar.onClick.AddListener(() => NextCarButton());
previousCar.onClick.AddListener(() => PreviousCarButton());
}
private void Start()
{
cars.RemoveRange(1, 18);
if (FinishLineTouch.levelOneComplete == true) {
//Re-adding the same sprites here
}
}
public void NextCarButton()
{
if (i + 1 < cars.Count)
{
i++;
}
}
public void PreviousCarButton()
{
if (i - 1 >= 0)
{
i--;
}
}
void Update()
{
if (LevelSelect.isCarChosen == true) {
nextCar.interactable = false;
previousCar.interactable = false;
}
displayImage.sprite = cars[i];
}
}
Thanks in advance.
You have a small flaw in your logic here.
Saying that you add the car sprites in the inspector, removing all the sprites is not ideal.
You can have a list of all the sprites, and a list with available options (starts with sprite[0] and adds each time you complete the level).
However, if you want to know how to add them back after deleting them, you need to make a copy in a global variable, so it's more efficient to try what I am suggesting above.
You should have two different arrays, one for the sprites, and the other one for if the player unlocked it yet. Or you should create a custom class that has the sprite of the car, and if it is unlocked yet.
Sounds like all you really need to do is make sure the index doesn't exceed the unlocked cars
if(i + 1 < cars.Count && i + 1 <= amountOfFinishedLevels)
{
i++;
// and then you really should do this in both methods and not every frame
displayImage.sprite = cars[i];
}
and don't remove/readd any items at all.
I am making a video game, and while I have an existing code in generic C#, I now have to move it to Unity. I have some basic knowledge with generic C#, but I just started to learn the Unity way of coding.
For starters, I want to write a code that positions all game areas to correct positions, then turn them invisible. Yes, don't be surprised, they need to be all in same places.
Areas can have three size options, I called them Small, Medium and Large. Small and large areas have to be written manually.
List <GameObject> SmallAreas = new List<GameObject>();
void DefineSmallAreas()
{
SmallAreas.Add(areaConfirmLoad);
SmallAreas.Add(areaConfirmQuit);
SmallAreas.Add(areaConfirmSave);
SmallAreas.Add(areaGameSaved);
SmallAreas.Add(areaSave);
SmallAreas.Add(areaLoad);
}
Same with large areas.
Now, all other areas, are medium, and there is a large number of them.
So, I want to go through all game objects that are children of "areaContainer", check if their names start with "area", and if they do, I want to add them to MediumAreas list.
That's how I tried it:
void DefineMediumAreas()
{
GameObject areaContainer = GameObject.Find("areaContainer");
foreach (GameObject thisObject in areaContainer)
{
char[] a = thisObject.Name.ToCharArray();
if (a.Length >= 4)
{
char[] b = { a[0], a[1], a[2], a[3] };
string thisObjectType = new string(b);
(if (thisObjectType == "area")&&(!(SmallAreas.Contains(thisObject))
&&(!(LargeAreas.Contains(thisObject)))
{
MediumAreas.Add(thisObject);
}
}
}
This however shows an error, that "areaContainer" can't be used that way, I don't have access to Unity now, so can't copy exact message. I think that it's something like "Gameobject doesn't have IEnumerator".
I did try to google for the better approach, and found something called "transform".
foreach(Transform child in transform)
{
Something(child.gameObject);
}
What I don't understand, is how to use this "transform" in my specific situation.
Please don't get angry at me if this question is silly, I am very new to Unity, and have to learn it from scratch.
And a small second question. Will this work of turning object invisible work:
foreach(GameObject thisObject in MediumAreas)
{
thisObject.position = MediumVector;
thisObject.GetComponent<Renderer>().enabled = false;
}
MediumVector is location where the object must be moved to, and it seems to be working.
You can do this: foreach(Transform child in transform)
because the Transform class implements IEnumerable and have some mechanism that enables you to access the child GameObjects with the foreach loop.
Unfortunately, you can't do this: foreach (GameObject thisObject in areaContainer)
because areaContainer is a GameObject and this implementation is not done for the GameObject class. That's why you are getting this error:
foreach statement cannot operate on variables of type
'UnityEngine.GameObject' because 'UnityEngine.GameObject' does not
contain a public definition for 'GetEnumerator'
To fix it, change your loop to use Transform after finding the GameObject:
GameObject areaContainer = GameObject.Find("areaContainer");
foreach (Transform thisObject in areaContainer.transform){}
The complete code:
List<GameObject> MediumAreas = new List<GameObject>();
void DefineMediumAreas()
{
GameObject areaContainer = GameObject.Find("areaContainer");
foreach (Transform thisObject in areaContainer.transform)
{
//Check if it contains area
if (thisObject.name.StartsWith("area"))
{
//Add to MediumAreas List
MediumAreas.Add(thisObject.gameObject);
}
}
}
There is multiple approaches to your problem. One of them is using Tags. Simply mark your MediumArea prefab with some Tag and then you can find all tagged GameObjects with FindGameObjectsWithTag(string) (Unity Docs). Then you can populate your collection like that:
MediumAreas.AddRange(FindGameObjectsWithTag("MediumArea"));
Second approach could be finding all objects with same attached script FindObjectsOfType<T>() (Unity Docs). This is usefull when you are searching for instances of same type, like Medium Area.
Lets say that you have an Area script
public class Area : MonoBehaviour {
public AreaSize Size; // AreaSize is Enum
}
Then you can simply find your areas like:
var allAreas = FindGameObjectsOfType<Area>();
var mediumAreas = allAreas.Where(e => e.Size == AreaSize.Medium); // using System.Linq;
I created a project to answer your question, the final result will be like this :
As you can see I have created a game object called "areaContainer" and added 3 children with respective names : "area01", "area02" and "anotherObject".
The script that manage to get all "areaContainer" children that start with "area" looks like :
public GameObject areaContainer;
public List<GameObject> MediumAreas = new List<GameObject>();
private void Start()
{
DefineMediumAreas();
}
void DefineMediumAreas()
{
for (int i = 0; i < areaContainer.transform.childCount; i++)
{
var childGameObject = areaContainer.transform.GetChild(i).gameObject;
if (childGameObject.name.StartsWith("area"))
MediumAreas.Add(childGameObject);
}
}
1- I ended up referencing the areaContainer object in a script rather than using GameObject.Find because it's more performant.
2- To get a child of a game object you need to access to its transform and call GetChild(index). So by iterating through the parent container which is "areaContainer" we are getting its childCount.
3- To check if the name start with "area", simply use .StartsWith("area") which return true or false.
For your second question, you can hide object disabling the Renderer or by deactivating it (thisObject.SetActive(false);
I hope this help you; Happy coding!
You want to access all game objects that are children of "areaContainer"
In void DefineMediumAreas() function, you need to Transform[] to get an array of childeren. Use this:
Transform[] areaContainer = GameObject.Find("areaContainer").GetComponentsInChildren<Transform>();
foreach(Transform thisTransform in areaContainer)
{
...
}
I hope it helps you
So I've been trying to access game objects in my scene (which are disabled), to enable them. But I'm getting an error: "Object reference not set to an instance of an object"
private List<Character> characters = new List<Character>();
private List<GameObject> characterGameObjects = new List<GameObject> ();
public void loadSceneCharacters(){
if (characters != null) {
for(int i = 0; i < characters.Count; i++){
characterGameObjects.Add(GameObject.Find(characters[i].CharacterName));
characterGameObjects[i].SetActive(true);
}
}
}
You can't find disabled gameobjects.
A solution is to either reference them in inspector or find them all first when they are enabled, then disable those you don't need.
I think your characters list is empty.
If you don't Instantiate GameObjects you can fill characters list with drag and drop so you can change code like this.
public List<Character> characters = new List<Character>(); ///Thanks to public
//you can see list in Unity Editor
private List<GameObject> characterGameObjects = new List<GameObject> ();
public void loadSceneCharacters(){
if (characters != null) {
for(int i = 0; i < characters.Count; i++){
characterGameObjects.Add(characters[i])); //get and add gameobject
characterGameObjects[i].SetActive(true);
}
}
}
If you have dynamically created GameObjects you can fill the list with GameObject.Find("CharacterName");
However, i dont suggest that find every gameobject with name.Instead of that, During the Instantiate state you can add new gameObject instance to your character list.
Even if the character list would be empty this code would not throw an exception.
The problem is probably that you can not find disabled gameobject using the find methods (at least that was my experience, correct me if i am wrong guys).
What i usually do as a workaround instead of searching is to add the gameobjects via drag and drop. If this is not possible you can either, search for the gameobjects in Awake or Start, add them to your list and disable them. Or do some sort of adding when you instanciate them... Basicly you have to somehow get the reference to the gameobjects before you disable them.
Hope this helps.
Something that can help you is to create an empty object, then put all your characters inside the empty object.. then by code do something like:
foreach(Character c in MyEmptyObject.GetComponentsInChildren(Character, true))//the true in this
//line indicates that it should search for inactive objects
{
c.gameObject.SetActive(true);
}
this assuming that your characters have an script called "Character"
For example I have coin1(clone) and coin1(clone)(clone). If I click coin1(clone)(clone) it will be destroyed. But in my case, when I click coin1(clone)(clone), coin1(Clone) is deleted.
Codes I've been using
void NumberOfSelectedCoins()
{
Debug.Log (BlueCoinScript.b);
if(SelectedCoins.cntCoins%BlueCoinScript.b == 0)
{
for (int n=0;n<selectC.selectedNumCoins.Count; n++)
{
Destroy(selectC.selectedNumCoins[n]);
TotalSum.totalValue = 0;
SelectedCoins.breakPointsCount++;
}
breakpointsText.text = SelectedCoins.breakPointsCount.ToString ();
selectC.selectedNumCoins.Clear();
}
}
And also if you have 1 game object and you cloned it so that you can have 3 same game objects, how can you identify that when you clicked that game object, that game object is clone1 or clone2 or clone3?
You can for example identify GameObject instances by Object.GetInstanceID() or by Object.name. In the latter case though you have to take care that the object you search for is named and that this name is unique.
Further reference is found here.