How to Destroy multiple gameObject at runtime? - c#

In unity3D I am creating and destroying capsule dynamically at run-time. I used space to create capsule and C for destroying.
I want create multiple object and destroy multiple object at time. When I pressed Space multiple times object is creating multiple time it fine.
But the problem is when I pressed C multiple times only one object is destroying. How I can achieve destroying multiple object? One by one.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DynamicCreate : MonoBehaviour
{
public GameObject caps;
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown(KeyCode.Space))
{
createObject();
}
if (Input.GetKeyDown(KeyCode.C))
{
destroyObject();
}
}
private void createObject()
{
caps = GameObject.CreatePrimitive(PrimitiveType.Capsule);
}
public void destroyObject()
{
Destroy(caps);
}
}

You can simply create a list of capsules.
Declare a list of capsule like under:
List<GameObject> Capsules;
void Start()
{
Capsules=new List<GameObject>();
}
Add any created capsule to the list:
private void createObject()
{
caps = GameObject.CreatePrimitive(PrimitiveType.Capsule);
Capsules.Add(caps);
}
You can then destroy the capsules in the list:
public void destroyObject()
{
foreach(GameObject capsule in Capsules)
{
Destroy(capsule);
}
}
See if that helps.

I would like to add more to bolkay answer. If you want to add and remove one by one best datastructure to use is a queue.
Queue<GameObject> Capsules;
void Start()
{
Capsules = new Queue<GameObject>();
}
private void createObject()
{
GameObject caps = GameObject.CreatePrimitive(PrimitiveType.Capsule);
Capsules.Enqueue(caps);
}
public void destroyObject()
{
if(Capsules.Count > 0)
Destroy(Capsules.Dequeue());
}
Queue data structure will give a first in first out. So it will give out first gameobject which you added to the queue at runtime and you wont need a variable to manage the index as in the state of List.
Note: This solution is still very basic but I would recommend you to use object pooling instead of creating and destroying objects at runtime as creating and destorying objects is quite intensive at runtime. So you should unless abosolutely necessary avoid creating and deleting.
Queue<GameObject> pooledObjects;
Queue<GameObject> Capsules;
void Start()
{
pooledObjects = new Queue<GameObject>();
Capsules = new Queue<GameObject>();
}
private void createObject()
{
if(pooledObjects.Count > 0)
{
GameObject fetchedObject = pooledObjects.Dequeue();
fetchedObject.SetActive(true);
//Do translations,scaling or other stuff on this reference
//Add it back to main list of things present in scene.
Capsules.Enqueue(fetchedObject);
} else
{
//Only create objects if necessary
GameObject caps = GameObject.CreatePrimitive(PrimitiveType.Capsule);
Capsules.Enqueue(caps);
}
}
public void destroyObject()
{
//here instead of destorying the object we will just disable them and keep them out if scene so we dont have to recreate them everytime
if(Capsules.Count > 0)
{
GameObject fetchedObject = Capsules.Dequeue();
fetchedObject.SetActive(false);//Turn the object off from scene
pooledObjects.Equals(fetchedObject);
}
}
Here is list of supported datastructures in C# you might want to use depending on your required case.

Related

Grab objects in Unity like half life 2

