Ball Mechanics - Is this the best approach? - unity3d

Good day,
I'd like to program a constantly moving ball (object3) being passed between two stationary objects (object1, object2), with the ability to set the max height Y of the pass trajectory dynamically.
What would you argue is the best way to program the ball physics for this concept?
I've looked at using addForce on a default sphere w/ a rigidbody. It seems like there should be an equation that expresses the trajectory of a pass of object3 from object1's x to object2's x... at a known, given speed, with a known, set mass, and a known gravity environment.
However, currently I have a Vector3.Lerp interpolating the ball between the two objects on each FixedUpdate() with t expressed as:
`(Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;`
It works and all, but with this approach, it seems there's no clear way to add height to the trajectory of the ball path. I've considered adding the height to the Y value in object2 until the ball is half way there, and then setting it back to the original Y position... but it just feels wrong! Thoughts?
Thanks!

Okey so if I understand you correctly currently you are doing
privte void FixedUpdate()
{
var factor = (Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;
object1.MovePosition(Vector3.Lerp(object2.position, object3.position, factor));
}
which moves the ball pingpong between object1 and object2 position but only planar.
Assuming for now the objects will only be moving within the XZ plane and never have different Y position in order to rather get a curve with height you could treat the separatly:
- Interpolate between both positions as before
- Separately calculate the Y position with sinus or any other mathematical curve function - for realistic physics probably rather a parabola actually
Could look somhow like
public class Example : MonoBehaviour
{
public Rigidbody object1;
public Transform object2;
public Transform object3;
// adjust in the Inspector
public float speed = 1;
public float Amplitude = 0;
// Just for debug
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// This always returns a value between 0 and 1
// and linearly pingpongs forth and back
linearFactor = Mathf.PingPong(Time.time * speed, 1);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 (no need for +1 and /2 anymore)
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(linearFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// As before interpolate between the positions
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.MovePosition(new Vector3(position.x, yPosition, position.z));
}
}
You could optionally also try and add some dumping in the Y direction in order to make the vertical movement more realistic (slow down when reaching the peak). I tried a bit using inverted SmoothStep like
// just for debug
[Range(0, 1)] [SerializeField] private float dampedSinusFactor;
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// Use two different factros:
// - a linear one for movement in XZ
// - a smoothed one for movement in Y (in order to slow down when reaching the peak ;) )
linearFactor = Mathf.PingPong(Time.time * speed, 1);
dampedSinusFactor = InvertSmoothStep(linearFactor);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 ()
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(dampedSinusFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.position = new Vector3(position.x, yPosition, position.z);
}
// source: https://stackoverflow.com/a/34576808/7111561
private float InvertSmoothStep(float x)
{
return x + (x - (x * x * (3.0f - 2.0f * x)));
}
However for slow movements this looks a bit strange yet. But you can come up with any other maths curve that results in the expected behavior for x=[0,1] ;)

Related

How can I get smooth random movement (2D Unity)?

I'm using Random.onUnitSphere to simulate bubbles floating around jostling for position. It works well although the movement is too "jerky". I'd like to create the same effect but slow it down and make smoother random movements. Is there anyway I can easily achieve this? Here's my code:
private void Update()
{
if (floaty == true)
{
rb.AddRelativeForce(Random.onUnitSphere * speed);
speed = 0.06f;
}
}
A Perlin noise random walker should work
//There are probably better ways to do this.
Vector3 RandomSmoothPointOnUnitSphere(float time)
{
//Get the x of the vector
float x = Math.PerlinNoise(time, /* your x seed */);
//Get the y of the vector
float y = Math.PerlinNoise(time, /* your y seed */);
//Get the z of the vector
float z = Math.PerlinNoise(time, /* your z seed */);
//Create a vector3
Vector3 vector = new Vector3(x, y, z);
//Normalize the vector and return it
return Vector3.Normalize(vector);
}
And in the Update function
if (floaty)
{
//Get the vector
Vector3 movementvector = RandomSmoothPointOnUnitSphere(Time.time);
//You can also use CharacterController.Move()
transform.Translate(movementvector * Time.deltatime);
}
I should also mention that this approach shouldn't work with RigidBody.ApplyForce(), but I don't usually use Unity's default physics, so it may. Regardless, it shouldn't change anything.

Lerp not smooth when velocity changes abruptly

