In my 2D Unity game, balls can bounce off each other. My goal is to have them bounce off each other as you would expect physically in the real world.
The only difference is that the speed of the balls decreases, but they can never come to a stop.
I have different approaches to map the physical behavior. But none of them leads to a good result.
(1)
private void OnCollisionEnter2D(Collision2D collision) {
var point = collision.GetContact(0);
var curDire = transform.TransformDirection(Vector2.up);
var newDir = Vector2.Reflect(curDire, point.normal);
transform.rotation = Quaternion.FromToRotation(Vector2.up, newDir);
speed *= 0.75f;
}
I don't know how describe exactly: It can happen that the balls push past each other. They collide, but do not bounce off each other, but maintain their direction.
Another approach I have read about is to add an AddForce to the velocity of Rigidbody2D.
Then the following code should do the physics.
private void Start()
{
rb = GetComponent<Rigidbody2D>();
_velocity = new Vector3(1f, 1f, 0f);
_rb.AddForce(_velocity, ForceMode2D.Force);
}
private void OnCollisionEnter2D(Collision2D collision) {
var inDirection = GetComponent<Rigidbody2D>().velocity;
var inNormal = collision.contacts[0].point;
var newVelocity = Vector2.Reflect(inDirection, inNormal);
rb.velocity = newVelocity;
}
The results are really random. I think I use a wrong AddForce or the velocity-Vector is strange. I fall here the deeper mathematical understanding to fully comprehend this solution.
(3) Then I read about Physic Material 2D. This sounds best. I created one (Asset -> Create -> 2D -> Physic Material 2D). Set bounciness to 1 the friction to 0.1. I select my Ball-prefab and add the physic material to my collider. I added it to the Rigidbody2D, too. But this asset has no effect. (My balls have no gravity. I don't know if this is important.)
I imagine Unity has a super simple solution for this problem. But I just can't find it. Does anyone have any ideas or approaches for me? I actually just want shot balls to bounce off each other. This should look as much as possible like it would look in real life.
Update Question
I move the ball like that
private Vector3 _inDirection;
private void Start()
{
_inDirection = Vector3.right * (speed * Time.deltaTime * (_didCollide ? -1 : 1));
}
private void FixedUpdate()
{
transform.Translate(_inDirection);
}
Related
I'm trying to do a little game on mobile using Unity and I've got a problem with the rotation of a maze.
To add context :
When your moving your finger on the screen, the maze is rotating on himself. There is a ball in it and you need to make it go on a cell to win the level.
When the maze is rotating too fast, the ball falls down and go through the ground and I don't know how to fix it.
I tried to play with the gravity, colliders...
This is the same when the ball is jumping (when the maze is going up and down quickly).
For the moment I just reset the ball position when you're falling.
{
ball.transform.position = new Vector3(0, 2, 0);
maze.transform.position = Vector3.zero;
maze.transform.rotation = Quaternion.identity;
}
Do you guys have some ideas ? Thanks
I had a similar problem in a tilt maze mini-game I worked on. Ideally implementing jkimishere's solution will work but I assume the maze is moving too fast for the collisions to register properly. You'll need to smooth the maze's rotation with a Lerp. In our case we had pressure plates with a tilt value, so it doesn't directly translate to your mobile use but perhaps give you a nudge in the right direction. We used:
public GameObject maze;
private float _lerpTime;
private bool _isRotating;
private Quaternion _startingRot, _desiredRot;
private void Awake()
{
_startingRot = maze.transform.localRotation;
}
private void Update()
{
//Don't want to move the maze if we don't ask for it
if(!_isRotating)
return;
//Lerp the maze's rotation
_lerpTime = Mathf.Clamp(_lerpTime + Time.deltaTime * 0.5f, 0f, 1f);
maze.transform.localRotation = Quaternion.Lerp(_startingRot, _desiredRot, _lerpTime);
//Once the maze gets where it needs to be, stop moving it
if(affectedObject.transform.localRotation.Equals(_desiredRot)
_isRotating = false;
}
private void ActivateTilt()
{
//Set the new starting point of the rotation.
_startingRot = maze.transform.localRotation;
//However you want to calculate the desired rotation here
//Reset our lerp and start rotating again
_lerpTime = 0f;
_isRotating = true;
}
This will ease the rotation of your maze over time. So that the ball can adapt to the new collider positions.
In the rigidbody(for the ball), make the collision detection to continuous, and in the rigidbody for the maze(if you have one) set the collision detection to continuous dynamic. Hope this helps!
I think that is unavoidable if you allow the player to move the platform freely. I suggest you restrain the speed at wich the player can tilt the platform. This way, the ball will have more frames to adapt to the new slope
I'm currently setting the rotation of a player object so that it "stands up" relative to the spheroid the user stands on.
However, that will break Rigidbody physics.
I already have the direction of gravity as a Quaternion, how can add torque to the Rigidbody so that it aligns with that direction?
Here's my code on Github, which does allow the player to move around the surface of the planet.
I have tried various approaches using AddTorque and Vector3.Cross, but they have all only ended in the Rigidbody spinning wildly.
Instead of using transform whenever a Rigidbody is involved rather first calculate the needed final Quaternion ad then apply it using theRigidbody.MoveRotation which does not break the physics.
The same also accounts to the position where you should rather use theRigidbody.MovePosition.
Both should be used in FixedUpdate so
either move the entire code from Update to FixedUpdate
or split the input and the physics and do your API stuff in Update, store the relevant values into fields but apply anything related to the Rigidbod transformations reactive in FixedUpdate.
Your code is quite complex so I will only give an example as a pseudo code and hope you can take it from there
So instead of doing e.g.
private void Update ()
{
player.transform.position = XYZ;
// Applies a world space rotation
player.transform.rotation = XYZW;
// Adds a relative rotation in local space
player.transform.Rotate(x,y,z);
}
You would rather do something like
[SerializeField] Rigidbody playerRb;
private Vector3 targetPosition;
private Quaternion targetRotation;
private void Awake ()
{
if(!playerRb) playerRb = player.GetComponent<Rigidbody>();
}
private void Update ()
{
targetPosition = XYZ;
targetRotation = XYZW * Quaternion.Euler(x,y,z);
}
private void FixedUpdate ()
{
playerRb.MovePosition(targetPosition);
playerRb.MoveRotation(targetRotation);
}
I've got a bullet script with a particle system and a decal.
I think that it has something to do with these events not being able to fire in time or with fps in update. Not sure yet.
So, it's being late.
The ellow points are where the particles start to play. They should be right on these wooden walls. There should be three particles working and three bullet holes, kinda bullet penetrating one wall and getting destroyed on the second one.
THE QUESTION IS HOW TO MAKE IT WORK NORMAL, SO THAT THE TRIGGERS WORK WHEN NEEDED AS WELL AS THE PARTICLES AND THE DECALS? Maybe there's a way to excellerate the code to work on time? Or maybe there's another problem with that?
The screenshot:
The Code:
public class BulletScript : MonoBehaviour {
public bool isThrough = true;
public float BulletSpeed = 100;
public float CurrentDamage;
public float EnterLuft = -0.005f;
public float ExitLuft = 0.05f;
public GameObject woodParticle;
private ContactPoint CollisionPoint;
public GameObject BulletMarkPref;
Rigidbody bullet;
private void Start()
{
bullet = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
bullet.velocity = Vector3.forward * BulletSpeed;
//this.transform.Translate(Vector3.forward * BulletSpeed * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
Transform hitPoint = this.transform;
LevelObject.LvlObjectType objectType = other.gameObject.GetComponent<LevelObject>().objType;
if(objectType == LevelObject.LvlObjectType.obstacle)
{
if (isThrough)
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(this.transform, true);
}
else
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
else if(objectType == LevelObject.LvlObjectType.obstacle)
{
Destroy(this.gameObject);
}
else if(objectType == LevelObject.LvlObjectType.wall)
{
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
private void OnTriggerExit(Collider other)
{
Transform hitPoint = this.transform;
Instantiate(woodParticle, hitPoint.localPosition, hitPoint.rotation).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, false);
}
void LeaveBulletMark(Transform hitPoint, bool ifEnter)
{
GameObject TemporaryBulletMarkHandler;
TemporaryBulletMarkHandler = Instantiate(BulletMarkPref, hitPoint.localPosition, Quaternion.LookRotation(ifEnter ? hitPoint.forward : CollisionPoint.normal)) as GameObject;
isThrough = false;
TemporaryBulletMarkHandler.transform.Translate(hitPoint.forward * (ifEnter ? 0.005f : -0.005f));
}
}
I don't think your problem is something simple with the code. There is an inherent issue with calculating fast moving objects like bullets with true physics calculations especially if they are small. Often between physics updates, they pass through wall colliders completely without registering.
You have to think of it like this to see the problem. The bullet isn't tracked continuously along its trajectory. It has a starting location, a formula for its movement and it calculates a new location at each physics update. You could fire a bullet straight at a wall, and in one update the bullet would be several meters in front of the wall, and in the next, it would be several meters behind the wall without ever triggering a collision. This is why so many game use ray tracing to calculate bullet trajectories. The bullet movement isn't perfectly accurate, especially for long shots, but obstructions to the bullet path are registered.
By default unity's Physics Engine runs at 50 frames per second. A modern bullet travels between 1200 and 1700 m/s. That gives you a distance between 24 and 34 meters traveled between frames. Even a small object falling at terminal velocity (54 m/s) might pass through a collider unnoticed. If you made a 1-meter thick box collider, you would likely register a falling object but not a bullet.
I think you could do some clever combination of ray tracing and bullet physics to get the best of both worlds. Maybe you could ray trace from the bullet at each fixed update or there may be some better technique already invented for this exact situation that I don't know about.
I'm making 2D mobile game and I have a ball, but my ball doesn't have a constant moving speed. What I need to do?
When I build game on my android device, ball have a various speed. In that case, my game is not playable.
I hope that someone can help me. Thanks.
This is on my start function:
void Start () {
GetComponent<Rigidbody2D> ()
.AddForce (new Vector2 (1f, 0.5f)* Time.deltaTime * force);
}
Is it a good practice if I used in code " Application.LoadLevel ("__Scena_0"); " to load existing scene when I lose " life" ? Problem happens when I lost " life " and try playing game in second chance.
My update function is about OnTriggerEnter2D when my ball hit trigger objects.
EDIT 23.12.2015. : problem solve with adding physics material (fiction 0) and adding to script:
using UnityEngine.SceneManagement;
...
SceneManager.LoadScene ("MainScene");
The problem is the calculation of the force vector:
new Vector2 (1f, 0.5f) * Time.deltaTime * force
You are using Time.deltaTime as a factor, which means that the applied force is not constant, but depending on the deltaTime, which is the duration of the last frame. This explains why it changes randomly.
I don't think Time.deltaTime is what you want here, try just removing the factor and adjusting the "force" factor accordingly. You should now have a constant force applied, independent from the platform you play on.
Create a new Physics Material, set friction zero and add it to your object. If your object has no friction, its speed cannot be decreased. Then, use AddForce() on Rigidbody2D to speed up.
Assuming that your ball and colliding walls have bouncy materials with no friction. Try
public float _ballSpeed = 2.5f;
Rigidbody2D _rb;
void Start()
{
_rb = GetComponent<Rigidbody2D>();
_rb.velocity = Vector2.one * _ballSpeed;
}
void Update()
{
}
From this you can control ball speed through _ballSpeed
//========== OR ==========//
If you want to retain speed even after ball destruction.
Retain _ballSpeed in any global static variable.
Suppose you have a class named Globals, declare a variable like,
public class Globals
{
public static float BALL_SPEED = 2.5f;
}
Now in Ball Script
Rigidbody2D _rb;
void Start()
{
_rb = GetComponent<Rigidbody2D>();
_rb.velocity = Vector2.one * Globals.BALL_SPEED;
}
void Update()
{
}
In Unity, I want to create a platform that rotates like a propeller. When it hits an object, I want that object to go flying in a logical direction. If I just update the object's rotation every frame, the object tends to stick to the platform and pass through it at higher speeds. I'm thinking that Unity's physics would be the best solution - how can I rotate the platform at a constant speed such that it can do what I want? In addition, how can I start and stop the spinning without it speeding up or slowing down? I'm using C#.
If you're looking to incorporate Unity's physics into your platform behaviour, but don't want to deal with adding force/torque to change its rotational speed, consider using Rigidbody.angularVelocity. With this, you can also start and stop the rotation instantly (use FixedUpdate() when you're working with a RigidBody).
So your code may look like:
Vector3 activeVelocity = new Vector3(0, 10, 0);
bool isStopped = false;
RigidBody rBody;
void Start() {
rBody = GetComponent<Rigidbody>();
}
void FixedUpdate() {
if (!isStopped){
rBody.angularVelocity = activeVelocity;
}
else{
rBody.angularVelocity = Vector3.zero;
}
}
public void ActivateRotation() {
isStopped = false;
}
public void FreezeRotation() {
isStopped = true;
}
Hope this helps! Let me know if you have any questions.