I'm using bulletsharp (C# wrapper of bullet physics) to do some collision detection. Everything went fine until i tried to change the collision shape of an existing rigid body from box to compound at runtime. I'm doing this to simulate the collision again with a higher accuracy. The problem is: collision of compound shapes is not detected.
Scenario:
DicreteDynamicsWorld containing some RigidBody with BoxShape
Collision of two kinematic RigidBody with BoxShape happens (and is detected)
Change the shapes of these two RigidBody to CompoundShape using the HACD algorithm for convex decomposition
Remove RigidBody from DicreteDynamicsWorld
RigidBody.CollisionShape = CompoundShape
Set Position using RigidBody.MotionState.WorldTransform
Add RigidBody to DicreteDynamicsWorld
Undo the movement which caused the collision (one object doesn't contain the other)
Repeat the movement
Collision is not detected
Remarks:
Changing the CollisionShape from BoxShape to CompoundShape was successful (correct CollisionShape and correct position)
For collision detection i'm using DicreteDynamicsWorld.Dispatcher.NumManifolds > 0 after a DicreteDynamicsWorld.StepSimulation(...)
Some code snippets as requested:
If you need something particular, please tell me. My solution is too big and too complexe to post the complete code...
RigidBody creation:
// Create rigid body
MotionState motionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(0.0f, motionState, collisionShape);
RigidBody rigidBody = new RigidBody(rbInfo);
rbInfo.Dispose();
// Kinematic body: mass=0 -> static/kinematic -> use flag
bool isKinematicBody = (compModel.Children[i].Type ==...) || ... ;
rigidBody.CollisionFlags = isKinematicBody ? CollisionFlags.KinematicObject : CollisionFlags.StaticObject;
rigidBody.ActivationState = ActivationState.DisableDeactivation;
Basic steps:
// Get old collision data
if (compModel.Children[i].Container.TryGetValue(ContainerType.Collision, out container))
collisionData = ((ContainerCollision) container).CollisionData;
// Get geometry
if (compModel.Children[i].Container.TryGetValue(ContainerType.Geometry, out container))
{
verticesGeo = ((ContainerGeometry) container).GeometryData.Vertices;
trianglesGeo = ((ContainerGeometry) container).GeometryData.Triangles;
}
// Remove rigid body from world
_world.RemoveRigidBody(collisionData.RigidBody);
// Create new shape
List<Vector3> vertices = Utility.ToBulletVector3List(verticesGeo);
List<int> indices = Utility.ListIntArrayToListInt(trianglesGeo);
CompoundShape collisionShape = ConvexDecomposition(compModel.Children[i].Id, vertices, indices);
// Set collision shape
collisionData.RigidBody.CollisionShape = collisionShape;
// Set position
collisionData.RigidBody.MotionState.WorldTransform *= collisionData.PositionDifference;
// Add rigid body to world
_world.AddRigidBody(collisionData.RigidBody, collisionData.CollisionGroup, collisionData.CollisionMask);
CollisionContainer:
public interface IContainer
{
ContainerType Type { get; }
}
public struct ContainerCollision : IContainer
{
public ContainerType Type
{
get { return ContainerType.Collision; }
}
public CollisionData CollisionData;
}
Structure CollisionData:
public struct CollisionData
{
public BulletSharp.RigidBody RigidBody;
public BulletSharp.Matrix PositionDifference;
public BulletSharp.Vector3 ZeroPosition;
public short CollisionGroup;
public short CollisionMask;
}
Any ideas, what i'm doing wrong?
Thanks.
Assuming that all your change in shape is happening here
CompoundShape collisionShape = ConvexDecomposition(compModel.Children[i].Id, vertices, indices);
// Set collision shape
collisionData.RigidBody.CollisionShape = collisionShape;
// Set position
collisionData.RigidBody.MotionState.WorldTransform *= collisionData.PositionDifference;
// Add rigid body to world
_world.AddRigidBody(collisionData.RigidBody, collisionData.CollisionGroup, collisionData.CollisionMask);
Probably you will have to recalculate the entire RigidBody from start, I have no clue how you do that in the first place so I will show you an example how I do it.
public virtual RigidBody LocalCreateRigidBody(float mass, Matrix startTransform, CollisionShape shape)
{
//rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.0f);
Vector3 localInertia = Vector3.Zero;
if (isDynamic)
shape.CalculateLocalInertia(mass, out localInertia);
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
DefaultMotionState myMotionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, shape, localInertia);
RigidBody body = new RigidBody(rbInfo);
rbInfo.Dispose();
return body;
}
With that being said, take a note at your "collisionData" (Which I guess its a class?) and the naming of RigidBody (it might have a conflict with the BulletSharp class?)
Best reguards with your progress and feel free to contact me if you have any questions!
Related
So I'm trying to make a little pushback effect in my tests arena, I've got a sphere collider and here is my script:
// PushBack Class Script
if (Input.GetKeyDown(KeyCode.Q))
{
explosion_ball.transform.position = transform.position;
StartCoroutine(WaitAndPrint());
}
IEnumerator WaitAndPrint()
{
float i = 0;
while (i < 1)
{
i += 0.01f;
explosion_ball.radius = curve.Evaluate(i) * 10;
yield return new WaitForSeconds(0.01f);
}
}
//__________//
Sphere collider is set and stuff, but it doesn't push things back like I thought it would.
Thanks!
Edit:
explosion_ball is a sphere collider, I'm changing it with the point on the animation curve and * it by 10
EDIT:Unity Rigid bodies go to sleep so[Also Change interpolation to continuous if collider changes size too quickly]
A.check if obstacle rigid bodies are Sleeping with onTriggerEnter and Wake Up
void OnTriggerEnter(collider){ if(rb.IsSleeping()){rb.WakeUp();}
or
B.Attach this forceWakeUp Script to all Objects you want to be obstacles.
using UnityEngine;
public class forceWakeUp : MonoBehaviour
{
private Rigidbody rb;
void Start()
{
rb = gameObject.GetComponent<Rigidbody>();
}
void Update()
{
if(rb.IsSleeping()){rb.WakeUp();}
}
}
Forcibly Keeping Many objects awake Will impact performance.So ,your decision.
You need to scale up the collider on the object by radius and all the objects it is supposed to wobble need to have Rigid body component attached to them
2.if you ARE doing above things and its not adding any force you could just add a force on all the overlapping objects using the "OnCollisionEnterTrigger" and AddForceMethod radially away from the Sphere
Obstacle.position - Sphere.position is the vector Radially away from Sphere I think.
You don't need coroutines for this I think.
While searching through the Unity scripting API, found a method in there called
Rigidbody.AddExplosionForce(explosionForce, explosionPosition, explosionRadius, upwardsModifier, mode)
In order to get the rigid bodies that are to be affected by the explosion I would need to get them using Physics.OverlapSphere(position, radius) this gets all of the objects within the radius variable, then get the component.
Combining these two would also look like:
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach (Collider hit in colliders)
{
if (hit.transform.tag == "Interactable")
{
if (hit.gameObject != hitgameObject)
{
Rigidbody rb = hit.GetComponent<Rigidbody>();
if (rb != null)
rb.AddExplosionForce(power, transform.position, radius, upForce, ForceMode.Impulse);
}
}
}
If there is any explaining that you would like me to do about my variables mentioned I will reply :)
Thanks for the help guys.
I don't know your curve and what values that evaluation produces, but you can check your code visually with Window/Analysis/Physics Debugger, or write a gizmo:
private void OnDrawGizmos()
{
Gizmos.color = new(1.0f, 0.0f, 0.0f, 0.5f);
Gizmos.DrawSphere(explosion_ball.transform.position, explosion_ball.radius);
}
Or simply just use https://docs.unity3d.com/ScriptReference/Rigidbody.AddExplosionForce.html
I'm creating a basic pool game in Unity with C#, what im trying to do is that if the cue ball is moving, the stick will disappear, and once it becomes stationary again, it will reappear to where the cue ball is located. This is my code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class stickDeplacement : MonoBehaviour
{
public bool bIsOnTheMove = false;
Vector3 lastPos;
public GameObject Stick;
void Start()
{
}
void Update()
{
var stick = Instantiate(Stick, gameObject.transform.position, gameObject.transform.rotation);
if (this.transform.position != lastPos)
{
Destroy(stick);
Debug.Log("Is moving");
}
else
{
Debug.Log("Is not moving");
}
lastPos = this.transform.position;
}
}
But what happens is that the ball, along with the stick, will just spasm and be buggy right from the start (when I open and play the game). Am I missing something here?
This is extremely inefficient and dangerous!
Why instantiate a stick every frame just to eventually already destroy it in that very same frame? And if the ball is stationary you want an additional stick to be spawned every frame?
Instead of all the time instantiating and destroying it at all you should rather keep one stick and only (de)activate it.
In your case you could do this in a single line
bIsOnTheMove = transform.position == lastPos;
stick.SetActive(!bIsOnTheMove);
Also I doubt a lot you would like the stick to have the same rotation as a rolling ball! Of course this will behave awkward
Most certainly you do not simply want to clone the ball's orientation. I would e.g. try to determine the closest point of the table edge to the current ball's position (iterate through the wall colliders and use Collider.ClosestPoint) and let the stick face the direction from that edge point towars the ball position (+ maybe an offset in X so the stick is slightly inclined by default).
And finally you anyway do not want to assign that rotation every frame, since you most probably later want your users to be able to rotate that stick. You only want to apply this once when the ball becomes stationary.
Something like e.g.
// The stick is not a prefab anymore but simply always exists in the scene!
[SerializeField] private Transform stick;
[SerializeField] private Vector3 eulerOffset;
[SerializeField] private Collider[] wallColliders;
public bool bIsOnTheMove;
private Vector3 lastPos;
private void Start()
{
lastPos = transform.position;
}
private void Update()
{
// is the ball currently moving?
var isMoving = transform.position == lastPos;
last Post = transform.position;
// Did this state change since the last frame?
if(bIsOnTheMove == isMoving) return;
bIsOnTheMove = isMoving;
// (de)activate the stick accordingly
stick.gameObject.SetActive(!isMoving);
// Do this ONCE when ball becomes stanionary
if(!isMoving)
{
var ballPosition = transform.position;
// among the wall colliders find which one is closest to the ball
Vector3 closestPoint;
var smallestDistance = float.PositiveInifinity;
foreach(var wall in wallColliders)
{
var edgePoint = wall.ClosestPoint(ballPosition);
var distane = (edgePoint - ballPosition).sqrMagnitude;
if(distance < smallestDistance)
{
closestPoint = point;
smallestDistance = distance;
}
}
// then make the stick look towards the ball from that edge
var direction = ballPosition - closestPoint;
var rotation = Quaternion.LookRotation(direction);
// optional add the offset
rotation *= Quaternion.Euler(eulerOffset);
stick.rotation = rotation;
}
}
I'm trying to assign a skill to a projectile that, when used, divides the project into 3 (the original projectile and 2 more new ones).
However, when I instantiate these two clones, they keep following the same trajectory. The idea would be for them to take this route:
The green dotted curve indicating the motion of the original bullet, the blue vector indicating the instantaneous velocity of the original bullet at time of special activation, the red vectors indicating the two velocity vectors belonging to each of the newly spawned bullets, and the green angle indicating the direction of the new bullet relative to the original velocity direction
But at the moment, they spawn and continue following the same trajectory as the original. The sprites even rotate to the right angle, but that doesn't seem to make much difference in how the physics is applied.
Does anyone know how I can solve this?
This is my code so far
Ability Script:
public class AirSpecialSplit : MonoBehaviour, IAirSpecial
{
public float SplitAngleInDegrees = 10;
GameObject bird_down;
GameObject bird_up;
public void ExecuteAirSpecial()
{
{
//hold the velocity of the original bird
Vector2 original_velocity = this.gameObject.GetComponent<Rigidbody2D>().velocity;
//clone two new birds
bird_down = Birb.MakeBirbCopy(this.gameObject);
bird_up = Birb.MakeBirbCopy(this.gameObject);
//apply the angle to the clones
bird_down.transform.rotation = Quaternion.AngleAxis(-SplitAngleInDegrees, Vector2.up);
bird_up.transform.rotation = Quaternion.AngleAxis(SplitAngleInDegrees, Vector2.up);
//get the rigidboy from the clones
Rigidbody2D rb_bird_down = bird_down.GetComponent<Rigidbody2D>();
Rigidbody2D rb_bird_up = bird_up.GetComponent<Rigidbody2D>();
rb_bird_down.simulated = true;
rb_bird_up.simulated = true;
rb_bird_down.velocity = new Vector2(original_velocity.x, original_velocity.y);
rb_bird_up.velocity = new Vector2(original_velocity.x, original_velocity.y);
}
}
}
Well you apply the same velocity to both so of course they will move in the same direction. The velocity is in world space!
You probably wanted to rather add the rotation like e.g.
rb_bird_down.velocity = bird_down.transform.forward * original_velocity.magnitude;
And before that you probably should take the current bullet rotation into account like
bird_down.tranform.rotation = transform.rotstion * Quaternion.Euler(0,0, -SplitAngleInDegrees);
I’m looking for a way to compare the speeds of two gameObjects on colliding. So if the speed of the object is faster than the object it has collided with it ApplysBreak. However when I try and access the variables for speed for each gameobject I get a null reference.
Well what I’m trying to do is have cars travelling on the road. The cars have a trigger at the front and a hitbox at the back of the cars. What I want to happen is when the trigger hits the hitbox it compares the Variables for speed of the two cars in the collision, i.e. the make the car at the back slow down or equal the speed of the car in the front. However, what I am having trouble with is referencing the speed of both cars and comparing them. This is due to them both being prefabs using the same script. Which I’m learning means that I’m just referencing the same script and not the actual values of the individual car.
private void OnTriggerStay2D(Collider2D collision)
{
Debug.Log("TriggerStay");
Traffic Col_speed = collision.gameObject.GetComponent<Traffic>();
if (speed > Col_speed.speed)
{
Accelerate = false;
ApplyBreak = true;
}
else if (speed< Col_speed.speed)
{
Accelerate = false;
ApplyBreak = false;
speed = Col_speed.speed;
}
}
If you use the collision and not the trigger, then the 'collision' passed in to the OnCollisionEnter() function has a property Impulse, which gives you an idea of the difference in speed. It also has a GetContact() to find where exactly the touched.
https://docs.unity3d.com/ScriptReference/Collision.html
With triggers, it is a little different. You need to calculate the speed of the items on the collisionpoint. Trouble is, that the OnTriggerEnter() and OnTriggerStay() methods do not supply contact points.
If you items are of similar size, you could simply assume the point in the middle to be the contact point, then calc the speed on these. Use the Rigidbody2D's velocity and angularVelocity to do this. If your objects have a simple shape, such as rectangular cars, you might make some own code to figure out a more useful contactpoint.
The code in the this sample assumes a simple contact point in the middle of the two items and then calculates the velocity of both ends of it. If you are not using Rigidbody2D on the same object, but in some parent, you might need to change this a little. When getting a rigidbody from a parent, be careful to use the transform.position of the same gameObject that has the rigidbody, not the one that has the collider!
using UnityEngine;
public class TriggerHitSpeed : MonoBehaviour
{
public Vector3 hit_v1;
public Vector3 hit_v2;
private Rigidbody2D rgb;
private void Start()
{
rgb = GetComponent<Rigidbody2D>();
if(rgb==null)
Destroy(this);
}
private void OnTriggerEnter2D(Collider2D collision)
{
Vector3 hitpoint;
// unlike OnCollisionEnter2D(), trigger functions don't get a contact point
// simply assume middle of centers. For a CollisionEnter, can use the contact point from the collision.
hitpoint = (collision.transform.position+transform.position)*0.5f;
// speed of self at hitpoint
Vector3 rotspeed;
Vector3 v1,v2;
Vector2 speed;
if(rgb!=null)
{
rotspeed = Vector3.forward * rgb.angularVelocity*Mathf.Deg2Rad; // for 3D object, use angularVelocity directly.
speed = rgb.velocity;
v1 = Vector3.Cross( rotspeed , hitpoint-transform.position );
v1 += new Vector3(speed.x,speed.y,0f);
}else
v1 = Vector3.zero;
// speed of other at hitpoint
if(collision.attachedRigidbody!=null)
{
rotspeed = Vector3.forward * collision.attachedRigidbody.angularVelocity*Mathf.Deg2Rad;// for 3D object, use angularVelocity directly.
speed = collision.attachedRigidbody.velocity;
v2 = Vector3.Cross( rotspeed , hitpoint-collision.transform.position );
v2 += new Vector3(speed.x,speed.y,0f);
}else
v2 = Vector3.zero;
// can now look at the difference and do something with it.
// v2-v1;
// [...]
hit_v1 = v1;
hit_v2 = v2;
}
}
The code that considers the rotation makes it nicer and more pricise, in case your cars are spinning out of control and then hit someone, bumping him sideways.
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)