adding GameObject at runtime - c#

This may seem like a stupid question but I'm stuck with it. I have GameObjects in a list (List<GameObject>) and I want to add them on the scene runtime, prefarbly on predefined places (like placeholders or something). What would be a good way to do it? I've been searching the net but can't really find anything that would solve this. This is my code so far:
public static List<GameObject> imglist = new List<GameObject>();
private Vector3 newposition;
public static GameObject firstGO;
public GameObject frame1;//added line
void Start (){
newposition = transform.position;
firstGO = GameObject.Find ("pic1");
frame1 = GameObject.Find ("Placeholder1");//added line
//this happens when a button is pressed
imglist.Add(firstGO);
foreach(GameObject gos in imglist ){
if(gos != null){
print("List: " + gos.name);
try{
//Vector3 temp = new Vector3 (0f, 0f, -5f);
Vector3 temp = new Vector3( frame1.transform.position.x, frame1.transform.position.y, -1f);//added line
newposition = temp;
gos.transform.position += newposition;
print ("position: " + gos.transform.position);
}catch(System.NullReferenceException e){}
}
}
}
How can I place the pics (5) on the predefined spots?
//----------------
EDIT: Now I can place 1 image to a placeholder (transparent png). For some reason z-value goes all over the place so it needs to be forced to -1f but that's OK. I add the images to the list from other scenes and there can be 1-5 of them. Do I need to put the placeholders in another list or array? I'm a bit lost here.

If you've already created 5 new objects you can just do like they do here:
http://unity3d.com/learn/tutorials/modules/beginner/scripting/invoke under the InvokeScript
foreach(GameObject gos in imglist)
{
Instantiate(gos, new Vector3(0, 2, 0), Quaternion.identity);
}

I don't really understand what you're trying to do, but if I'm correct and you have a list of objects, and you know where you want to move them at runtime, just make two lists,
one containing the objects and
one containing transforms of empty game-objects in the scene placed at those predefined positions, and match them at runtime.
Populate both lists from the inspector.
public List<GameObject> imglist = new List<GameObject>();
public List<Transform> imgPositions = new List<Transform>();
void Start()
{
for(var i = 0 i < imglist.Count; ++i)
{
imglist[i].transform.position = imgPositions[i].position
}
}

The general best way is to create prefabs for your objects, passing them as a parameter and instantiate when needed (Start in your case). That's the common case, but maybe yours is slightly different.
This is an example of passing a prefabs array and to instantiate one object for each one in the array:
public GameObject prefabs[];
List<GameObject> objects = new List<GameObject>();
void Start() {
for(GameObject prefab in prefabs) {
GameObject go = Instantiate(prefab, Vector3.zero, Quaternion.identity) as GameObject; // Replace Vector3.zero by actual position
objects.Add(go); // Store objects to access them later: total enemies count, restart game, etc.
}
}
In case you need several instances for the same prefab (multiple enemies or items, for instance) just adapt code above.

Related

Unity - What is OOP - Null Reference Exception

