I have a 2D scene that consists of 4 sprites, each with a box collider.
I then have a prefab that is simply a sprite of a circle with a 2D circle collider.
Finally I have a script named Atmosphere attached to my main camera that will fill the box with lots of instances of the prefab and give them a velocity in a random direction.
using UnityEngine;
public class Atmosphere : MonoBehaviour
{
public GameObject Molecule;
void Start()
{
float x = -4.5f;
while (x < 4.5f)
{
float y = -4.5f;
while (y < 4.5f)
{
var inst = GameObject.Instantiate(Molecule);
inst.transform.position = new Vector3(x, y, 0);
var rb = inst.GetComponent<Rigidbody2D>();
float xForce = Random.value * 2f - 1f;
float yForce = Random.value * 2f - 1f;
rb.AddForce(new Vector2(xForce, yForce) * 100f);
y += 0.5f;
}
x += 0.5f;
}
}
}
For a while the dots bounce around against each other and the edges of the box.
But after a while they eventually get stuck to the edges of the box.
Both the box and balls and the box walls have the same physics material
Which has zero friction and a bounciness of 1.
Why do they stick to the walls, and how can I stop it from happening?
Download Unity 3D demo
UPDATE
If I drop a single molecule into the box with the following script attached it gets stuck to the wall immediately.
[RequireComponent(typeof(Rigidbody2D))]
public class Molecule : MonoBehaviour
{
Rigidbody2D RigidBody2D;
void Start()
{
transform.position = new Vector3(0, 1, 0);
RigidBody2D = GetComponent<Rigidbody2D>();
float xForce = Random.value * 2f - 1f;
float yForce = Random.value * 2f - 1f;
RigidBody2D.AddForce(new Vector2(-.25f, -0.25f) * 100f);
}
}
Unity3D has a velocity threshold. Objects with a velocity below this threshold won't bounce etc.
The solution is to go to Edit > Project Settings > Physics 2D and to set the Velocity Threshold lower.
EDIT - The real solution:
So I was doing some reading on integration techniques and it hit me: The energy you're losing is quite likely coming from numerical inaccuracies stemming from several approximation techniques compounded.
I'm guessing the continuous collision detection algorithm is mostly to blame, floating-point error accumulation, but probably also from numerical integration techniques.
What you're trying to achieve is elastic collision, which means that it satisfies conservation of energy as well as momentum. The trick is going to be for you to add a script to every object that preserves the energy of the system by adding error-correcting velocity over time. Whether you actually use the energy route or not is up to you, there are a number of ways to track and modify the energy resulting in a change of velocity. Momentum would be the would be the easiest to track in my opinion. What you're trying to achieve is elastic collision, which means that it satisfies conservation
i.e.
Make a component that preserves the scalar quantity of kinetic energy or momentum (it'll probably produce the same results)
When you initialize all the particles with a velocity, make sure to save the starting momentum in the particle's script. I would save the scalar quantity for simplicity, so just use the length of velocity
Every frame, in the update event check the velocity of the rigid body. Find the change in momentum or change in energy and add the -difference and apply it to the object.
I doubt you'll have to work on collision events as well, especially if you use the exact same mass for every object. Here's a link with an example where someone corrected unity's friction-less physics using conservation of energy https://answers.unity.com/questions/168393/frictionless-physics.html
colliders may be stuck in each other for the reason of low contact offset, go to edit-project settings-physics2d and increase the default contact offset a little bit, 0.12 for example, it was work for me.
cheers
Related
So I have a script for a day/night cycle attached to the directional light in unity. It slowly rotates the light which creates an effective day/night cycle. There's an event that I want to call once every sunset, or more specifically when the x rotation of the light is at 200 degrees. The problem is my script rotates a little bit each frame, according to Time.deltatime which is obviously not perfectly consistent. Because of this, I might be at a rotation just below 199 and then at the next frame, I might be at a rotation just above 200 degrees, overshooting it so that it's never actually 200 degrees. I tried to get around this by checking if the x rotation is above 200 AND the x rotation - my rotate amount in that frame is below 200, then calling the event. That was the idea but it didn't work for some reason. It never calls the event. Here's my script.
using UnityEngine;
using UnityEngine.Events;
public class DayNightCycle : MonoBehaviour
{
public TerrainGenerator terrainGenerator;
public float dayLength = 3;
float rotationSpeed;
public UnityEvent night;
public float timeNightStarts = 200;
// Start is called before the first frame update
void Start()
{
rotationSpeed = 360 / (dayLength * 60);
}
// Update is called once per frame
void Update()
{
if (terrainGenerator.mapLoaded)
{
Vector3 rotateAmount = Vector3.right * rotationSpeed * Time.deltaTime;
transform.Rotate(rotateAmount, Space.World);
float xRotation = transform.eulerAngles.x;
if (xRotation >= timeNightStarts && xRotation - rotateAmount.x < timeNightStarts)
{
night.Invoke();
}
}
}
}
The problem you are facing is expected since Unity uses Quaternions under the hood and quaternion to euler conversions are not stable.
Quote from Unity docs:
When you read the .eulerAngles property, Unity converts the
Quaternion's internal representation of the rotation to Euler angles.
Because, there is more than one way to represent any given rotation
using Euler angles, the values you read back out may be quite
different from the values you assigned. This can cause confusion if
you are trying to gradually increment the values to produce animation.
To avoid these kinds of problems, the recommended way to work with
rotations is to avoid relying on consistent results when reading
.eulerAngles particularly when attempting to gradually increment a
rotation to produce animation. For better ways to achieve this, see
the Quaternion * operator.
If you want to avoid Quaternions, you can represent the eulerX angle as a float variable in your code. Increment its value, always set the transform.euler.x from it, but never read it back from the transform. If no other script or physics affects your transform (which should be the case for sun) you will be fine.
I'm trying to figure out simple custom 2D physics for platformer. At the moment I'm using ray casts to figure out collision between the player and map. However using ray cast has some problems. For example, if the player is falling (has somewhat high downwards velocity) its unable to pick up holes in the wall because it moves past them due to going too far down to be detected as empty space.
One solution is to move to tile based system instead of using ray casts but I would preferably not to do so.
So I'm wondering is there some kinda solution to figure out empty holes in wall, even small edge ones without huge performance impact.
High quality drawn illustration, assume leftwards velocity:
Physics2D.BoxCast is how I would tackle this. It does what you expect, instead of a ray it calculates as if a full box was traversing the distance.
Usage is very similar to raycasting. Example:
public Collider2D mainCollider;
public Vector2 velocity;
void Update() {
Vector2 origin = new Vector2(transform.position.x, transform.position.y);
Vector2 size = mainCollider.bounds.size;
Vector2 direction = velocity * Time.deltaTime;
float maxDist = velocity.magnitude;
var hit = Physics2D.BoxCast(origin, size, 0, direction, maxDepth);
if (hit == null) {
transform.position += direction;
} else {
transform.position += direction.normalized * hit.distance;
}
}
If boxes are not your taste, there's also a Physics2D.CapsuleCast and Physics2D.CircleCast.
I create a game and I need use inertia for object.
Example:
The image shows all what I need.
When I touch on screen, blueObject no longer uses the position of brownObject and rotation of redObject. And I add component Rigidbody. The object just falls down. I need him to fall further along his trajectory (inertia).
I tried to use addForce(transform.forward * float), this not work.
By setting the position of the transform, you don't use Unity Physics engine. Your cube must have a rigidbody from the begin of the simulation and what you need here is a spring joint (https://docs.unity3d.com/Manual/class-SpringJoint.html) or a fixed joint.
You need to calculate the current speed, when releasing the object.
Track the positions over the last frame & current frame, and use Time.deltaTime to compensate different frame-rates.
Then set this velocity to your objects rigidbody. (AddForce is just manipulating the velocity, but depending on the ForceMode it respects mass etc.)
public Vector3 lastPosition = Vector3.zero;
void Update()
{
// maybe do : if(lastPosition != Vector3.zero) to be sure
Vector3 obj_velocity = (lastPosition - transform.position) * Time.deltaTime;
lastPosition = transform.position;
// if you release the object, do your thing, add rigidbody, then:
rb.velocity = obj_velocity;
}
That should create the "inertia". the velocity contains the direction and the speed.
I am trying to build a 2D video game using JavaFX. There is a submarine at the bottom of the screen that fires torpedos. I have noticed that if I fire a torpedo on an Angle it travels at a faster rate, than if say I fired it straightup where is goes directly North. I'm assuming the difference in rates is because the screen is a rectangle, and when moving on a diagonal you are covering more distance.
Are there techniques to account for this, so you can get a constant rate of movement, regardless of the angle of travel of the projectile? I am writing the game using JavaFX under Eclipse.
Appreciate any help with this!
Thanks!
Since you are already firing in different directions, I'm assuming you have a velocity vector defined by two components (x, y). In JavaFX such a vector can be represented by Point2D. Given the speed you want the projectile to travel at, you can do the following:
public void fire() {
// the point at which you are shooting
Point2D target = ...
// the point from where you are shooting
Point2D source = ...
// the projectile speed per frame
double speed = ...
// velocity vector to be used to move
// the projectile at constant speed per frame
Point2D velocity = target.subtract(source).normalize().multiply(speed);
}
UPDATE: (included vector generation from angle)
In case you do not know the target point, you can obtain the vector from the angle you use to shoot:
public void fire() {
double angleDegrees = ...
double angleRadians = Math.toRadians(angleDegrees);
// the point from where you are shooting
Point2D source = ...
// the projectile speed per frame
double speed = ...
Point2D shootingVector = new Point2D(Math.cos(angleRadians), Math.sin(angleRadians));
// velocity vector to be used to move
// the projectile at constant speed per frame
Point2D velocity = shootingVector.normalize().multiply(speed);
}
I am having trouble keeping game objects inside of a contained space. When they reach the edge, there is some momentary push back but then they will go right through the wall.
I am using a Box Collider on the player, and a Mesh Collider for the level's wall. I am having issues with both a Player Character (a space ship) that the movement is controlled by the player. And with projectiles, which are fire and forget moving at a constant speed.
This is my movement code for my player. It is being run in the FixedUpdate() function.
//Movement
haxis = Input.GetAxis("Horizontal") * speed;
vaxis = Input.GetAxis("Vertical") * speed;
moveVector.x = haxis;
moveVector.z = vaxis;
if(moveVector.magnitude > 1)
{
moveVector.Normalize();
}
rigidbody.MovePosition(transform.position + moveVector * speed);
With the bullets, they are given a velocity and the engine calculates their moviements. They are using Box Collider and it is set as a Trigger so they don't have physics. But I use OnTriggerEnter to destroy them.
//Projectiles without physics collisiions
function OnTriggerEnter (other : Collider) {
Destroy(gameObject);
}
Some, but not all of the bullets will be destroyed when hitting the mesh collider wall. The player will sometimes hit it and stop, but can usually push through it. How can I make the collisions with the mesh collider work every time?
Collision with fast-moving objects is always a problem. A good way to ensure that you detect all collision is to use Raycasting instead of relying on the physics simulation. This works well for bullets or small objects, but will not produce good results for large objects.
http://unity3d.com/support/documentation/ScriptReference/Physics.Raycast.html
Pseudo-codeish (I don't have code-completion here and a poor memory):
void FixedUpdate()
{
Vector3 direction = new Vector3(transform.position - lastPosition);
Ray ray = new Ray(lastPosition, direction);
RaycastHit hit;
if (Physics.Raycast(ray, hit, direction.magnitude))
{
// Do something if hit
}
this.lastPosition = transform.position;
}
I have a pinball prototype that also gave me much trouble in the same areas. These are all the steps I've taken to almost (but not yet entirely) solve these problems:
For fast moving objects:
Set the rigidbody's Interpolate to 'Interpolate' (this does not affect the actual physics simulation, but updates the rendering of the object properly - use this only on important objects from a rendering point of view, like the player, or a pinball, but not for projectiles)
Set Collision Detection to Continuous Dynamic
Attach the script DontGoThroughThings (https://www.auto.tuwien.ac.at/wordpress/?p=260) to your object. This script cleverly uses the Raycasting solution I posted in my other answer to pull back offending objects to before the collision points.
In Edit -> Project Settings -> Physics:
Set Min Penetration for Penalty to a very low value. I've set mine to 0.001
Set Solver Iteration Count to a higher value. I've set mine to 50, but you can probably do ok with much less.
All that is going to have a penalty in performace, but that's unavoidable. The defaults values are soft on performance but are not really intented for proper simulation of small and fast-moving objects.
How about set the Collision Detection of rigidbody to Continuous or Continuous Dynamic?
http://unity3d.com/support/documentation/Components/class-Rigidbody.html
So I haven't been able to get the Mesh Colliders to work. I created a composite collider using simple box colliders and it worked exactly as expected.
Other tests with simple Mesh Colliders have come out the same.
It looks like the best answer is to build a composite collider out of simple box/sphere colliders.
For my specific case I wrote a Wizard that creates a Pipe shaped compound collider.
#script AddComponentMenu("Colliders/Pipe Collider");
class WizardCreatePipeCollider extends ScriptableWizard
{
public var outterRadius : float = 200;
public var innerRadius : float = 190;
public var sections : int = 12;
public var height : float = 20;
#MenuItem("GameObject/Colliders/Create Pipe Collider")
static function CreateWizard()
{
ScriptableWizard.DisplayWizard.<WizardCreatePipeCollider>("Create Pipe Collider");
}
public function OnWizardUpdate() {
helpString = "Creates a Pipe Collider";
}
public function OnWizardCreate() {
var theta : float = 360f / sections;
var width : float = outterRadius - innerRadius;
var sectionLength : float = 2 * outterRadius * Mathf.Sin((theta / 2) * Mathf.Deg2Rad);
var container : GameObject = new GameObject("Pipe Collider");
var section : GameObject;
var sectionCollider : GameObject;
var boxCollider : BoxCollider;
for(var i = 0; i < sections; i++)
{
section = new GameObject("Section " + (i + 1));
sectionCollider = new GameObject("SectionCollider " + (i + 1));
section.transform.parent = container.transform;
sectionCollider.transform.parent = section.transform;
section.transform.localPosition = Vector3.zero;
section.transform.localRotation.eulerAngles.y = i * theta;
boxCollider = sectionCollider.AddComponent.<BoxCollider>();
boxCollider.center = Vector3.zero;
boxCollider.size = new Vector3(width, height, sectionLength);
sectionCollider.transform.localPosition = new Vector3(innerRadius + (width / 2), 0, 0);
}
}
}
1.) Never use MESH COLLIDER. Use combination of box and capsule collider.
2.) Check constraints in RigidBody. If you tick Freeze Position X than it will pass through the object on the X axis. (Same for y axis).
Old Question but maybe it helps someone.
Go to Project settings > Time and Try dividing the fixed timestep and maximum allowed timestep by two or by four.
I had the problem that my player was able to squeeze through openings smaller than the players collider and that solved it. It also helps with stopping fast moving objects.
Edit ---> Project Settings ---> Time ... decrease "Fixed Timestep" value .. This will solve the problem but it can affect performance negatively.
Another solution is could be calculate the coordinates (for example, you have a ball and wall. Ball will hit to wall. So calculate coordinates of wall and set hitting process according these cordinates )
Try setting the models to environment and static. That fix my issue.