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.
Related
I want to display a 2D arrow on the screen that always moves to point to a 3D object in the AR scene.
The issue is how to measure the angle that should the arrow rotate to point the desired 3D object.
Thanks in advance.
One strategy is to project the position of your object into screen space.
Then calculate the vector between the position of your arrow and that projected position. You can use this vector to calculate an angle of rotation from, for example, a vertical direction, using the Dot Product, followed by an acos.
Finally, you'd need to do a little cross-product calculation to decide whether the above rotation is clockwise or anticlockwise.
Here is some sample code:
public GameObject Target;
RectTransform rt;
void Start()
{
rt = GetComponent<RectTransform>();
}
void Update()
{
// Get the position of the object in screen space
Vector3 objScreenPos = Camera.main.WorldToScreenPoint(Target.transform.position);
// Get the directional vector between your arrow and the object
Vector3 dir = (objScreenPos - rt.position).normalized;
// Calculate the angle
// We assume the default arrow position at 0° is "up"
float angle = Mathf.Rad2Deg * Mathf.Acos(Vector3.Dot(dir, Vector3.up));
// Use the cross product to determine if the angle is clockwise
// or anticlockwise
Vector3 cross = Vector3.Cross(dir, Vector3.up);
angle = -Mathf.Sign(cross.z) * angle;
// Update the rotation of your arrow
rt.localEulerAngles = new Vector3(rt.localEulerAngles.x, rt.localEulerAngles.y, angle);
}
For the above code, I suppose that:
You are only using one main camera, you may need to change this
Your arrow is on the Canvas, by default pointing upwards (when its rotation is (0, 0, 0))
You are using a Canvas in Render Mode: Screen Space - Overlay. The above code would be different if the Canvas were in World Space.
As a high-level overview:
Find direction from UI/view-plane centre to 3D object
Project direction onto UI/view-plane (using forward as normal vector), and normalize
Point 2D arrow toward projected direction
Thank You all Guys, I got an Answer for two Situations :
First One: When the two objects are in 3D
public GameObject flesh;
public GameObject target;
// Start is called before the first frame update
void Start()
{
flesh.transform.position = Camera.main.ScreenToWorldPoint(new Vector3( Screen.width/2, Screen.height/2,1));
}
// Update is called once per frame
void Update()
{
var dir = Camera.main.WorldToScreenPoint(target.transform.position) -
Camera.main.WorldToScreenPoint(flesh.transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
flesh.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
Second: When the Flesh is an Image that Has RectTransform,
, this solution Inspirational from #kevernicus
public GameObject Target;
RectTransform rt;
void Start()
{
rt = GetComponent<RectTransform>();
}
void Update()
{
// Get the position of the object in screen space
Vector3 objScreenPos = Camera.main.WorldToScreenPoint(Target.transform.position);
// Get the directional vector between your arrow and the object
Vector3 dir = (objScreenPos - rt.position).normalized;
float angle = Mathf.Rad2Deg * Mathf.Atan2(dir.y, dir.x);
rt.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
Use Unity's built in Transform.LookAt function
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!
I am trying to archive without success a z-axis rotation movement around a moving object keeping always "looking at front". So it should looks like this:
The closest I got was with:
transform.RotateAround(targetPosition, Vector3.forward, moveSpeed);
But it does not keeps looking "at front".
Could someone give me a hand with this?
Thank you in advance.
Best regards.
If your object ("Lightning Bolt") has no world rotation, i.e. aligned with the world axis as your example image seems to suggest, then the easiest is to simply set the world rotation to the Quaternion Identity:
transform.rotation = Quaternion.identity;
Note that the image wont rotate if its parent object rotates. It will essentially "Billboard" your lightning object. If you want to your lightning bolt to be aligned with a parent object, then try something like:
transform.rotation = transform.parent.rotation;
Fitst store the current rotation, then rotate around point, lastly apply the previous rotation.
var rot = transform.rotation;
transform.RotateAround(targetPosition, Vector3.forward, moveSpeed);
transform.rotation = rot;
A simple solution would just manipulate actual coordinates and ignore rotation ^^ If an object moves and you want it to keep rotating around it, just make it a child object.
This is a 3d solution where we rotate around Y:
void Start() { angle = 0.0f }
void Update() {
angle += speed * Time.deltaTime; //Your starting angle that will be modified every frame
CheckAngle(ref angle);
float x = Mathf.Cos(angle * Mathf.Deg2Rad) * Radius;
float z = Mathf.Sin(angle * Mathf.Deg2Rad) * Radius;
}
static void CheckAngle(ref float Angle) //It should probably return a value by it's name here tho not ref, not sure which is the correct way of doing this
{
if (Angle > 360) //You don't want your angle to go past the boundaries of an INT
Angle = 0;
}
I am fairly new to Unity so please bear with me I have tried looking for the answer everywhere, but have had no luck.
Basically I am using onMouseDrag to move a sprite around the background for a classroom (1366x768) that has a table. However, I want to limit where the sprite can go so that it does not end up off screen or off of the table on my background.
My sprite has the 2d box collider and rigidbody components attached (gravity is set to zero and it is at a fixed angle). I thought that by placing four 2d box colliders around the area I want to keep the sprite in it would be enough to contain it but the sprite simply goes straight through them.
I also read up about using Mathf.Clamp to restrict the area but I do not really understand how to use it from the examples I have seen.
Below is my code for moving the sprite:
using UnityEngine;
using System.Collections;
public class MovementScript : MonoBehaviour {
float x;
float y;
void Update() {
x = Input.mousePosition.x;
y = Input.mousePosition.y;
}
public void OnMouseDrag() {
transform.position = Camera.main.ScreenToWorldPoint (new Vector3 (x, y, 1.0f));
}
}
Any help would be greatly appreciated!
Moving an object using its transform is not the same as moving it.
When you use the transform, the object doesn't "move", it teleports every Update by a small amount. Unfortunately, the Rigidbody can't detect this change in position as movement and thus does not react with any colliders. Regardless, that's probably the more complicated way to do this. Using Clamp is definitely easier.
Clamp is a pretty straightforward function. it takes three args: a value, a min, and a max. If the value is less than min or greater than max, it returns that boundary. Otherwise, it returns the value itself.
For instance,
Mathf.Clamp(5, 1, 3); //returns 3
Mathf.Clamp(2, 1, 3); //returns 2
Mathf.Clamp(-2, 1, 3); //returns 1
This is simply a convenience function for something like this:
if(val > max) {
return max;
} else if(val < min) {
return min;
} else {
return val;
}
So using Clamp, you can restrict the values of your x and y coordinates:
//to avoid confusion, I'm referring to your x and y variables
//as inputX and inputY. they represent the mouse position.
public void OnMouseDrag() {
Vector3 pos = Vector3.zero;
pos.x = Mathf.Clamp(inputX, minX, maxX);
pos.y = Mathf.Clamp(inputY, minY, maxY);
pos.z = 1.0;
transform.position = Camera.main.ScreenToWorldPoint (pos);
}
Mathf.Clamp will keep the X and Y coordinates of your Transform within the range (minX, maxX) and (minY, maxY) respectively. You can create these variables as inspector variables so you can change them on the fly.
I have a sphere gameobject with 5 cubes placed on different points on the surface of the sphere. When a key is pressed, i would like the sphere to spin for a few seconds and then slowly stops on the first cube point on the cube and always keeping the same direction in rotation. The issue i am facing now is that, Quaternion.Slerp always takes the shortest path to the next cube which means sometimes the rotation direction is changed. Any ideas?
Thanks
You can easily handle the rotation as a Vector3 of Euler angles.
This way you can Linearly Interpolate the angles to the correct value. And you can use coterminal angles so that you're always interpolating to a higher value ( so no backwards rotations would occur ). After every rotational step you might want to normalize the angles to the 0, 360 range with this approach though.
Example Code :
using UnityEngine;
using System.Collections;
public class Rotation : MonoBehaviour {
public Transform firstPosition;
public Transform secondPosition;
public float rotationDuration = 3;
void Start () {
transform.rotation = firstPosition.rotation;
StartCoroutine(Rotate());
}
IEnumerator Rotate() {
var deltaTime = 0.0f;
var distance = firstPosition.rotation.eulerAngles - secondPosition.rotation.eulerAngles;
while(deltaTime < rotationDuration) {
var rotation = transform.rotation.eulerAngles;
rotation = firstPosition.rotation.eulerAngles + deltaTime/rotationDuration*distance;
transform.rotation = Quaternion.Euler(rotation);
deltaTime += Time.deltaTime;
yield return null;
}
transform.rotation = secondPosition.rotation;
}
}