I have a object that moves at a variable speed. It can reach from 0 speed units up to N speed units.
I'm lerping the camera angle to rotate and look at the object smoothly and nice, like this:
camera.eulerAngles = Vector3.Lerp(initialAngle, finalAngle, speed / N);
The problem is when the object collides and the velocity decrease instantaneously to 0, the lerp happens very abruptly.
How can I make it handle this situation? I'd like a fast interpolation in this case, but no instantaneous like I'm seeing.
You can clamp the speed delta per iteration:
private const float MAX_DELTA = 0.1F;
private float prevSpeed;
private void UpdateRotation() {
float currentSpeed = Mathf.Clamp(speed, prevSpeed - MAX_DELTA, prevSpeed + MAX_DELTA);
camera.eulerAngles = Vector3.Lerp(initialAngle, finalAngle, currentSpeed / N);
prevSpeed = currentSpeed;
}

Lossless movement in hinge joints - Unity

I've created a simple pendulum in Unity - GameObject with Rigidbody and Hinge Joint components. I've set both drag ang angular drag to 0. With starting position at 90 degrees I'd expect the pendulum to swing back and forth from 90 to -90 degrees. However, that's not the case - the amplitude decays very quickly, but for small angles the pendulum looks like it's never going to stop.
My question is: How should I configure hinge joints in order to achieve full control over physics and forces that resist motion? My goal is to have physics simulation as precise as it's possible, even at the cost of performance.
I've already tried to reduce time intervals for fixed step and increased solver iterations - none of these worked out.
Why do I need it? I'm planning to design a control system for multiple inverted pendulum on a cart. I have a mathematical model of my pendulum implemented in Matlab and I wanted to verify it using a simple model in Unity (because in that case I adjust all parameters, initial conditions etc. and the physics engine is calculating everything for me). If it turns out the physics engine that is backing Unity isn't reliable enough, what other software would you recommend me?
My understanding is that due to the way Unity's physics operates, there can be a loss of kinetic energy over time in this sort of pendulum motion if you only use a hinge joint. Basically, if you want an accurate pendulum simulation, you have to bypass the physics engine and implement it directly.
There is a very good post on the gamedev stackexchange originally posted by MLM about how to implement a more accurate pendulum simulation in Unity, which I have pasted below.
I thought this would be a relatively simple problem to solve but I spent a couple days trying to figure out how the heck to simulate pendulum movement. I didn't want to cheat and just change the x,y position based on sin(theta) and cos(theta) curves. Instead I wanted to deal with the two forces that are applied in real life, Gravity and Tension. The main piece I was missing was centripetal force.
The Pendulum (mathematics) wikipedia page has a great animation(below, on left) explaining the pendulum motion. You can see my result(on right) strikingly similar to that diagram
The "bob" is the swinging object and the "pivot" is the origin/root.
I also found this article and diagram(below) pretty helpful:
Theta equals the angle between the rope and the direction of gravity.
When the bob is on the left or right the tension equals:
The reason the tension force is greater as the bob approaches equilibrium point(middle) is because of centripetal force:
So the overrall tension formula looks like as the bob swings is:
There are two forces in the pendulum system:
Gravity
GravityForce = mass * gravity.magnitude
GravityDirection = gravity.normalized
Tension
TensionForce = (mass * gravity * Cos(theta)) + ((mass * velocityTangent^2)/ropeLength)
TensionDirection = ropeDirection = bob to pivot
Just apply gravity to your object like you would for a normal object and then apply the tension. When you apply the forces, just multiply the force by the direction and deltaTime.
Below is the Pendulum.cs script(also as a GitHub Gist). It works quite well but there is some rounding error drift if you leave it for a while (doesn't return to exactly same position).
The script works in 3D but of course a pendulum only swings in a 2D plane. It also works with gravity in any direction. So for example, if you invert the gravity the pendulum works upside down. Edit->Project Settings->Physics->Gravity
It is very important to have a consistent relatively small deltaTime when updating the pendulum so that you do not bounce around the curve. I am using the technique found in this article, FIX YOUR TIMESTEP! by Glenn Fiedler to accomplish this. Check the Update() function below to see how I implemented it.
Also as a GitHub Gist
using UnityEngine;
using System.Collections;
// Author: Eric Eastwood (ericeastwood.com)
//
// Description:
// Written for this gd.se question: http://gamedev.stackexchange.com/a/75748/16587
// Simulates/Emulates pendulum motion in code
// Works in any 3D direction and with any force/direciton of gravity
//
// Demonstration: https://i.imgur.com/vOQgFMe.gif
//
// Usage: https://i.imgur.com/BM52dbT.png
public class Pendulum : MonoBehaviour {
public GameObject Pivot;
public GameObject Bob;
public float mass = 1f;
float ropeLength = 2f;
Vector3 bobStartingPosition;
bool bobStartingPositionSet = false;
// You could define these in the `PendulumUpdate()` loop
// But we want them in the class scope so we can draw gizmos `OnDrawGizmos()`
private Vector3 gravityDirection;
private Vector3 tensionDirection;
private Vector3 tangentDirection;
private Vector3 pendulumSideDirection;
private float tensionForce = 0f;
private float gravityForce = 0f;
// Keep track of the current velocity
Vector3 currentVelocity = new Vector3();
// We use these to smooth between values in certain framerate situations in the `Update()` loop
Vector3 currentStatePosition;
Vector3 previousStatePosition;
// Use this for initialization
void Start () {
// Set the starting position for later use in the context menu reset methods
this.bobStartingPosition = this.Bob.transform.position;
this.bobStartingPositionSet = true;
this.PendulumInit();
}
float t = 0f;
float dt = 0.01f;
float currentTime = 0f;
float accumulator = 0f;
void Update()
{
/* */
// Fixed deltaTime rendering at any speed with smoothing
// Technique: http://gafferongames.com/game-physics/fix-your-timestep/
float frameTime = Time.time - currentTime;
this.currentTime = Time.time;
this.accumulator += frameTime;
while (this.accumulator >= this.dt)
{
this.previousStatePosition = this.currentStatePosition;
this.currentStatePosition = this.PendulumUpdate(this.currentStatePosition, this.dt);
//integrate(state, this.t, this.dt);
accumulator -= this.dt;
this.t += this.dt;
}
float alpha = this.accumulator/this.dt;
Vector3 newPosition = this.currentStatePosition*alpha + this.previousStatePosition*(1f-alpha);
this.Bob.transform.position = newPosition; //this.currentStatePosition;
/* */
//this.Bob.transform.position = this.PendulumUpdate(this.Bob.transform.position, Time.deltaTime);
}
// Use this to reset forces and go back to the starting position
[ContextMenu("Reset Pendulum Position")]
void ResetPendulumPosition()
{
if(this.bobStartingPositionSet)
this.MoveBob(this.bobStartingPosition);
else
this.PendulumInit();
}
// Use this to reset any built up forces
[ContextMenu("Reset Pendulum Forces")]
void ResetPendulumForces()
{
this.currentVelocity = Vector3.zero;
// Set the transition state
this.currentStatePosition = this.Bob.transform.position;
}
void PendulumInit()
{
// Get the initial rope length from how far away the bob is now
this.ropeLength = Vector3.Distance(Pivot.transform.position, Bob.transform.position);
this.ResetPendulumForces();
}
void MoveBob(Vector3 resetBobPosition)
{
// Put the bob back in the place we first saw it at in `Start()`
this.Bob.transform.position = resetBobPosition;
// Set the transition state
this.currentStatePosition = resetBobPosition;
}
Vector3 PendulumUpdate(Vector3 currentStatePosition, float deltaTime)
{
// Add gravity free fall
this.gravityForce = this.mass * Physics.gravity.magnitude;
this.gravityDirection = Physics.gravity.normalized;
this.currentVelocity += this.gravityDirection * this.gravityForce * deltaTime;
Vector3 pivot_p = this.Pivot.transform.position;
Vector3 bob_p = this.currentStatePosition;
Vector3 auxiliaryMovementDelta = this.currentVelocity * deltaTime;
float distanceAfterGravity = Vector3.Distance(pivot_p, bob_p + auxiliaryMovementDelta);
// If at the end of the rope
if(distanceAfterGravity > this.ropeLength || Mathf.Approximately(distanceAfterGravity, this.ropeLength))
{
this.tensionDirection = (pivot_p - bob_p).normalized;
this.pendulumSideDirection = (Quaternion.Euler(0f, 90f, 0f) * this.tensionDirection);
this.pendulumSideDirection.Scale(new Vector3(1f, 0f, 1f));
this.pendulumSideDirection.Normalize();
this.tangentDirection = (-1f * Vector3.Cross(this.tensionDirection, this.pendulumSideDirection)).normalized;
float inclinationAngle = Vector3.Angle(bob_p-pivot_p, this.gravityDirection);
this.tensionForce = this.mass * Physics.gravity.magnitude * Mathf.Cos(Mathf.Deg2Rad * inclinationAngle);
float centripetalForce = ((this.mass * Mathf.Pow(this.currentVelocity.magnitude, 2))/this.ropeLength);
this.tensionForce += centripetalForce;
this.currentVelocity += this.tensionDirection * this.tensionForce * deltaTime;
}
// Get the movement delta
Vector3 movementDelta = Vector3.zero;
movementDelta += this.currentVelocity * deltaTime;
//return currentStatePosition + movementDelta;
float distance = Vector3.Distance(pivot_p, currentStatePosition + movementDelta);
return this.GetPointOnLine(pivot_p, currentStatePosition + movementDelta, distance <= this.ropeLength ? distance : this.ropeLength);
}
Vector3 GetPointOnLine(Vector3 start, Vector3 end, float distanceFromStart)
{
return start + (distanceFromStart * Vector3.Normalize(end - start));
}
void OnDrawGizmos()
{
// purple
Gizmos.color = new Color(.5f, 0f, .5f);
Gizmos.DrawWireSphere(this.Pivot.transform.position, this.ropeLength);
Gizmos.DrawWireCube(this.bobStartingPosition, new Vector3(.5f, .5f, .5f));
// Blue: Auxilary
Gizmos.color = new Color(.3f, .3f, 1f); // blue
Vector3 auxVel = .3f * this.currentVelocity;
Gizmos.DrawRay(this.Bob.transform.position, auxVel);
Gizmos.DrawSphere(this.Bob.transform.position + auxVel, .2f);
// Yellow: Gravity
Gizmos.color = new Color(1f, 1f, .2f);
Vector3 gravity = .3f * this.gravityForce*this.gravityDirection;
Gizmos.DrawRay(this.Bob.transform.position, gravity);
Gizmos.DrawSphere(this.Bob.transform.position + gravity, .2f);
// Orange: Tension
Gizmos.color = new Color(1f, .5f, .2f); // Orange
Vector3 tension = .3f * this.tensionForce*this.tensionDirection;
Gizmos.DrawRay(this.Bob.transform.position, tension);
Gizmos.DrawSphere(this.Bob.transform.position + tension, .2f);
// Red: Resultant
Gizmos.color = new Color(1f, .3f, .3f); // red
Vector3 resultant = gravity + tension;
Gizmos.DrawRay(this.Bob.transform.position, resultant);
Gizmos.DrawSphere(this.Bob.transform.position + resultant, .2f);
/* * /
// Green: Pendulum side direction
Gizmos.color = new Color(.3f, 1f, .3f);
Gizmos.DrawRay(this.Bob.transform.position, 3f*this.pendulumSideDirection);
Gizmos.DrawSphere(this.Bob.transform.position + 3f*this.pendulumSideDirection, .2f);
/* */
/* * /
// Cyan: tangent direction
Gizmos.color = new Color(.2f, 1f, 1f); // cyan
Gizmos.DrawRay(this.Bob.transform.position, 3f*this.tangentDirection);
Gizmos.DrawSphere(this.Bob.transform.position + 3f*this.tangentDirection, .2f);
/* */
}
}
More glamour shots:
Set the maxAngularVelocity on your Rigidbody to Mathf.Infinity.
I know this topic is 9 months old, but I have been banging my head against a wall recently because of this issue. For some reason the Unity developers thought it was a good idea to limit the maximum rotational velocity of rigidbodies to 7 radians per second! That's just a little over one revolution per second, which is way too low for any application that requires physcal accuracy. And on top of that, the property isn't visible in the inspector, or the physics settings!
I hope this will help you (if you haven't figured it out on your own yet) and anyone else who might be fighting with this problem in the future, cheers!

Unity3d: how to apply a vortex like force to objects?

I would like to simulate a vortex like force to a "bunch" of objects in my scene.
How can I do in Unity ?
Thanks
If you are using the physics system, there's two parts to this. Applying the vortex force, and getting the nice swirling effect. To apply the vortex force you can just loop over the rigidbodies and apply the force. To make the swirl look like a proper vortex swirl, you need to start the objects off with a tangential velocity that you can figure out using the vector cross product.
public float VortexStrength = 1000f;
public float SwirlStrength = 5f;
void Start () {
foreach(GameObject g in RigidBodies){
//to get them nice and swirly, use the perpendicular to the direction to the vortex
Vector3 direction = Vortex.transform.position - g.transform.position;
var tangent = Vector3.Cross(direction, Vector3.up).normalized * SwirlStrength;
g.GetComponent<Rigidbody>().velocity = tangent;
}
}
void Update(){
//apply the vortex force
foreach(GameObject g in RigidBodies){
//force them toward the center
Vector3 direction = Vortex.transform.position - g.transform.position;
g.GetComponent<Rigidbody>().AddForce(direction.normalized * Time.deltaTime * VortexStrength);
}
}
Circular motion:
float angle =0;
float speed=(2*Mathf.PI)/5 //2*PI in degress is 360, so you get 5 seconds to complete a circle
float radius=5;
void Update()
{
angle += speed*Time.deltaTime; //if you want to switch direction, use -= instead of +=
x = Mathf.Cos(angle)*radius;
y = Mathf.Sin(angle)*radius;
}
where the center of your circle is the center of your vortex.
Of course:
If you want multiple objects with diferent distance from vortex's
center you have to play with your radius variable (i would add a
Random.Range(minDistance, maxDistance))
If you want diferent speeds you can randomize/change the speed.
Randomize/change your x/y if you don't want a perfect circle
Hope i was clear enought.

Vertex position relative to normal

In a surface shader, given the world's up axis (and the others too), a world space position and a normal in world space, how can we rotate the worldspace position into the space of the normal?
That is, given a up vector and a non-orthogonal target-up vector, how can we transform the position by rotating its up vector?
I need this so I can get the vertex position only affected by the object's rotation matrix, which I don't have access to.
Here's a graphical visualization of what I want to do:
Up is the world up vector
Target is the world space normal
Pos is arbitrary
The diagram is bidimensional, but I need to solve this for a 3D space.
Looks like you're trying to rotate pos by the same rotation that would transform up to new_up.
Using the rotation matrix found here, we can rotate pos using the following code. This will work either in the surface function or a supplementary vertex function, depending on your application:
// Our 3 vectors
float3 pos;
float3 new_up;
float3 up = float3(0,1,0);
// Build the rotation matrix using notation from the link above
float3 v = cross(up, new_up);
float s = length(v); // Sine of the angle
float c = dot(up, new_up); // Cosine of the angle
float3x3 VX = float3x3(
0, -1 * v.z, v.y,
v.z, 0, -1 * v.x,
-1 * v.y, v.x, 0
); // This is the skew-symmetric cross-product matrix of v
float3x3 I = float3x3(
1, 0, 0,
0, 1, 0,
0, 0, 1
); // The identity matrix
float3x3 R = I + VX + mul(VX, VX) * (1 - c)/pow(s,2) // The rotation matrix! YAY!
// Finally we rotate
float3 new_pos = mul(R, pos);
This is assuming that new_up is normalized.
If the "target up normal" is a constant, the calculation of R could (and should) only happen once per frame. I'd recommend doing it on the CPU side and passing it into the shader as a variable. Calculating it for every vertex/fragment is costly, consider what it is you actually need.
If your pos is a vector-4, just do the above with the first three elements, the fourth element can remain unchanged (it doesn't really mean anything in this context anyway).
I'm away from a machine where I can run shader code, so if I made any syntactical mistakes in the above, please forgive me.
Not tested, but should be able to input a starting point and an axis. Then all you do is change procession which is a normalized (0-1) float along the circumference and your point will update accordingly.
using UnityEngine;
using System.Collections;
public class Follower : MonoBehaviour {
Vector3 point;
Vector3 origin = Vector3.zero;
Vector3 axis = Vector3.forward;
float distance;
Vector3 direction;
float procession = 0f; // < normalized
void Update() {
Vector3 offset = point - origin;
distance = offset.magnitude;
direction = offset.normalized;
float circumference = 2 * Mathf.PI * distance;
angle = (procession % 1f) * circumference;
direction *= Quaternion.AngleAxis(Mathf.Rad2Deg * angle, axis);
Ray ray = new Ray(origin, direction);
point = ray.GetPoint(distance);
}
}