I am doing a little, but I do not really understand the scripts. I want grab objects ingame like portal or half life 2 and I was trying to code a little script for that action. I made this:
using System.Collections.Generic;
using UnityEngine;
using System;
public class PIckUp : MonoBehaviour {
public Transform theDest;
void OnKeyDown()
{
GetComponent<Rigidbody>().useGravity = false;
this.transform.position = theDest.position;
this.transform.parent = GameObject.Find("Destination").transform;
}
void OnKeyUp()
{
this.transform.parent = null;
GetComponent<Rigidbody>().useGravity = true;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
OnKeyDown();
}
if (Input.GetKeyUp(KeyCode.F))
{
OnKeyUp();
}
}
}
I attach this script to empty object called Destination, when I add a component in grabbable object in "theDest" I target Destination. If i press F all objects are flying i no need aim to them and deform all boxes too i leave here a picture.
I dont find a easy solution, i follow this scripts but are to complex for me and i dont understand how use them or how attach them to a player or object.
https://answers.unity.com/questions/1459773/picking-upholding-objects-portal-style.html
Well the thing is each and every object in the entire scene with this script attached will react to the key press.
I would rather
put all your objects you want to be able to pick on a certain Layer e.g. "Pickable"
make sure they all have colliders
Never parent Rigidbody, rather use Joints e.g. ConfigurableJoint could be useful here, you can completely customize it and use e.g. it's connectedAnchor in order to force the object to move towards you
have a kinematic rigidbody on "theDest" so we can use it as anchor for the joint
have a collider on "theDest" so that we can check distances between colliders
Additional to the layer I would also use a dedicated script like
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(SpringJoint))]
public class Pickable : MonoBehaviour
{
[SerializeField] private Rigidbody rigidbody;
public Rigidbody Rigidbody => rigidbody;
private void Awake()
{
if(!rigidbody && !TryGetComponnet<Rigidbody>(out rigidbody))
{
Debug.LogError("This component requires a Rigidbody!", this);
}
}
public void MarkActive(bool active)
{
// Your customerhod for marking an object the currently active one
// e.g. change its color, add outline etc
}
}
This fulfills three purposes
allows to check if hit object even is pickable
precached components so we don't need GetComponent over and over again
can implement additional behavior such as the mark active visualization
And then have only one single script on your player and use e.g.
public class PIckUpController : MonoBehaviour
{
// Reference this via the Inspector
[SerializeField] private Rigidbody theDest;
// Adjust this in the Inspector, select your Layer(s) that should be Pickable
[SerializeField] private LayerMask pickableLayer;
// Maximum range for picking objects in units
// only Pickable objects within this range can be grabbed
[SerializeField] private float range = 1;
// How fast to attract picked objects
[SerializeField] private float dragSpeed = 1;
private Pickable currentHit;
private Pickable currentPicked;
private ConfigurableJoint currentJoint;
private void Awake ()
{
if(!theDest) theDest = GameObject.Find("Destination"). GetComponent<Rigidbody>();
}
void PickUp()
{
currentJoint = currentHit.AddComponent<ConfigurableJoint>();
// Todo Completely customizable behavior
currentPicked = currentHit;
}
void Release()
{
Destroy(currentJoint);
// TODO optional give it some impulse to throw it away here e.g.
// (Have to tweak the values of course)
currentPicked.Rigidbody.AddExplosionForce(10, theDest.position, 1f, 0.5f, ForceMode.Impulse));
currentPicked = null;
}
private void Update()
{
// Have we already picked up something?
if(!currentPicked)
{
// No -> we chekc if we are pointing on any object
// within the range
var ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, out var hit, range, pickableLayer)
{
// yes -> get the Pickable component
if(hit.gameObject.TryGetComponent<Pickable>(out var hitPickable))
{
// Is it a different one than the one we already have?
if(hitPickable != currentHit)
{
// yes -> deselect the current one
if(currentHit)
{
currentHit.MarkActive(false);
}
// And store and select the new one
currentHit = hitPickable;
currentHit.MarkActive(true);
}
}
else
{
// If there is no Pickable component we hit something else
// -> deselect and forget the current hit
if(currentHit)
{
currentHit.MarkActive(false);
currentHit = null;
}
}
}
else
{
// We don't hit anything
// -> deselect and forget the current hit
if(currentHit)
{
currentHit.MarkActive(false);
currentHit = null;
}
}
// Do we have a current hit?
if(currentHit)
{
// yes and pressing F -> pick it up
if (Input.GetKeyDown(KeyCode.F))
{
PickUp();
}
}
}
else
{
// We already have picked up something
// of release F -> release object
if (Input.GetKeyUp(KeyCode.F))
{
Release();
}
else
{
// Todo e.g. make the object move towards the "theDest"
}
}
}
}
This one is the main responsible and does
shoot a Raycast to check which object you are pointing at
can additionally (optional) store and mark the currently hit object (as far as I remember HL2 marks them with an outline)
controls the pickup and release of objects

Unity2D: Why won't a variable from a different script update in this script?

Here's the first script which is just for the gems to be "collected".
using UnityEngine;
public class GemDestructionScript : MonoBehaviour
{
public int gemsCollected = 0;
void Awake()
{
}
void Update()
{
}
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag.Equals("Player1"))
{
gemsCollected++;
GetComponent<SpriteRenderer>().enabled = false;
GetComponent<CircleCollider2D>().enabled = false;
}
}
}
This is the 2nd script which is for the gemsCollected to be displayed on the canvas.
using UnityEngine;
using UnityEngine.UI;
using System;
public class GemCounter : GemDestructionScript
{
private Canvas canvas;
void Start()
{
}
void Update()
{
Transform child = transform.Find("Text");
Text t = child.GetComponent<Text>();
t.text = Convert.ToString(gemsCollected);
}
}
The problem is that the gemsCollected variable doesn't update on the canvas, it just stays at 0 the whole time.
Thanks in advance.
I fail to see the need for GemCounter to inherit from GemDestruction. Inheritance is more commonly used when having different Categories build upon each other.
A good, simple example of this is the Fruit example from Unity
In your case I would recommend using 2 separate Classes that are not linked via Inheritance. This will save you the headache of properly accessing whatever Data you need, unless it's really important for you to use Inheritance.
When you've split the classes and moved your gemsCollected to the Counter, instead of on the Gems themselves, you can just have the Gems "report in" when they are collected. This way the Counter can keep track of how many Gems have been collected and displays them on the UI. Every Object should have their job, and only use things that are relevant for that Job.
Example:
public class GemDestructionScript : MonoBehaviour
{
GemCounter counter;
private void Start()
{
// Find where-ever you keep the GemCounter Script (the Canvas in your case)
counter = GameObject.Find("Canvas").GetComponent<GemCounter>();
}
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag.Equals("Player1"))
{
counter.AddGem();
GetComponent<SpriteRenderer>().enabled = false;
GetComponent<CircleCollider2D>().enabled = false;
}
}
}
public class GemCounter : MonoBehaviour
{
public int gemsCollected = 0;
Text GemCounterText;
private void Start()
{
// Finding the Text Object in every Frame would be inefficient, so unless that object is constantly changing, just get it once during Start/Awake/before using it
GemCounterText = GameObject.Find("Text").GetComponent<Text>();
}
public void UpdateGemCount()
{
GemCounterText.text = gemsCollected.ToString();
}
public void AddGem()
{
gemsCollected++;
UpdateGemCount();
}
}

