BulletPhysics - Door object spins uncontrollably when placed on ground - c#

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.

Related

Physics2D.OverlapCircleAll not detecting other gameobjects

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)));
}

Moving Prefabs, Unity Spawner, Move Position

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.

BulletPhysics - Set hinge angle explicitly?

A hinge's hingeAngle can be retrieved as a singular float but not be set explicit. I am simulating a door using hinges and need to be able to instantly set the angle to open or closed at will. How can this be achieved?
The hinge is created with:
const float mass = 10.0f;
BoxShape boxShape = new BoxShape(Door.CollisionShape);
Vector3 pivotA = new Vector3(-Door.CollisionShape.X, Door.CollisionShape.Y, Door.CollisionShape.Z);
door.rigidBody = physics.LocalCreateRigidBody(mass, door.getRotationMatrix() * Matrix4.CreateTranslation(position), boxShape);
door.rigidBody.ActivationState = ActivationState.DisableDeactivation;
door.rigidBody.UserObject = "Door";
var axisA = Vector3.UnitY; // pointing upwards, aka Y-axis
var hinge = new HingeConstraint(door.rigidBody, pivotA, axisA);
hinge.SetLimit(-(float)Math.PI * 0.25f, 0);
physics.World.AddConstraint(hinge);
and I am moving it with:
float targetVelocity = Door.OpenSpeed;
float maxMotorImpulse = 1.0f;
door.hinge.EnableAngularMotor(true, targetVelocity, maxMotorImpulse);
On each step BulletPhysics increases the hingeangle based on the targetVelocity. I'm looking to set this value directly, as opposed to indirectly by applying forces.
Did you try setMotorTarget() function?
void btHingeConstraint::setMotorTarget
(
btScalar targetAngle,
btScalar dt
);
Is your desired behaviour that the door changes from open to close, in a single simulation step?
If so, that seems a dangerous thing to do, because there may be something blocking the door, and by hard-setting the door rigid body, it may be intersection other shapes.

Quaternion.Lerp not even moving

I wanted to lerp rotation to new Quaternion(0,0,0,0); but it seems like not animating to desired rotation at all... It just stop at where it is...
What I tried were those three on below
RectTransform rectTransformComponent = this.transform.GetComponent<RectTransform>();
rectTransformComponent.localRotation = Quaternion.Lerp(rectTransformComponent.localRotation, new Quaternion(0,0,0,0), 0.1f);
and
this.transform.localRotation = Quaternion.Lerp(this.transform.localRotation, new Quaternion(0,0,0,0), 0.1f);
and
this.transform.rotation = Quaternion.Lerp(this.transform.rotation, new Quaternion(0, 0, 0, 0), 0.1f);
I'm using RectTransform for this object. It was working without lerping. It's in the Update() so it will be looped every frame. I tried to bring up the speed for lerp but no luck...
Does someone have any idea what is going on??
this.transform.localRotation = new Quaternion(0,0,0,0) //This works
An all-zero quaternion (new Quaternion(0,0,0,0)) is an invalid rotation. Quaternion.Lerp doesn't know how to interpolate to an invalid rotation.
To rotate to an Euler rotation of x=0,y=0,z=0, then you should instead use Quaternion.identity (which is equivalent to new Quaternion(0,0,0,1)).

Keep Point(2) or Vector2(2) joined to Point(1) or Vector2(1)

Imagine a segmented creature such as a centipede. With control of the head segment, the body segments are attached to the previous body segment by a point.
As the head moves (in the 8 cardinal/inter-cardinal directions for now) a point moves in relation to its rotation.
public static Vector2 RotatePoint(Vector2 pointToRotate, Vector2 centerOfRotation, float angleOfRotation)
{
Matrix rotationMatrix = Matrix.CreateRotationZ(angleOfRotation);
return Vector2.Transform(pointToRotate - centerOfRotation, rotationMatrix);
}
Was going to post a diagram here but you know...
center(2) point(2) center(1) point(1)
point(1)
point(2) ^ |
/ \ |
| |
center(2) center(1) \ /
V
I have thought of using a rectangle property/field for the base sprite,
private Rectangle bounds = new Rectangle(-16, 16, 32, 32);
and checking that a predefined point within the body segment remains within the head sprite's bounds.
Though I am currently doing:
private static void handleInput(GameTime gameTime)
{
Vector2 moveAngle = Vector2.Zero;
moveAngle += handleKeyboardMovement(Keyboard.GetState()); // basic movement, combined to produce 8 angles
// of movement
if (moveAngle != Vector2.Zero)
{
moveAngle.Normalize();
baseAngle = moveAngle;
}
BaseSprite.RotateTo(baseAngle);
BaseSprite.LeftAnchor = RotatePoint(BaseSprite.LeftAnchor,
BaseSprite.RelativeCenter, BaseSprite.Rotation); // call RotatePoint method
BaseSprite.LeftRect = new Rectangle((int)BaseSprite.LeftAnchor.X - 1,
(int)BaseSprite.LeftAnchor.Y - 1, 2, 2);
// All segments use a field/property that is a point which is suppose to rotate around the center
// point of the sprite (left point is (-16,0) right is (16,0) initially
// I then create a rectangle derived from that point to make use of the .Intersets method of the
// Rectangle class
BodySegmentOne.RightRect = BaseSprite.LeftRect; // make sure segments are connected?
BaseSprite.Velocity = moveAngle * wormSpeed;
//BodySegmentOne.RightAnchor = BaseSprite.LeftAnchor;
if (BodySegmentOne.RightRect.Intersects(BaseSprite.LeftRect)) // as long as there two rects occupy the
{ // same space move segment with head
BodySegmentOne.Velocity = BaseSprite.Velocity;
}
}
As it stands now, the segment moves with head but in a parallel fashion. I would like to get a more nuanced movement of the segment as it is being dragged by the head.
I understand that the coding of such movement will be much more involved than what I have here. Some hints or directions as to how I should look at this problem would be greatly appreciated.
I will describe what you need to do using a physics engine like Farseer but the same holds if you want to write your own physics engine.
Create a Body for each articulated point of the centipede body.
Create a Shape that encapsulates the outer shell that will be attached to each point.
Attach the Body and Shape using a Fixture. This creates one link in your centipede.
Attach multiple links using a SliderJoint.
For example - assuming that the outer shell of each link is a circle, here's how to create two links and join them together.
Fixture fix1 = FixtureFactory.CreateCircle(_world, 0.5f, 1, new Vector2(-5, 5));
fix1.Body.BodyType = BodyType.Dynamic;
Fixture fix2 = FixtureFactory.CreateCircle(_world, 0.5f, 1, new Vector2(5, 5));
fix2.Body.BodyType = BodyType.Dynamic;
JointFactory.CreateSliderJoint(_world, fix1.Body, fix2.Body, Vector2.Zero, Vector2.Zero, 10, 15);
Now applying a force on any of the bodies or collisions on the shapes will drag the second joint around - just like you want.
This is all just stick physics - so you could implement your own if you REALLY wanted to. ;)

Categories