I'm trying to make a shooting game and I want to set a range for the bullet. That means when it's been shot it flies for a period of time and then it gets destroyed.
I've tried the Destroy function; however, the way I've implemented the code looks as though it attempts to destroy the original bullet from the firing point, rather than at the end of it's range.
here's my code:
if (Input.GetKey("space"))
{
Instantiate(bullet, transform.position + new Vector3(0, 0, 1), bullet.rotation);
Destroy(bullet, 0.4f);
}
It will just fire once then I get MissingReferenceException.
You destroy the prefab. When you instantiate you get a reference back, thats the actual gameobject you want to destroy.
if (Input.GetKey("space"))
{
GameObject bulletInstance; //or whatever type your bullet is
bulletInstance = Instantiate(bullet, transform.position + new Vector3(0, 0, 1), bullett.rotation);
Destroy(bulletInstance , 0.4f); //if you instantiated via a component, you have to destroy bulletInstance.gameObject or you'll only destroy the component
}
https://docs.unity3d.com/Manual/InstantiatingPrefabs.html
Edit: Please note this creates a lot of garbage and was only meant to answer your question, you should consider using object pooling for bullets.
https://learn.unity.com/tutorial/object-pooling
Related
So I'm new to unity and trying to create a cube programmatically at the start, and when you press the down arrow key it decrements gradually.
using System.Collections.Generic;
using UnityEngine;
public class LetThereBeLight : MonoBehaviour
{
public GameObject rubixCube;
public Vector3 newScale;
public Vector3 currentScale;`
void Start()
{
GameObject rubixCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
rubixCube.transform.localScale = new Vector3(10f, 10f, 10f);
rubixCube.transform.position = new Vector3(0, 0, 0);
currentScale = rubixCube.transform.localScale;
newScale = new Vector3(-1f, -1f, -1f);
}
void Update()
{
if (Input.GetKey(KeyCode.DownArrow)){
currentScale += newScale;
if(currentScale.x < 10){
print("Well at least this worked");
}
}
}
}
When I run the play button in unity, the cube is created and I receive no errors but the cube does not shrink when I press the down arrow key. However, I know that the size of the vector is decrementing because I tell it to print a message as a test and it does so. But there are no visible changes to the size of the cube in the viewer.
Also, I originally put
currentScale = rubixCube.transform.localScale; in the void Update() function rather than the void Start() and it gave me an error Unassigned reference exception: The variable rubixCube of LetThereBeLight has not been assigned but shouldn't it not matter what function it is in whether it is called upon start or looped over because I already made rubixCube a public GameObject outside of both functions?
Does anyone know how to solve this? Why doesn't this script work?
Two issues:
In the Start you do
GameObject rubixCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
which creates a local variable and hides the class field with equal name rubixCube!
=> The class field rubixCube you later try to use in Update is never assigned!
In Start it should rather simply be
rubixCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Then in Update you do
currentScale += newScale;
You are changing the field currentScale, which is a Vector3 value ... but that is all.
You are not actually changing the Transform component on the GameObject itself at all, so the cube in your game is not actually being affected at all by this code.
You rather have to actually assign something back to the property
currentScale += newScale;
rubixCube.transform.localScale = currentScale;
I tried to modify your code, I searched for information, and clicked the "CreaterubixCube" button in the OnGUI() function to call the "CreatePrimitive" method, thereby creating a rubixCub object and adding its position attribute. I also just learned unity3d.
//so basically this is the update function. All part of code is working. the only problem is my projectile is instantiated at first, after 1.5 seconds it is destroyed. After 2 seconds(the timebtwshots variable is set to 2) another projectile is instantiated but after 1.5 seconds, it is not deactivated. Thus, projectiles clutter up on my screen.
void Update()
{
if (timebtwshots <= 0)
{
StartCoroutine(blah());
}
else
{
timebtwshots -= Time.deltaTime;
}
IEnumerator blah()
{
projectile.SetActive(true);
Instantiate(projectile, transform.position, Quaternion.identity);
timebtwshots = starttimebtwshots;
yield return new WaitForSeconds(1.5f);
projectile.SetActive(false);
}
}
I don't see where projectile is defined. Maybe a global object?
Anyway, when you Instantiate a new projectile, if you want to have control on it you have to assign it first to a variable.
var spawnedProjectile = Instantiate(projectile, transform.position, Quaternion.identity);
From that point, you can control THAT spawnedProjectile. In your code you're always accessing to the very first projectile thus the new ones are not disabled (you keep calling .setActive(false) on the projectile that has already active as false).
However, you're not destroying the object, you're just setting Active to false. You might consider using Destroy(spawnedProjectile) instead to entirely destroy the GameObject.
I'm making a level editor and I need help spawning prefabs.
Below is the prefab that i'm using. Now, the code that I'm using makes it so when I click one of those orbs, a new Prefab is spawned in that diretion, and then disable the orb. And it kinda works:
Code for spawning:
void OnMouseDown()
{
Instantiate(prefab, transform.position + transform.forward, transform.rotation);
gameObject.SetActive(false);
}
Note: I did try to use PrefabUtility.InstantiatePrefab, but that didn't spawn anything:
void OnMouseDown()
{
GameObject currentTile = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
currentTile.transform.position = gameObject.transform.position + gameObject.transform.forward;
gameObject.SetActive(false);
}
How do I pull this off?
I think that when you are setting the orb inactive, you are doing it in the prefab and not in the instance. (The instance is the (clone) that appears in the scene). So that might be causing that when a new clone is created, the clone has the orb inactive already.
For that you should deactivate the orb through the instance instead of the orb of the prefab with something like:
Gameobject GO = Instantiate(prefab, transform.position + transform.forward, transform.rotation);
GO.orb.SetActive(false);
Making the corresponding reference to the orb public and added in the editor so that you can then hace access to it and you can deactivate it.
Hope that makes sense.
I have a prefab of a enemy that will spawn in a random position multiple times around the player. However, sometimes this can make one enemy prefab overlap another enemy prefab.
So, I wrote a script which uses Physics2D.OverlapCircleAll() to detect any colliders before instantiating the enemy prefab which avoids the enemy prefab from overlaping an existing enemy. My issue is that the OverlapCircleAll() didn't detect the other instances of the prefab.
I already tried with Physics2D.OverlapBoxAll aswell. If I spawn more than 30 of these "enemy prefabs", at least one will overlap another enemy
This is the code used to detect overlap:
public void SpawnEachEnemy(GameObject Enemy)
{
Vector3 futurePosition = new Vector2(UnityEngine.Random.Range(UpperLeft.transform.position.x, DownRight.transform.position.x),
UnityEngine.Random.Range(UpperLeft.transform.position.y, DownRight.transform.position.y));
bool correctPosition = false;
while (!correctPosition)
{
Collider2D[] collider2Ds = Physics2D.OverlapCircleAll(futurePosition,0.2f);
if (collider2Ds.Length > 0)
{
//re-spawning to prevent overlap
futurePosition = new Vector2(UnityEngine.Random.Range(UpperLeft.transform.position.x, DownRight.transform.position.x),
UnityEngine.Random.Range(UpperLeft.transform.position.y, DownRight.transform.position.y));
}
else
{
correctPosition = true;
}
}
GameObject b = Instantiate(Enemy) as GameObject;
b.transform.position = futurePosition;
b.transform.parent = this.transform;
}
Louis Garczynski mentioned a few of the possibilities but one that wasn't mentioned is that if these are all instantiating in the span of a single frame (a guess based on a comment saying SpawnEachEnemy is called in a loop), then you may need to enable Auto Sync Transforms under Physics2D Settings:
This minimal reproducible example when attached to the camera in a new 3D project's scene should work as you intend with Auto Sync Transforms enabled and it will fail to prevent overlaps when it is disabled. It may be what is preventing it from working for you:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestScript : MonoBehaviour
{
Vector3 upperLeft;
Vector3 downRight;
GameObject prefab;
// Start is called before the first frame update
void Start()
{
transform.position = new Vector3(0, 0, -3);
upperLeft = new Vector3(-1, -1);
downRight = new Vector3(1, 1);
prefab = GameObject.CreatePrimitive(PrimitiveType.Sphere);
DestroyImmediate(prefab.GetComponent<SphereCollider>());
prefab.transform.localScale = 0.4f * Vector3.one;
prefab.AddComponent<CircleCollider2D>();
for (int i = 0; i < 12; i++)
{
SpawnEachEnemy(prefab);
}
prefab.SetActive(false);
}
public void SpawnEachEnemy(GameObject Enemy)
{
Vector3 futurePosition;
Collider2D[] collider2Ds;
do {
futurePosition = new Vector2(
UnityEngine.Random.Range(
upperLeft.x,
downRight.x),
UnityEngine.Random.Range(
upperLeft.y,
downRight.y));
collider2Ds = Physics2D.OverlapCircleAll(futurePosition, 0.2f)
}
while (collider2Ds.Length > 0)
GameObject b = Instantiate(Enemy) as GameObject;
b.transform.position = futurePosition;
b.transform.parent = this.transform;
}
}
I ran into exactly this while writing some feature tests.
As addendum to Ruzihm's awesome answer (was stuck for ages until I found that!).
If your game does not explicitly need AutoSyncTransforms every frame, then its preferable to leave it off, as it can cause a performance hit.
You should only set autoSyncTransforms to true for physics backwards compatibility in existing projects
If you just need it in tests or on a loading frame then:
You can manually call a transform sync with:
Physics.SyncTransforms(); or Physics2D.SyncTransforms();
Or set Physics.autoSyncTransforms = true; at the start, and then back to false at the end of your Start() method.
Either of these is preferable as you don't incur a penalty on subsequent frames.
If your finding you must use AutoSyncTransform or SyncTransform() in normal running. Consider a Coroutine to defer instantiations so that a script isn't creating lots of things at once.
Ideally you want as many frames a second as possible, so there 'may' be little gameplay impact spawning things one/a few at a time, on sequential frames. Rather than incurring an overall performance hit and potential stuttering as a script tries to create too much at once.
First your code could be simplified to something like this:
public void SpawnEachEnemy(GameObject Enemy)
{
Vector3 futurePosition;
do
{
futurePosition = new Vector2(
UnityEngine.Random.Range(UpperLeft.transform.position.x, DownRight.transform.position.x),
UnityEngine.Random.Range(UpperLeft.transform.position.y, DownRight.transform.position.y)
);
} while (Physics2D.OverlapCircleAll(futurePosition,0.2f).Length > 0)
GameObject b = Instantiate(Enemy) as GameObject;
b.transform.position = futurePosition;
b.transform.parent = this.transform;
}
I would also recommend adding a safety counter to your loop, to avoid an infinite loop if there is no room to be found.
Now, a lot of things could be wrong:
Maybe the OverlapCircle and the spawn don't happen in the same place? While setting the parent won't modify the world position, I would still set the position after setting the parent. Not the issue here.
Maybe the size of the overlap is too small ? Are you sure your enemies are 0.2 units in radius? Consider using Debug.DrawLine to draw the radius of the scanned circle.
Maybe your enemies are in a layer not in the DefaultRaycastLayers ? Try using a much larger circle radius and add a Debug.Log whenever OverlapCircleAll actually works.
There are a few other possible reasons, like disabled colliders, or colliders that are too small, etc. This should however cover most likely mistakes.
Thank you, Ruzihm!
I was looking for this answer for days, I had the same problem with my code using Physics2D.OverlapCircle. I am surprised I didn't find anyone who mentions Auto Sync Transforms anywhere else.
if ( Physics2D.OverlapCircle(position, radio) == null) {
GameObject obstacleInst = Instantiate(obstacle, transform);
obstacleInst.transform.position = position;
obstacleInst.transform.localScale = new Vector3(scale, scale, 1);
obstacleInst.transform.rotation = Quaternion.Euler(new Vector3(0, 0, Random.Range(0, 360)));
}
I have one problem. I want my prefabs to spawn every time my player picks them up. I did research on Google and YouTube and I tried to use the random function and instantiate. I don't know how to use them. I wrote this code I saw on YouTube and my prefab Sphere moves like 1cm to z position. I want to every time when I pick up object or my player go to spawn more of this on the z position. How do I do this?
My smaller script:
public GameObject Sphere;
public float zrange;
// Use this for initialization
void Start () {
RandomPosition();
}
void RandomPosition()
{
zrange = Random.Range(0f, 2f);
this.transform.position = new Vector3(0, 0, zrange);
}
You achieve that by not messing with the x and y values (your code sets them both to 0).
Vector3 p = transform.position;
p.z = zrange;
transform.position = p;
This assumes that your code to instantiate the object is already correctly placing the object. If not, more information is needed.