MissingReferenceException after scene reload

I just want reload a scene after GameOver. I have searched on the net but it didn't give me a clear reason and solution. Can someone point me the list of reason for this. I have posted below code for my scene reload.
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
Then my code for GameManager
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
DontDestroyOnLoad(this);
}
}
Kindly inform me if you need additional info. Thanks
Hi from what I saw on your code you might be missing some stuff on your sigleton code for the game manager, for example not having multiple instances for the game manager (each time you reload a scene you're making a new one and not destroying it on load).
withouth seeing all your scene I can't find out the why but I did some simple code to reload a scene and keep one scene manager only.
using UnityEngine;
using UnityEngine.SceneManagement;
public class GMScript : MonoBehaviour
{
private static GMScript instance;
private void Awake()
{
if (instance == null || instance == this)
{
instance = this;
}
else
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this);
Debug.Log("Scene reloaded");
}
public void ReloadScene()
{
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
ReloadScene();
}
}
}
Also make sure the scene is added to project settings, I'm using space key for reloading but you can put out any method fits your needs.
Also make sure all your objects that will need access of the gamemanager update their reference once the scene loads(preferably on start method, as we use awake for the singleton handle), this can be done using a find method like GameObject.FindWithTag.
Found the problem I have this code
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
DontDestroyOnLoad(this);
}
void Start()
{
shapeSpawnerGO = GameObject.Find("SpawnShapesObj");
scoreGO = GameObject.Find("ScoreText");
lifeGo = GameObject.Find("LifeText");
}
}
Then after reload scene the reference for this 3 becomes null it has something to do DontDestroyOnLoad(this); which means the GameManager script won't be destroyed after reload so when the game reloads the GameManager stays while it's reference get's destroyed. I'll check for efficient solution on this one

Unity image.enabled=false does not work

I tried both setting enabled to true and SetActive(true) to diplay a Gameover Image. However, none of them works. I have a public Image gameOverImage declared and set the gameOverImage.enabled in the Start() to false.
private void Start()
{
gameOverImage.enabled = false;
}
Then in one of my function, I put:
public void killAt(Vector2 loc)
{
foreach (GameObject obj in setup.GetActive())
{
if (obj.GetComponent<PieceInfo>().GetLocation() == loc)
{
if (obj.GetComponent<PieceInfo>().GetPieceType() == 'G')
{
gameOver = true;
gameOverImage.enabled = true;
Debug.Log("?????");
}
setup.deactivate(obj);
break;
}
}
}
The console does have ????? logged but no gameOverImage displayed in the game view. The game is over because I couldn't click my game any more. Any ideas? I also tried UI text. It doesn't work as well.
In Unity in order to activate an object you need to have it in the scene. If the GameObject does not exist in the scene, or in your case the UI Element that contains the Image, is not in your scene SetActive(true) or Enabled = true will not have any effect. You will need to instantiate the object. To make it exist in your world.
Prefabs, are useful to store a common configuration that can be used multiple times in your game but they do not exist in the scene, that is why you have to instantiate them.
For your GameOver Image you have a few options the simplest is this:
using UnityEngine;
using UnityEngine.UI;
public class EnableUI : MonoBehaviour {
public Image GameOverImage;
// Use this for initialization
void Start () {
GameOverImage.gameObject.SetActive(false);
}
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
GameOverImage.gameObject.SetActive(true);
}
}
}
If you want to instantiate it:
using UnityEngine;
public class EnableUI : MonoBehaviour {
// This is a prefab that is canvas with your game over image nested as a child image UI under it
public GameObject GameOverObject;
// Use this for initialization
void Start () { }
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(GameOverObject);
}
}
}

