public void PlaceIconToSlot() //gets called by a button
{
GameObject IconClone = Instantiate(Icons[properIconIndex], Slots[properSlotIndex].transform.position, Quaternion.identity);
}
Icons and Slots are Arrays. The first one tells the program what to instantiate and the second one where to instantiate. Quaternion.identity just means no rotation.
What I am trying to do: Duplicate an image and place it in a slot, then if another image is placed on top of the old one, the old one should be destroyed.
What is happening: Everything works, except that the old one doesn't get destroyed and the new one sits on top of the old. I mean of course it doesn't get destroyed since I didn't program that, but this is my question. How can I Destroy(OldClone) when there is only an IconClone? How can I introduce to the function the concept of an OldClone?
Since you call the function PlaceIconToSlot I'd guess, you might have a Slot component. If so, you can add a member variable to it that holds the current icon (assuming it's one icon per slot) and just work with that.
Something like this:
public class Slot
{
public GameObject Icon;
public void PlaceIconToSlot()
{
// If you overwrite it, the garbage collector will destroy it a some point anyways,
// but it doesn't hurt to do this destroy call
Destroy(Icon);
Icon = Instantiate(...);
}
}
Potentially pass the parameters (the new icon to instantiate) to this function if you above function at some centralized spot. Something like SpotXYZ.PlaceIcon(icon) or SpotXYZGameObject.GetComponent<Slot>().PlaceIcon(icon).
An idea would be to set a tag (let's say oldImage) to your original image. When you instantiate, destroy the object with that tag and then add the oldImage tag to the new image so that it will then be destroyed when another image is instantiated.
public void PlaceIconToSlot() //gets called by a button
{
GameObject IconClone = Instantiate(Icons[properIconIndex], Slots[properSlotIndex].transform.position, Quaternion.identity);
Destroy(GameObject.FindWithTag("oldImage"));
IconClone.gameObject.tag="oldImage";
}
I haven't tried this but it's worth a go!
Related
In my game, I declare a PlayerClass (warrior/rogue/etc) like so:
public PlayerClass playerClass;
On the first frame of the game (for now) I instantiate my player's PlayerClass like so:
playerClass = new WarriorClass(player, 100);
This works great. The WarriorClass lets my player use some abilities. However, now I'd like to make my player a rogue:
playerClass = new RogueClass(player, 100);
But for whatever reason, I now have two instances, not one. I push my ability button and it triggers both the Warrior and Rogue ability.
How do I wipe or remove the WarriorClass reference before assigning the new Rogue one?
What also happens if that I exit to the main menu and log into the game again, my player has two instances of the WarriorClass. I'm not sure how to proceed.
Ultimately, the source of my problem is that I don't understand why playerClass isn't getting overwritten each time like another variable would.
Try to remove all references of your player object in your PlayerClass.
Create a method called Destroy() in your PlayerClasses(WarriorClass, RogueClass) and call this method whenever you will create a new object.
public void Destroy(){
this.player = null;
//remove all other references, stop tasks-threads if exists.
}
Apologies for not giving enough info. I didn't understand my problem enough to know what to give. I had implemented an idea that I didn't realize was causing the bug. I was holding onto a list of Abilities and not clearing them on game start. Every game start would append more to it:
In player:
public List<Ability> ActiveAbilities = new List<Ability>();
Appending to that in each PlayerClass instantiation:
ability1 = new LeapAbility();
player.GetModPlayer<MyPlayer>().ActiveAbilities.Add(ability1);
At that point my keybinds would be triggering everything in the list. The fix in player:
ActiveAbilities.Clear();
playerClass = new TestClass(player, 100);
My Problem:
I cant get it to work, that every Building has its own SpawnPoint.
I'm working on an RTS like build mechanism. I have set it up that after pressing a key I can place buildings in the scene. The buildings get instantiated from a prefab and after they get instantiated they get a script called "BuildingScript" attached to them. Now i want to implement, so that i get a individual spawn point for every building (for now just next to it). I got a UI set up with a button, which by pressing spawns a unit at the building.
I had the "BuildingScript" attached to the prefab, but when i set the spawn point for one Building, it set it for every Building in the Scene. So a Unit was Spawned always at the same Buidling.
I want to set it up, that every Building has its own spawn point. Thats why I want to give every Building the script "BuildingScript" when Instatiated, because I hope, that this way every script gets handled individually. Is that right? Or will it still set the same point for every building, because the script is still the same?
Also I wanna reference the current placed building to the button, so when its clicked, it will run only the code of the last placed building (for now). I think I cant do this by using "On Click()" Of the Button, because my clone isnt Instatiated yet, so I have to reference the clone to the button somehow via Script, so the button works with the clone.So my problem is, that I need to set a reference from my cloned Building to the Button, after I placed the clone.
I googled a lot on how to do this, but didnt found any answers to my problem besides this https://forum.unity.com/threads/controlling-instantiated-game-objects-from-ui-buttons.332005/.
But I cant get it to work and I think it will not work because my clone is an Object and not a GameObject, so I could never set reference to it to call the funktion SpawnUnit(), because GetComponent only works for a GameObject.
Now I'm really at a point where I just don't know how Unity handles these kind of things.
BuildingScript on the Instantiated Building
public class BuildingScript : MonoBehaviour
{
public bool SavePos = false;
public Vector3 SpawnPoint;
public Vector3 BuildingPos;
public GameObject Unit;
void Start()
{
FindObjectOfType<SpawnButtonReference>().GiveReference(this);
}
public void SpawnUnit()
{
//I did this because if a building gets instatiated i wanted it to save its
Position to Spawn Units from it (doesnt really work though).
"MousePos" ist the last Position of the Mouse in the PlacementScript, before klicking to place the building.
if (SavePos == false)
{
BuildingPos = GameObject.FindObjectOfType<GroundPlacementController>().GetComponent<GroundPlacementController>().MousePos;
Debug.Log(BuildingPos);
SavePos = true;
}
float PosX = BuildingPos.x + 2;
float PosZ = BuildingPos.z + 2;
SpawnPoint = new Vector3(PosX, 0, PosZ);
Debug.Log("Spawn" + SpawnPoint);
Instantiate(Unit, SpawnPoint, Quaternion.identity);
}
}
Script on the Button
public class SpawnButtonReference : MonoBehaviour
{
public GameObject objectReference = null;
internal void GiveReference(BuildingScript Object)
{
objectReference = Object;
}
void OnButtonPress()
{
if (objectReference != null)
{
objectReference.GetComponent<BuildingScript>().SpawnUnit();
}
}
}
So I solved it myself with a little workaround. Instead of trying to reference the clone, I wrote a script on the Spawn Button which searches all Objects with the "BildingScript" then if they are Selected (which can only be one Building) it spawns a Unit at its Spawn point.
The Building itself saves his spawn point when being placed (so when Input.GetMouseButtonUp(0))
works very well for me :)
I have code that looks like this.
public static Dictionary<int, Action> functionsMap;
void Function()
{
if (!isDictionaryInitialized)
{
functionsMap = new Dictionary<int, Action>();
functionsMap.Add(1, () => StartCoroutine(Function1()));
functionsMap.Add(1, () => StartCoroutine(Function2()));
}
}
void CheckForFunction()
{
var r = currentFunctionNumber;
if (functionsMap.TryGetValue(r, out currentAction)) { currentAction(); }
}
The code works fine when I start my program. However if I go to another scene and then return to it, I get this error.
"MissingReferenceException: The object of type 'ScriptName' has been
destroyed but you are still trying to access it. Your script should
either check if it is null or you should not destroy the object."
The problem is I have never destroyed the object. Initially I didn't have bool isDictionaryInitialized and I defined the new Dictionary outside of the Function because I thought the error was related to my program trying to access a Dictionary that was deleted after the scene was closed. I get the same problem with or without the bool, and regardless of where I define the Dictionary.
What is causing this, and what is the reason so I can avoid making the same mistake?
Edit: This question was marked as duplicate, but the link I don't believe applies to my situation, or if it does I don't understand how. It says static objects are not reloaded on a scene change, and the Dictionary is defined as a static object. I also tried changing it to non-static and the result is the same.
I have dozens of gameobjects in my code and don't have this issue with any other object, so I assume the problem is related to how the dictionary object is defined. Is there a way to keep the Dictionary object from being destroyed on scene change? I don't have it as a game object in the scene, it's just defined in the code itself as a public static Dictionary. Could someone tell me what I need to do differently please and thank you?
The problem might be caused by changing scenes because simply loading another scene would destroy all gameobject in the current scene.
According to Object.DontDestroyOnLoad.
The load of a new Scene destroys all current Scene objects.
To solve this, you can either use DontDestroyOnLoad function to mark the object you want to be kept before loading another scene, or using different way to load like LoadSceneMode.Additive without destroying the current scene.
First you are adding two Actions to your Dictionary but both with the same key.
functionsMap.Add(1, () => StartCoroutine(Function1()));
functionsMap.Add(1, () => StartCoroutine(Function2()));
wouldn't this overwrite the first entry?
Than your general problem is:
While functionsMap is static the two actions / one action you added are non-static (StartCoroutine always requires an instance of MonoBehaviour to run on) and therefore only available for the according component! (You talked about an Update method so this has to be a MonoBehaviour attached to a GameObject in your first scene.)
Now if you change the Scene, the GameObject holding that component is probably destroyed if you are not using DontDestroyOnLoad
The result is that your static Dictionary stays intact and filled meaning the entry with key = 1 still exists but the Value namely the added non-static Action is no longer available.
To avoid that you should add
DontDestroyOnLoad(this.gameObject);
either in Awake or Start to prevent the GameObject holding the component and thereby holding the Action that belongs to the reference(s) in the entry in your Dictionary.
I have 2 different scripts attached to 2 different game objects.
One instantiates a bunch of game obejcts and I want the second one to destroy them.
code1:
public static volatile GameObject newObj;
public GameObject go;
newObj = GameObject.Instantiate(go);
newObj.tag = "mytag";
Code 2:
GameObject[] all_cloned_prefabs = GameObject.FindGameObjectsWithTag("mytag");
foreach (var AllPrefabs in all_cloned_prefabs)
{
Destroy(AllPrefabs);
}
When I run code 2 (let's say by pressing a button on GUI, it doesn't destroy the objects I created on the screen.What am I doing wrong?
It's hard to understand what you are trying to do in the first code with public static volatile GameObject newObj;, it's not used in the other part of the code you shown.
The second code looks fine, what could be wrong is:
The objects you want to destroy are inactive (FindGameObjectsWithTag will not return inactive GameObjects).
You didn't created the tag "mytag" in the Tag Manager.
Try to Debug.Log the content of the all_cloned_prefabs array to see if it actually contains anything.
I put all the objects inside a list, made the list public static volatile prefab1. accessed it from second script. then destroyed them
It worked perfect.
foreach (GameObject obj1 in script1.prefab1)
{
Destroy(obj1);
}
I'm trying to make a city builder, and I'm writing the part of the game that creates a transparent render of the building you're going to place before you actually place it. What I'd like to do is simply make a copy of the GameObject in code, modify the materials' transparency, remove some components, and then instantiate it.
There's a problem: pretty much everything in Unity is pass by reference. Is there a way to do what I want to do or is there no such way?
If you want to create a scene by combining several GameObjects instantiated on runtime, you can create a script called generateEnviroment.cs (just an example name), then you should pass one or several GameObjects as attributes to that script from the inspector.
Next step is to instantiate the correct gameObject and then modify its properties as you please (position, size, material...). Each instance will be independet, with its own attributes that you can modify as you want,and other instances of the same GameObject will not be altered.
public GameObject customGameObject1;
public GameObject customGameObject2;
void Start()
{
generateEnviroment()
}
void generateEnviroment()
{
//In case you want to add other type of GameObject, like a car or sth you have created:
GameObject myInstantiatedGameObject = GameObject.Instantiate (customGameObject1);
//You change its position
myInstantiatedGameObject.transform.position = new Vector3(0, 0.5F, 0);
// Widen the object by 0.1
myInstantiatedGameObject.transform.localScale += new Vector3(0.1F, 0, 0);
//Change material properties, assuming it has a material component
Renderer rend = myInstantiatedGameObject.GetComponent<Renderer>();
rend.material.shader = Shader.Find("Specular");
rend.material.SetColor("_SpecColor", Color.red);
...
}
You can even add to the GameObject a script component, and you will also be able to access and modify each attribute (variable) in that script independently for each instance of the GameObject (As long as you don't declare that attribute as static)
You dont have to create a copy of your gameobject. Just instantiate it and change values to it's temporary placement state. Like setting material properties, disabling child objects, etc. when the building is finally placed just reset the changes.
As everyone here already said, creating a copy of prefab or scene object is possible with GameObject.Instantiate. You cannot delay instanitiation of object (placing it on scene), but you can delay its startup calls (e.g. Start and Awake). You simply must copy an object that is not active, modify whatever you want and then activate it manually. Is that what you are looking for?
static GameObject InstanitateDelay(GameObject source)
{
bool isActive = source.activeSelf;
source.SetActive(false); // disable it temporarly
var go = Instantiate(source); // create inactive copy
source.SetActive(isActive); // return to previous state
return go; // remeber to activate manually via go.SetActive(true)
}
Instantiating is the way u duplicate an object in Unity.
If instantiating is to slow for you, just instantiate every object you need, when the scene loads and disable/enable them when you need them.
That way you can change Property before enabling and/or moving it to its final position.
The following call should make it transparent.
instantiatedObject.GetComponent<MeshRenderer>().material.color = new Color(0.0f, 0.0f, 0.0f, 0.0f);
Have look at the following 3 links:
https://docs.unity3d.com/Manual/InstantiatingPrefabs.html
https://unity3d.com/de/learn/tutorials/topics/scripting/instantiate
https://docs.unity3d.com/ScriptReference/Color.html