im kind of newbie to unity and object oriented programming. Recently im trying to clone Cube Surfer mobile game. Basic idea from my view is this ;
-When we triggered to collactable cubes which consist script will be duplicated and it will be belong the main cube parent as child object then triggered cube will be destroyed.(After positioning)
-Later this duplicate child objects(cubes) will do the same when they enter trigger area of other collectable cubes(those will be the same prefab but did not yet create a prefab)
Im trying to collect(create a clone of it position and destroy the object) cubes. For first cube, I added some code to my movement script which is below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public GameObject addup;
Rigidbody rb;
float controlSpeed = 1.25f;
float forwardMovementSpeed = 10f;
private Vector3 axisGet;
float deathTime;
public int collected;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody>();
collected = 0;
}
// Update is called once per frame
void FixedUpdate()
{
axisGet = new Vector3(0, 0, Input.GetAxis("Horizontal"));
rb.MovePosition(transform.position + Vector3.left * forwardMovementSpeed * Time.deltaTime + axisGet * controlSpeed * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
if(other.tag=="add up")
{
gameObject.transform.position += Vector3.up;
var newObject = Instantiate(addup.gameObject, Vector3.zero, Quaternion.identity);
newObject.transform.parent = transform;
newObject.transform.position = gameObject.transform.position + Vector3.down;
Destroy(other.gameObject);
newObject.GetComponent<BoxCollider>().isTrigger = false;
collected++;
}
}
}
WORKED WITHOUT ERROR BUT THEN, I applied the same method to collectable cubes scripts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UpNdown : MonoBehaviour
{
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "add up")
{
GameObject parentTransform;//?????????
parentTransform = gameObject.GetComponentInParent<GameObject>(); //get first cube component
parentTransform.transform.position += Vector3.up; //first cube one unit up
GameObject newObject; // ?????????
newObject = Instantiate(other.gameObject, Vector3.zero, Quaternion.identity) as GameObject; //???????????
Debug.Log(newObject);
var collect = parentTransform.GetComponent<Movement>().collected;
if (other != null)
{
Destroy(other.gameObject); //destroy triggered collactable
}
newObject.transform.parent = parentTransform.transform; //setting parent to new cube
newObject.transform.position = parentTransform.transform.position + Vector3.down * (collect + 1); //setting position of new cube
newObject.GetComponent<BoxCollider>().isTrigger = false; //prepare the below cubes(new cubes) for trigger with other collactable cubes
collect++;
}
}
}
And, I had nullexception error in every line in ontriggerenter method then, I changed(added) the lines with question marks. So, I get
ArgumentException: GetComponent requires that the requested component 'GameObject' derives from MonoBehaviour or Component or is an interface.
UnityEngine.GameObject.GetComponentInParent[T] (System.Boolean includeInactive) (at :0)
UnityEngine.GameObject.GetComponentInParent[T] () (at :0)
UpNdown.OnTriggerEnter (UnityEngine.Collider other)
I thought, I understood the OOP instance idea which objects in the scenes are instances scripts has their own value... but i dont understand that while I was operating on a instance why it is null in the memory :((((((((((((( if PC can't access how instantiates the object ?
SORRY I WRITE THIS LONG BUT IM ABOUT THE EDGE AGAIN I DON'T WANT TO QUIT BECAUSE OF FACING THIS PROBLEM AGAIN
TY FOR YOUR ANSWERS, ALREADY APPRECIATED :)
GameObject is no component (it is rather a container of all components attached to it!)
=> you can't get it using GetComponent or GetComponentInParent at all. (Btw Note that GetComponentInParent starts the search on this object itself first before bubling up the hierarchy so either way this isn't what you want to use).
What you want is simply transform.parent to get the Transform component of the parent object of the object this script is attached to (assuming the rest of your code does what it should)
private void OnTriggerEnter(Collider other)
{
// Rather use CompareTag instead of string ==
// The latter silently fails in case of typos making your debugging life miserabel
// it is also slightly less efficient
if (!other.CompareTag("add up")) return;
// Get the parent of this object
var parentTransform = transform.parent;
// Cache this you will need it later see below
var parentMovement = parentTransform.GetComponent<Movement>();
var collect = parentMovement.collected;
parentTransform.position += Vector3.up;
// By using the correct type you want later you can skip GetComponent
var newObjectCollider = Instantiate(other, Vector3.zero, Quaternion.identity);
Debug.Log(newObjectCollider);
Destroy(other.gameObject);
newObjectCollider.transform.parent = parentTransform;
newObjectCollider.transform.position = parentTransform.position + Vector3.down * (collect + 1);
newObjectCollider.isTrigger = false;
// This does absolutely nothing. Numeric values are passed by value and there is no connection
// between your local variable and the component you got it from
//collect++;
// you probably rather want to increase
parentMovement.collected++;
}
Or alternatively since you anyway have a specific component on your parent object you could also do
// Instead directly get this component
var parentMovement = GetComponentInParent<Movement>();
// Then wherever needed access the transform through it
var parentTransform = parentMovement.transform;
...
I'm quite sure though that the other way round it is more efficient since you already know exactly which parent you are searching the component on.
Or - and this would probably be the best option - cache that parent information once right away:
// If possible already reference this vis the Inspector
[SerializeField] private Movement parentMovement;
private Transform parentTransform;
private void Awake ()
{
if(! parentMovement) parentMovement = GetComponentInParent<Movement>();
parentTransform = parentMovement.transform;
}
Ty sir my first code was nearly the same of your first answer but didn't work again at least for error.
private Transform parentTransform;
private void Awake ()
{
if(! parentMovement) parentMovement = GetComponentInParent<Movement>();
parentTransform = parentMovement.transform;
}
But this worked, I guess the problem I need to define instances to class so they don't disappear instantly on the trigger function or direct I need to define them to class.
Anyway, thank you derHugo now need to solve different problems :D

How to loop through unity gameobjects

I want to make a script that loops through all game objects with the tag, Enemy
I modified a code snippet from here, and ended up with 2 errors.
Cannot Implicitly convert 'Unity.GameObject[]' to 'Unity.GameObject'
and
Error CS1579 foreach statement cannot operate on variables of type 'GameObject' because 'GameObject' does not contain a public instance definition for 'GetEnumerator'
If anyone could tell me why this is happening or a solution to this I would be very grateful, thanks in advance!
Here is my code:
void FixedUpdate()
{
GameObject objects = GameObject.FindGameObjectsWithTag("Enemy");
var objectCount = objects.Length;
foreach (var obj in objects)
{
// Move the players accordingly
//var rb =
Vector2 direction = (player.position - transform.position).normalized;
obj.rigidbody.velocity = direction * moveSpeed;
}
}
FindGameObjectsWithTag as the name hints returns a GameObject[].
In order tog et the attached Rigidbody2D use GetComponent<Rigidbody2D>
It should be either GameObject[] or simply var
/*GameObject[]*/ var objects = GameObject.FindGameObjectsWithTag("Enemy");
var objectCount = objects.Length;
foreach (var obj in objects)
{
// Move the players accordingly
var rb = obj.GetComponent<Rigidbody2D>()
Vector2 direction = (player.position - transform.position).normalized;
rb.velocity = direction * moveSpeed;
}
The second one was just a follow up error since you declared objects as a GameObject which indeed as the error says has no GetEnumerator implementation.
In general it is not the best thing to use FindObjectsWithTag repeatedly. I would rather use a pattern with a static list of all existing instances like
// Put this component on your enemy prefabs / objects
public class EnemyController : MonoBehaviour
{
// every instance registers to and removes itself from here
private static readonly HashSet<EnemyController> _instances = new HashSet<EnemyController>();
// Readonly property, I would return a new HashSet so nobody on the outside can alter the content
public static HashSet<EnemyController> Instances => new HashSet<EnemyController>(_instances);
// If possible already drag the Rigidbody into this slot via the Inspector!
[SerializedField] private Rigidbody2D rb;
// public read-only access
public Rigidbody2D Rb => rb;
private void Awake()
{
if(!rb) rb = GetComponent<Rigidbody2D>();
_instances.Add(this);
}
private void OnDestroy()
{
_instances.Remove(this);
}
}
and then use
var enemies = EnemyControler.Instances;
foreach (var enemy in enemies)
{
// Move the players accordingly
Vector2 direction = (player.position - transform.position).normalized;
enemy.Rb.velocity = direction * moveSpeed;
}
Change GameObject type declaration to var
Or change it to GameObject[] since FindGameObjectsWithTag returns an array of GameObject
Also I wouldnt use FindGameObjectsWithTag its slow. More so in a update method
First off, change GameObject to GameObject[] because FindGameObjectsWithTag returns a GameObject array. Be careful not to confuse it with FindGameObjectWithTag without the s which returns a single GameObject
Secondly, I think it would be best to have a separate Enemy script or something like that and assign them to each enemy rather than using FindGameObjectsWithTag which is very slow.

How to add different gravity on spawning objects?

Im working on a project that I want to create a power up effect whenever the button "Q" is pressed, I have the animation working and the character, I also have the spawning objects around my player that I want to spawn (See Figure below)
My question is how to add different gravity on each rock (spawning object).
Here is the script that I'm currently using.
/* Public Variables Declaration */
public Transform spawn_LocationForSmall;
public Transform spawn_LocationForMedium;
public Transform spawn_LocationForLarge;
public GameObject smallRock_Prefab;
public GameObject mediumRock_Prefab;
public GameObject largeRock_Prefab;
/* Private Variables Declaration */
private GameObject[] smallRocks_List;
private float posX, posY, posZ;
private bool smallCount = false;
private bool mediumCount = false;
private bool largeCount = false;
private bool small_CheckPos = false;
private bool medium_CheckPos = false;
private bool large_CheckPos = false;
void Start() {
//smallRocks_List = GameObject.FindGameObjectsWithTag("smallRock");
Create_Small_Rocks();
Create_Medium_Rocks();
Create_Large_Rocks();
}
private void Create_Small_Rocks(){
for(int i=0; i<=20; i++){
small_CheckPos = false;
posX = this.transform.position.x + Random.Range(-3.0f, 3.0f);
posY = this.transform.position.y + Random.Range(-3.0f, 3.0f);
posZ = this.transform.position.z + Random.Range(-3.0f, 3.0f);
if(posX > 3f && posY > 3f){
small_CheckPos = true;
}
if (small_CheckPos == true) {
Vector3 newPos = new Vector3(posX, posY, posZ);
GameObject createdObject = GameObject.Instantiate(smallRock_Prefab,
newPos, spawn_LocationForSmall.rotation) as GameObject;
createdObject.transform.parent = spawn_LocationForSmall.transform;
}
}
smallCount = true;
}
/* the other two functions are similar to this */
I don't really know if you can change the gravity for each individual, but you can change these things:
Mass:
In the Rigidbody component, there is a "Mass" components at the top. As in the Unity Documentation says: "Higher mass objects push lower mass objects more when colliding. Think of a big truck, hitting a small car." However, it doesn't change how fast an object falls.
Physics Material:
In the Collider components, you should see something called "Material". You can create new physics materials and edit them randomly to make the friction between the rock and the surface higher or lower, and change the bounciness of rocks that way.
Constant Force:
If you want some objects to fall faster, you might want to use this component. I personally never used this before, but it looks great for your problem. You can add a constant force to an object with this component, so if you add some downwards force on your rocks it should help them get down faster.
Please let me know if any of these helped.
Search for Particle Systems :
1) https://docs.unity3d.com/ScriptReference/ParticleSystem.html
2) https://www.youtube.com/watch?v=FEA1wTMJAR0&t=536s
3) https://www.youtube.com/watch?v=xenW67bXTgM
It allows you to upload cool effects or even prefabs as the clone objects (in this case rocks/asteroids). Its also able to control the spawning speed/ amount/ velosity/ (random)size/ physics(gravity)

How to destroy GameObject & Instantiate new one after a while

I am creating a game, with-in the game there's 7 GameObject Prefabs, each one Instantiate it's on level, I am now creating the main script that will instantiate each GameObject Prefab, now what I want to do, is so create each 80f "seconds" to instantiate a new GameObject Prefab, and destroy the last one, for example, in 0f seconds since Level Started, Instantiate (Random) GameObject Prefab, and in 80f seconds destroy the one I created in 0f seconds, and Instantiate a new one, and so on.
This is the script that I have right now, it doesn't work, it does Instantiate new gameobject, but do not destroy the last one I created.
I hope you can help me / give me an idea how to solve this problem
NOTE I tried using Stack, but when I destroy Prefab from the stack, it destroyes the prefab it self, and it's not recoverable.my script:
TimeSinceLevelStarted = Time.timeSinceLevelLoad;
if (Mathf.Clamp(TimeSinceLevelStarted - TimeToLoadNext, -2f, 2f) == TimeSinceLevelStarted - TimeToLoadNext
&& LoadedFirstLevel == false)
{
GameObject go = LevelsPrefabs[Random.Range(0, LevelsPrefabs.Length)];
FirstLevel(go);
LoadedFirstLevel = false;
Debug.Log("Instantiated Prefab2");
}
}
private void FirstLevel(GameObject go)
{
if (LoadedFirstLevel == false)
{
Instantiate(go, new Vector3(0, 0, 0), Quaternion.identity);
goStack.Push(go);
Debug.Log("Instantiated Prefab1");
TimeToLoadNext += 50f;
LoadedFirstLevel = false;
if (TimeToLoadNext >= 30f) {
Destroy(go);
}
}
}
Repeating events are best done with Coroutines. Also Unity tells us to avoid instantiating and destroying GameObjects when the game runs (since its "slow") - especially if they might get reused.
So a quick and dirty implementation of your desired behaviour should be the following. If you want to make sure that an object doesnt get actived twice in succession, you cant take the straight forward random approach, but youll need to shuffle the array and spawn it in the new order. There are plenty of examples for shuffling an array available. The below should be easily adaptable with a bit of tinkering on your side ;)
public class RandomSpawner : MonoBehaviour {
//i learned there should always be an option to break out of a loop
public bool exitCoroutine = false;
public GameObject[] prefabs;
public float waitTime = 80.0f;
WaitForSeconds waitObject;
GameObject[] gameObjects;
int currentlyActive = 0;
void Start() {
waitObject = new WaitForSeconds(waitTime);
gameObjects = new GameObject[prefabs.Length];
for (int i = 0; i < prefabs.Length; i++) {
gameObjects[i] = Instantiate(prefabs[i]);
gameObjects[i].SetActive(false);
}
StartCoroutine(SpawnRandomObject());
}
void ActivateRandom() {
int nextActive = Random.Range(0, gameObjects.Length);
gameObjects[currentlyActive].SetActive(false);
gameObjects[nextActive].SetActive(true);
currentlyActive = nextActive;
}
IEnumerator SpawnRandomObject() {
while (!exitCoroutine) {
ActivateRandom();
yield return waitObject;
}
}
}
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
https://docs.unity3d.com/ScriptReference/WaitForSeconds.html

Unity 2D - sprite flicker while follow object

im creating simple script which tells gameobject that it needs to follow other gameobject in defined distance.
code:
void Update()
{
objPos = GameObject.FindGameObjectWithTag("Player").transform.position;
transform.position = new Vector3(objPos.x + 0.5f, objPos.y + 0.5f);
}
problem is: following gameobject is flickering. When you watch it, it appears to be few frames behind followed gameobject. I noticed it before in my earlier prototype but now its crutial for my new game. So i think its pretty common issue.
Is there sollution for this unwanted behaviour?
Thanks
GameObject.FindGameObjectWithTag("Player") has high performance cost, instead of using it each frame, hold an instance of the player. If the player is constant in the scene,
public Transform playerTransform;
void Update()
{
transform.position = playerTransform.position + new Vector3(0.5f, 0.5f);
}
and then assign the Player transform in Unity editor to this object using inspector. (Or dynamically in the code)
If your Player object gets destroyed and a new one instantiates once in a while, you can either assign the new one to the follower objects, or use a simple accessor method like this:
Transform _playerTransform;
Transform playerTransform
{
get
{
if(_playerTransform == null)
{
_playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
return _playerTransform;
}
}
void Update()
{
transform.position = playerTransform.position + new Vector3(0.5f, 0.5f);
}

Categories

Resources