Unity 5: Clean way to manage dynamically created GameObjects

In Unity 5, what is "clean" way to manage dynamically created game objects?
I've written a component (MonoBehavior) that creates/destroys several GameObjects. The objects are loaded as part of custom customization system that selects portions of character - hair/clothes, etc. Meaning they're visible to player, visible in editor, but are not supposed to be editable in editor. The objects being loaded are meshes with skeletons.
The script behaves in this fashion:
Loads GameObjects from Resources (exact object is determined in script, they are not prefabs)
Attaches them to some portion of the scene (not necessarily to its own node)
Deletes when destroyed.
Deletion:
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
}
Creation:
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
Destruction handler:
void OnDestroy(){
unloadLastResource();
}
That seems to work fine in editor, but when I switch from gaming mode back to edito mode, I get a lot of warnings:
Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.
UnityEngine.Object:DestroyImmediate(Object)
And I get bunch of new object trees (the ones that are supposed to be deleted - trees originate from object that was loaded along with original "resources" and was attached) at the top level of scene hieararchy.
So, how do I cleanly handle dynamically created gameobjects?
I need to know which flags I need to set, and which steps I should do to ensure that object does not "leak" into scene and is properly destroyed when I delete component that created it.
Advice?
Full base class used by "ResourceLoader"
public class BaseResourceLoader : MonoBehaviour {
public GameObject targetNode = null;
protected GameObject lastTargetNode{
get{return lastTargetNodeInternal;}
}
private GameObject lastTargetNodeInternal = null;
protected bool targetNodeChanged(){
return targetNode != lastTargetNode;
}
protected string lastResourceName{
get{return lastResourceNameInternal;}
}
private string lastResourceNameInternal = "";
//private Object lastResource;
private GameObject lastResourceInstance;
protected GameObject getEffectiveTargetNode(){
if (targetNode == null)
return this.gameObject;
return targetNode;
}
public void reloadResource(){
loadNewResource(lastResourceNameInternal, true);
}
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
lastResourceNameInternal = "";
}
protected void loadNewResource(string newResourceName, bool forceReload){
if ((newResourceName == lastResourceNameInternal) && !forceReload)
return;
GameObject instance = null;
if (newResourceName != ""){
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
}
unloadLastResource ();
lastResourceInstance = instance;
lastResourceNameInternal = newResourceName;
lastTargetNodeInternal = targetNode;
}
void OnDestroy(){
unloadLastResource();
}
}
I've figured it out.
The problem is caused by this line:
instance.hideFlags = HideFlags.HideAndDontSave;
In hierarchy, this flag affects only ONE object and does not affect object's children. As a result, if you set this flag for the root object in hierarchy and scene gets saved, root will be destroyed, but its children will get serialized (meaning they'll appear in inspector upon scene reload) AND you'll get tons of warnings about destroying same object many times.
To deal with the problem, it is necessary to walk through the entire hierarchy and set the flag to all objects in hierarchy. Which fixes the problem and eliminates all errors.
void makeHierarchyHidden(GameObject obj){
obj.gameObject.hideFlags = HideFlags.HideAndDontSave;
foreach(Transform child in obj.transform){
makeHierarchyHidden(child.gameObject);
}
}
.....
makeHierarchyHidden (newObject);
.....
It is unclear if this only happens when loading non-prefabs from disk, or just in general case with all hierarchical objects.
The error message means that your code would create an infinite recursion, because you are destorying an object from within OnDestroy() which would - again - call OnDestroy() on this object and so forth...
Let me share this piece of code which I use as the base class for all my Behaviors:
using UnityEngine;
using System.Collections.Generic;
namespace de.softfun.drawntogether {
public class EnhancedBehavior : MonoBehaviour {
private List<GameObject> linkedObjects = new List<GameObject>();
protected void destroy(params GameObject[] objects) {
foreach (GameObject o in objects) {
try {
Destroy(o);
} catch {
continue;
}
}
}
public void LinkObjects(params GameObject[] objects) {
foreach (GameObject o in objects) {
linkedObjects.Add(o);
}
}
void OnDestroy() {
foreach (GameObject o in linkedObjects) {
destroy(o);
}
}
protected T instantiate<T>(T prefab, bool addLink = true) where T : Object {
T o = (T)Instantiate(prefab);
if (addLink && (o is GameObject)) {
linkedObjects.add(o);
}
return o;
}
}
}
The trick here is that I collect a List of GameObjects which are bound to this MonoBehaviour and should be destroyed if the 'parent' gets destroyed. When using instantiate of this class, the created object gets automatically added to the List, unless addLink is set to false in this method.
Maybe you can use this or something similar.

Categories

Resources