Hey everyone and thanks for taking the time.
I'm currently working on a 2D something in Unity and have the following problem:
We have a spawner that periodically creates 'thing':
public float spawnTime = 3;
public Object thing; //public GameObject thing;
// Use this for initialization
void Start () {
InvokeRepeating("spawn", spawnTime, spawnTime);
}
void spawn ()
{
Vector3 pos = new Vector3(-14, 0, 0);
Quaternion ori = new Quaternion(0, 0, 0, 0);
Instantiate(thing, pos, ori);
}
And it does a good job. Meet 'thing':
void Start () {
InvokeRepeating("move", 0.1f, 0.1f);
}
void move()
{
transform.Translate(0.2f, 0, 0);
if (this.transform.position.x > 14)
{
Destroy(this); //Destroy(GameObject);
So basically a 'thing' is created, hauls itself from -14 to 14 and then suffocates.
Problem: As soon as the first 'thing' is deleted the newly created (not the ones already moving) dont ever move from -14.
I guess I haven't properly specified which method to call periodically but wasn't able to find a proper solution in the unity3d top-down-shooter or similar examples.
Thanks again.
€: After applying the proposed changes the spawner stops creating 'thing's after 3 cycles of creation.
€: My mistake was not using the 'thing' as a prefab (dragging it into "assets" as described in the beautiful answer) so I was referencing a soon to be deleted instance of my 'thing'. Dragged it to hell (assets) and was happy ever after.
I recreated you setup with just these scripts and what I got is that the objects just didn't get destroyed, but newly spawned moved properly until 14.
The reason they didn't get destroyed though is, that you called Destroy(this); which destroys the script component, not the gameobject. So this should be Destroy(gameObject); instead. With this fix it runs just fine for me.
Also, if there is no special reason to use object, your public Object thing; should better be public GameObject thing;.
Edit:
How I set it up:
Created a Empty (renamed it to Spawner)
Created a Sphere (as my thing, renamed it to Ball)
Made Ball a prefab by dragging it into the assets folder
Deleted the Ball from the scene
Created a Spawner script and put your spawning code into it
Created a Ball script and put your move code into it
Made the two changes to the scripts
Put the Spawner script on the spawner gameobject
Dragged the Ball prefab on into the public GameObject thing of Spawner
Put the Ball script on the ball prefab
Done.
Related
i made a "turret" in unity that shoots automatically on enemies it works with one enemy but when i put 2 it shoots in the midle of then dont know why it does that
public transform enemy;
public void Update() {
enemy = Gameobject.FindgameObjectWithTag("Enemy").getComponent<Transform>;
//just put the getting the possition part because the shooting works just fine
}
Your script is refreshing the enemy position every Update() iteration.
Try to have the turret stick to the enemy until it gets out of range, or until it is dead. Before doing your variable assignation, check if enemy != null, if it is the case, then you can write :
enemy = Gameobject.FindgameObjectWithTag("Enemy").getComponent<Transform>;
Also, try to avoid refreshing every Update() call, instead, use a different function that you call every half second.
I recommend you checking this project by Brackeys, that handles the situation perfectly. Here
GLHF
I have a terrain with two enemies that I placed and a plane that I have given a script to spawn enemies continuously (basically wave defence without towers)
The two enemies I placed work as intended, killing them will give experience and coming into contact with them cost the player some health.
But the ones spawned by my scripted plane don't grant experience on a kill, the only reason they do health damage on contact is because I set the collision to look for a tag of "Enemy" which they have.
# THE SCRIPT FOR THE ENEMY-SPAWNING PLANE
IEnumerator Spawner()
{
yield return new WaitForSeconds(startWait);
while(!stop)
{
randEnemy = Random.Range(0, 2);
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), 2, Random.Range(-spawnValues.z, spawnValues.z));
Instantiate(enemies[randEnemy], spawnPosition + transform.TransformPoint(0, 0, 0), gameObject.transform.rotation);
yield return new WaitForSeconds(spawnWait);
}
}
# THE SCRIPT WHICH CALLS FOR EXPERIENCE TO BE GIVEN (Enemy as GameObject)
void Dead()
{
_characterXP.GainExp(120);
Destroy(gameObject);
}
# THE SCRIPT WHICH GIVES THE PLAYER EXPERIENCE (CharacterXP as GameObject)
public void GainExp(float expThatWasGained)
{
CurrentExp += expThatWasGained;
}
I'm still pretty new to Unity and C# so if there is anything else that's needed I can provide it, I have checked all relevant objects to make sure the relevant things are connected correctly, and if they didn't work then it would affect the original enemies, the issue is only in regards to the spawned enemies that get named ENEMY(Clone)
EDIT
The Prefab ^
The Pre-Spawned Enemy Mid-Game^
The Clone Enemy Mid-Game^
You cannot assign scene objects to prefab property fields. You can only assign other prefab (or assets) into the prefab property slots.
To assign a scene object to your enemy script, you need to find it at runtime.
In your enemy's Start method, find the fpscontroller
void Start()
{
_characterXP = GameObject.FindObjectOfType<FPSController>();
}
I think this will solve your problem
Not the most intelligent way of doing this, but I was on a tight schedule and the method works.
I created two prisons outside of the playable area and put one of each enemy inside of it and then referred to these two trapped individuals in my Monster Spawner (The Plane) instead of the prefabs and now I gain the right amount of exp per kill.
I have the following code:
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update() {
if (Input.GetKeyDown("w")) {
SpawnBall();
DestroyBall();
}
}
void SpawnBall()
{
Instantiate(ball, sp, transform.rotation);
}
void DestroyBall()
{
if (ball.transform.position.y >= -5.7f)
{
Destroy(ball);
}
}
and the code is supposed to generate a new ball every time when "w" is pressed, but for some reason it creates multiple clones and it crashes the engine. How can I create a single clone only?
And also the destroy method doesn't do anything, although it should remove the clone when it passes -5.7 on the y-axis.
Thanks in advance
Create a new script named "SpawnRigidbody" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnRigidbody : MonoBehaviour
{
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update()
{
if (Input.GetKeyDown("w")) {
SpawnBall();
}
}
void SpawnBall()
{
Debug.Log ("spawn");
GameObject go = Instantiate(ball, sp, transform.rotation).gameObject;
go.AddComponent<DestroyAfterPosition> ();
}
}
Now create another script named "DestroyAfterPosition" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyAfterPosition : MonoBehaviour
{
void Update ()
{
if (transform.position.y <= -5.7f)
{
Destroy(gameObject);
}
}
}
Create an empty game object and attach the SpawnRigidbody and then assign your ball in the inspector.
Hope this help you.
There are a couple problems with your Destroy() statement. First, it is destroying ball which is the Rigidbody2D. You will instead want to destroy the gameobject attached to the ball:
Destroy(ball.gameObject);
Second, you are trying to destroy the ball immediately after it is created, but only if y>=-5.7f. I think instead what you are looking to do is continuously check if the ball is above that point and destroy it if it is. Since you are creating multiple balls, they will all need their own check, which means you need to create a script for the ball prefab and in the Update() check its position and Destroy(gameObject) as necessary.
I think you may want to declare ball as a GameObject at the beginning of your script as well, so you are instantiating a gameobject and not just a Rigidbody2D:
public GameObject ball;
Object.Instantiate creates a clone of the provided object, so each time it is called a new object is created. MonoBehaviour.Update is called every frame, which means it's called at least 30 times per second (usually 60 or more on a PC).
So 30+ times per second, your code checks if the w key is held down, and, if so:
A clone of ball is created
Destroy the original ball if it is beyond -5.7 on the y-axis.
Thus, you're creating 30+ ball clones per second, and none will be deleted (because the DestroyBall method only looks at the original ball, not the clones).
If you want to work with the cloned object, assign the result of Instantiate to a field:
ballClone = Instantiate(ball, sp, transform.rotation);
Then you can check if ballClone exists and skip the SpawnBall call if the clone already exists. You can also use ballClone in the DestroyBall method.
ryemoss' answer seems like it's
also important. This answer is just based on looking at your code and the public Unity docs; I don't have a lot of experience with Unity.
Your destroy call is being called once, and only immediately after creating the object.
So it's currently doing this...
User clicks button...
Create ball...
Check to see if the ball is a certain height. It's not, so ignore...
Ball drops down based on gravity or whatever force compels it.
... and is never deleted.
If you want the ball to be destroyed when it reaches a certain point, either...
Have SpawnBall return the game object, which you store and check / delete later in Update,
...or...
Create a script that just checks the object's transform.position and blows up if it's where it needs to be. Attach that to the prefab of the ball you're creating a duplicate of.
I've recently been following a tutorial from "Brackeys" on youtube for a tower defence game, I followed it word for word but have an error (which is on the photos below) that won't let my enemy prefabs move in the scene. The prefabs are all spawning, but are stuck in the one spot.
any help will be appreciated
thanks, Mitch
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float speed = 10f;
private Transform target;
private int wavePointIndex = 0;
// Use this for initialization
void Start()
{
// An error pops up on the first frame for this line of code below
target = Waypoints.points[0];
}
// Update is called once per frame
void Update ()
{
// This is the main source of the error below.
Vector3 dir = target.position - transform.position;
transform.Translate (dir.normalized * speed * Time.deltaTime, Space.World);
if (Vector3.Distance (transform.position, target.position) <= 0.4f)
{
GetNextWayPoint ();
}
}
void GetNextWayPoint()
{
if (wavePointIndex >= Waypoints.points.Length - 1)
{
Destroy (gameObject);
return;
}
wavePointIndex++;
target = Waypoints.points[wavePointIndex];
}
}
error description in unity
Enemy prefab that has script on it
The image you uploaded shows that you named Enemy script EnemyMover in your Editor. This is not a problem but you should always post script names as it is in your scene to avoid confusion.
According to you, this line of code is the problem: target = Waypoints.points[0];
Issue is that points is an array of Transform and is not initialized.
There are the three possible problems:
1.Your Waypoint script is NOT attached to the Waypoints GameObject.
Select the Waypoints GameObject, drag the Waypoint script into it. It must be attached to your Waypoints GameObject in order for it to initilize the
points array variable.
2.Your Waypoint script is attached to multiple GameObjects. Make sure that the Waypoint script is ONLY attached to one GameObject and that GameObject is the parent of all your waypoints. This GameObject is named Waypoints.
To verify this, select the Waypoint script from the Project Tab, right click on it and click Find Reference In Scene. It will show you every GameObject the Waypoint script is attached to. If it is attached to any GameObject that is not named Waypoints, remove the script from that GameObject.
3.Functions in your Waypoint script are being called before the ones in your Enemy script. This happens sometimes in Unity.
Go to Edit->Project Settings->Script Execution Order. Drag the Waypoint script into the order then drag the Enemy script in the oder. Click Apply.
Second solution to problem #3 is to remove the static keyword and use GameObject.Find("Waypoints"); and GetComponent<Waypoint>();. You can find complete scripts both scripts here and here. Note that if you follow this second solution of removing the static keyword, you may have hard time following the rest of the tutorial.
I'm trying to have my game spawn enemies when ever the player reaches a way point.
Right now, I have this functionality working. When my player gets to the first way, the enemies spawn. He only moves on once he has killed all of the enemies on the screen are dead. However, when he gets to the second way point, no enemies spawn.
Right now, in my collision class I call the following line of code:
Destroy(gameObject)
Whilst this work for the first way point, the second one wont spawn anything as the game object my collision class has been attached to has been destroyed. However, all of my enemies are prefabs and I thought the destroy function would only destroy that instance of the prefab. No matter when you called the instantiate method, it would only destroy that instance.
I'm spawning my enemies with the following method:
public GameObject SpawnEnemies()
{
Vector3 _position = new Vector3(transform.position.x, transform.position.y, transform.position.z);
// instantiate particel system
Instantiate(_particle, _position, Quaternion.identity);
_clone = (GameObject)Instantiate(_enemy, _position, transform.rotation);
_ai = _clone.GetComponent<HV_BaseAI>();
_ai._waypoints = _wayPoints;
return _clone;
}
Then I'm finding out how many of the enemies are still alive with the following code in my collision method:
GameObject g, f; // enemies I want to spawn
g = GameObject.FindGameObjectWithTag("SectionController");
f = GameObject.FindGameObjectWithTag("SectionController");
HV_SectionController tempSectionControllerGroundEnemies, tempSectionControllerFlyingEnemies;
tempSectionControllerGroundEnemies = g.GetComponent<HV_SectionController>();
tempSectionControllerFlyingEnemies = f.GetComponent<HV_SectionController>();
tempSectionControllerGroundEnemies._numberOfGroundEnemies.Remove(gameObject); // remove enemies from list
tempSectionControllerFlyingEnemies._numberOfFlyingEnemies.Remove(gameObject);
//Destroy(gameObject);
_numberOfGroundEnemies = tempSectionControllerGroundEnemies._numberOfGroundEnemies.Count;
_numberOfFlyingEnemies = tempSectionControllerFlyingEnemies._numberOfFlyingEnemies.Count;
Then when I want to move on I do the following check:
if (_groundEnemiesRemaining == 0)
{
MoveToNextSection();
_sectionStartTime = Time.time;
}
i know the above line is checking only one type of enemy at the moment, but its the ground enemies I'm having issues with at the moment.
Does anyone know how I can delete the enemy prefab I'm spawning from my first section, once they've been hit, then have it respawn at the next section without the error:
The object of type 'GameObject' has been destroyed but you are still
trying to access it.
My guess would be that the gameobjects are being "destroyed" by two different collision events. First one destroys it, second throws the error.
What I've done in similar situations is put the Destroy code within the object being destroyed. Then from within your collision class, use gameObject.SendMessage(string) to send a message to the actual object, which destroys it.
Without seeing further code I can't speculate as to the origin of the error, but the above is my best guess. SendMessage can also take a DontRequireReceiver parameter, so it won't pop an error if two collision events try to send it a message.
Instead of destroying them u can just disable them using gameObject.SetActive() !