I have a random world with some trees that get randomly instantiated.
I use the following code to instantiate them:
GameObject go = Instantiate(treePrefab);
go.name = "Tree";
go.tag = "Wood";
go.layer = 9;
go.transform.position = new Vector3(y + UnityEngine.Random.Range(-0.5f, 0.5f), heightPos, x + UnityEngine.Random.Range(-0.5f, 0.5f));
go.transform.Rotate(0, UnityEngine.Random.Range(0, 360), 0);
x and y are part of a for loop that goes through all positions (like a grid) of my world.
heightPos is the current height of the terrain at the specific point (x/y).
My problem is that the prefab ob the trees has the position 0, 0, 0.
Every tree that get's instantiated gets set to 0, 0, 0 too.
I use the exact same code to instantiate rocks, cactus, feathers, car's and many other things, and it works perfect.
but it always fails at the trees.
I've looked through the code and there is no difference to the other objects, except the prefab (of course).
So i think it has to do something with the settings of the prefab.
But my prefab got no animator, it is not static or something.
This is what my tree-Prefab looks like in the inspector:
and this is what it looks like in scene view:
You can create another gameobject and make this instance as a child of that gameobject so even if the instance is always at (0, 0, 0) you can always change the position of the parent object.
Related
I'm trying to program an isometric building placement script. Each building has a PolygonCollider2D component on it with a trigger. When placing a new building, I'm trying to check if the PolygonCollider2D of the placed building overlaps with anything else (to check if placement is valid). My code is as follows:
Adjust the points of the new placed building collider based on the mouse position
Vector2[] points = polygonCollider2D.points;
points.SetValue(polygonCollider2D.points[0] + (Vector2)mousePosition, 0);
points.SetValue(polygonCollider2D.points[1] + (Vector2)mousePosition, 1);
points.SetValue(polygonCollider2D.points[2] + (Vector2)mousePosition, 2);
points.SetValue(polygonCollider2D.points[3] + (Vector2)mousePosition, 3);
polygonCollider2D.points = points;
Set up contact filter:
ContactFilter2D contactFilter2D = new ContactFilter2D();
contactFilter2D.useTriggers = true;
contactFilter2D.SetLayerMask(polygonCollider2D.gameObject.layer);
Check for collisions
List<Collider2D> list = new List<Collider2D>();
Debug.Log(polygonCollider2D.OverlapCollider(contactFilter2D, list));
However if there is already a building there, it still does not register an overlap.
What am I missing / doing wrong ?
Thanks so much for the help!
Your code which sets the polygon collider points seems to be the issue here. If that code runs multiple times, it will repeatedly offset the collider further and further away from its original position. You probably don't want to be changing the actual collider; usually you should move object which has the collider. So you would replace those lines with something like this:
gameObject.transform.position = mousePosition;
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'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
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.
I am trying to simulate a door in BulletPhysics using a HingeConstraint. The following code works as intended, in that the cube pivots on its corner around the Y-axis, effectively swinging open like a door, but as soon as I place the object on the ground it begins flying around uncontrollably.
RigidBody body = physics.LocalCreateRigidBody(mass, Matrix4.CreateTranslation(new Vector3(0, 0.5f, 0)), new BoxShape(1, 1, 1));
body.UserObject = "Door";
Vector3 pivotInA = new Vector3(1, 1, 1);
Vector3 axisInA = new Vector3(0, 1, 0);
var hinge = new HingeConstraint(body, pivotInA, axisInA);
const float targetVelocity = 1.0f;
const float maxMotorImpulse = 1.0f;
hinge.EnableAngularMotor(true, targetVelocity, maxMotorImpulse);
physics.World.AddConstraint(hinge);
Disabling collisions between the door and ground would solve the problem but I am struggling to achieve this also. The documentation says you can set a collision type and mask for a body but only the World.AddRigidBody() constructor appears
to take such parameters and as far as I can tell my door is added to the world as a Hinge and the ground is added automagically when calling LocalCreateRigidBody(), shown below.
CollisionShape groundShape = new BoxShape(10, 1, 10);
CollisionObject ground = LocalCreateRigidBody(0, Matrix4.CreateTranslation(0, -1, 0), groundShape);
ground.UserObject = "Ground";
How can I stop the door spinning out when it sits atop the ground?
You can write your own version of LocalCreateRigidBody that sets up the collision mask. LocalCreateRigidBody is a helper function in the BulletSharp demos and not a built-in function in Bullet.
Here is one way to set up the collision filter:
const CollisionFilterGroups groundGroup = (CollisionFilterGroups)0x40000000;
var groundMask = CollisionFilterGroups.AllFilter ^ CollisionFilterGroups.StaticFilter;
World.AddRigidBody(ground, groundGroup, groundMask);
var doorMask = CollisionFilterGroups.AllFilter ^ groundGroup;
World.AddRigidBody(door, CollisionFilterGroups.DefaultFilter, doorMask);
Another way to solve this is to make the door shorter so it's not close enough to the ground to generate collisions. Then render a larger graphics object in place of the smaller physics object. If you are using the BulletSharp demo framework, then you will have to modify the framework to support this.