I have made a projectile shooting system. I want to shoot an object from position A to B following an indicated path. Everything is working fine except for one thing. The velocity applied on the object is calculated based on the distance between A and B, the value of time is 1 to travel this distance. Meaning that the farther I hit, the quicker it goes. I want to have control of the force applied. Meaning that it should go with my set speed whether I hit near or far. Tried normalizing the velocity and multiplied it by my custom force value, but then it moves away from its trajectory.
(See this link below, no matter how close or far we hit the object. It goes with the same speed while following the trajectory indicated. I want to develop this functionality.
https://play.google.com/store/apps/details?id=com.ghakilo.trickytrack)
Vector3 calculateVelocity(Vector3 target, Vector3 origin, float time)
{
Vector3 distance = target - origin;
Vector3 distanceXZ = distance;
distanceXZ.y = 0f;
float Sy = distance.y;
float Sxz = distanceXZ.magnitude;
float Vxz = Sxz / time;
float Vy = 0f;
Vy = Sy / time + 0.5f * Mathf.Abs(Physics.gravity.y) * time;
Vector3 result = distanceXZ.normalized;
result = result * Vxz;
result.y = Vy;
return result;
}
Physics time!
Velocity is a vector whose magnitude is speed.
If you want to fix the speed at which your projectile starts, that only leaves the direction of the velocity in your hands, so you need to calculate the direction in which you want to yeet your projectile.
If you're like me, you shoot straight at the target, so you'd set the direction vector to go from the origin to the target. Which is simply target - origin, or what you calculate as distance. I'm going to call this direction because that's what it really is being used for.
Now to use this as the direction vector for your velocity, convert distance to a unit vector (not sure how you do this in the unity framework, but direction.Normalize()?)
Then multiply this by the speed to get your velocity vector!
Vector3 calculateVelocity(Vector3 target, Vector3 origin, float speed)
{
Vector3 direction = target - origin;
direction.Normalize();
Vector3 result = direction * speed;
return result;
}
First add rigidbody to your bullet and set the gravity scale to .5
and add this code to your bullet prefab
Vector3 calculateVelocity(Transform target, Transform origin, float time)
{
Vector3 direction = target.transform.position - origin.transform.position;
float distance = Vector3.Distance(origin.transform.position, target.transform.position);
result = direction.normalized * distance * speed * Time.deltaTime;
return result;
}
Related
How do I correctly calculate the rotation angle so that the object dodges the player? It means that whichever side the player comes from, the object must turn away from the player depending on its direction.
I want the effect like in the video but without Joint, only rotation angle: https://www.youtube.com/watch?v=GJhiR3SOyXs
I need it for spherical map. And right now it looks like this.
calculate the direction Vector from player to the object.
calculate the axis of rotation by calculating the cross product of the direction Vector and the world up. Doing so creates a vector that is orthogonal to both, which is what we need.
calculate the angle by how much to rotate. This is done by clamping the distance (magnitude of direction) from 0 to the maximum effect distance and dividing it by the effect distance. Doing so creates a value from 0 to 1. However we need a value from 1 to 0, so there is no effect when the player is far away from the object and the maximum when close. To do so you simply subtract the initial value from 1. By multiplying the result with the max angle we calculate an angle in the range of 0 to maxAngle.
Finally we calculate the object rotation by multiplying the initial rotation with the rotation around the axis.
[SerializeField] Transform player;
[SerializeField] float effectMaxDistance=1;
[SerializeField] float maxAngle=50;
Quaternion initialRotation;
void Start(){
initialRotation = transfrom.rotation;
}
void Update(){
Vector3 dir = player.position - transform.position;
Vector3 axis = Vector3.Cross(dir, Vector3.up);
float angle = (1-(Mathf.Clamp(dir.magnitude, 0 effectMaxDistance) / effectMaxDistance)) * maxAngle;
transform.rotation = initialRotation * Quaternion.AngleAxis(angle, axis);
}
Note that cross products and Quaternion multiplications are not commutative and need to be done in this exact order!
Bonus answer:
If you're on a sphere you need to use the normal of the ground as the up direction. Since you've probably initially rotated the tree the right way up, you could do the following:
[SerializeField] Transform player;
[SerializeField] float effectMaxDistance=1;
[SerializeField] float maxAngle=50;
Quaternion initialRotation;
Vector3 initialUp;
void Start(){
initialRotation = transfrom.rotation;
initialUp = transfrom.up;
}
void Update(){
Vector3 dir = player.position - transform.position;
Vector3 axis = Vector3.Cross(dir, initialUp);
float angle = (1-(Mathf.Clamp(dir.magnitude, 0 effectMaxDistance) / effectMaxDistance)) * maxAngle;
transform.rotation = initialRotation * Quaternion.AngleAxis(angle, axis);
}
this is my direction vector
new Vector3(target.transform.position.x - projectile.position.x, 0, target.transform.position.z - projectile.position.z).normalized
I tried multiplying it by Quaternion.AngleAxis(45, Vector3.up) but that simply doesn't work
All other orientations like Vector3.left, right, etc. don't help either
The only thing I could observe is the way that the angle changes when I move the target or projectile
You were close. Use cross product to get the axis you need, use that in AngleAxis, then finally apply that rotation to your starting direction:
Vector3 RotateTowardsUp(Vector3 start, float angle)
{
// if you know start will always be normalized, can skip this step
start.Normalize();
Vector3 axis = Vector3.Cross(start, Vector3.up);
// handle case where start is colinear with up
if (axis == Vector3.zero) axis = Vector3.right;
return Quaternion.AngleAxis(angle, axis) * start;
}
Hey and thanks for your help in advanced.
I've watched a few youtube videos on how to add Solar System and orbiting Gravity in Unity and ended up using the this for help for the solar system gravity part.
https://www.youtube.com/watch?v=Ouu3D_VHx9o&t=114s&ab_channel=Brackeys
But right after i decided to trying to make my planet orbit the sun i used this Wikipage for the math equation
https://en.wikipedia.org/wiki/Orbital_speed
But for some reason either my planets flies away of the sun start flying towards the planet. I've been looking around for 2 days and can't seem to make it work and tried diffrent type of possiblies.
Here is my code
public class Planets : MonoBehaviour
{
const float G = 100F;
public Rigidbody rb;
public float CurrentV;
private void FixedUpdate()
{
Planets[] attractors = FindObjectsOfType<Planets>();
foreach (Planets AllPlanets in attractors)
{
if (AllPlanets != this)
{
Orbiting(AllPlanets, CurrentV);
Attract(AllPlanets);
}
}
}
void Attract(Planets objToAttract)
{
Rigidbody RbTpAttract = objToAttract.rb;
Vector3 direction = rb.position - RbTpAttract.position;
float distance = direction.magnitude;
float ForceMagnitude = G * (rb.mass * RbTpAttract.mass) / Mathf.Pow(distance, 2);
Vector3 Force = direction.normalized * ForceMagnitude;
RbTpAttract.AddForce(Force);
}
void Orbiting(Planets objToAttract, float CV)
{
Rigidbody RbTpAttract = objToAttract.rb;
Vector3 direction = rb.position - RbTpAttract.position;
float distance = direction.magnitude;
float ForceMagnitude = Mathf.Sqrt((G * rb.mass) / (2 / distance - 1 / RbTpAttract.mass));
Vector3 Force = direction.normalized * ForceMagnitude;
RbTpAttract.velocity += Force;
}
}
The problem is that the formula for orbital speed is used to derive the speed of an object in orbit, but you're using it as a form of constant thrust applied to each body towards each other. That's a bit like calculating the speed of a moving car, and then applying the same speed back to it as an impulse!
The only force experienced by objects in orbit is the one you get from Newton's law G * m * m / r*r. In order to actually orbit though, the planets will need an initial velocity - this can be calculated from the orbital speed formula. Calculate it at the given distance, and apply it on Start() in a direction perpendicular to the orbital plane and the direction to the sun (or whatever you want to orbit), you can get this from dir = Vector3.Cross(sunDir, Vector3.up).normalized
Note that gravitational systems are not numerically stable in physics engines relying on euler integration (such as PhysX). You need things like Runge-Kutta integration for that, or the planets will eventually lose their orbit if you leave the simulation running for long enough.
I'm currently creating a game involving a hover-bike. when the bike collides with something , it's angles change naturally. I wish to create some sort of way for it to tend back to 0. Here's what I've tried:
if (hoverbike.rotation.x != 0 || hoverbike.rotation.z != 0)
{
hoverbike.AddTorque(x: Mathf.MoveTowardsAngle(hoverbike.rotation.x, 0, 0.01f), y: hoverbike.rotation.y, z: Mathf.MoveTowardsAngle(hoverbike.rotation.z, 0, 0.01f));
}
transform.Rotate(0.0f, -Input.GetAxis("Mouse X") * 0.5f, 0.0f);
It's hard to explain what it's doing because I don't understand what it's doing, it just seems to spin out.
Here's the rest of my code if interested: https://pastebin.com/kzMDQMVF, it's a mess but I'm still learning how to use Unity.
Oh and angle y shouldn't tend to 0 because that's the horizontal angle.
You can determine the quaternion that would rotate the hovercraft from its current up to world up using Quaternion.FromToRotation:
Rigidbody hoverRB; // hovercraft's rigidbody
Quaternion deltaQuat = Quaternion.FromToRotation(hoverRB.transform.up, Vector3.up);
Then use Quaternion.ToAngleAxis to convert that to an angle & axis:
Vector3 axis;
float angle
deltaQuat.ToAngleAxis(out angle, out axis);
Then, cancel out some of any existing rotational velocity so that you'll eventually reach the goal:
float dampenFactor = 0.8f; // this value requires tuning
hoverRB.AddTorque(-hoverRB.angularVelocity * dampenFactor, ForceMode.Acceleration);
And then apply some torque along the axis we found before, scaled by how much angle remains:
float adjustFactor = 0.5f; // this value requires tuning
hoverRB.AddTorque(axis.normalized * angle * adjustFactor, ForceMode.Acceleration);
Any conversion between radians Rigidbody uses and degrees of ToAngleAxis is redundant with the float constants, so don't worry too much about it.
Make sure this is all being done in FixedUpdate (or a function called/running in FixedUpdate time) due to how the torque's direction will likely need to change from one physics step to another. So, altogether:
Rigidbody hoverRB; // hovercraft's rigidbody
...
void FixedUpdate()
{
Quaternion deltaQuat = Quaternion.FromToRotation(hoverRB.transform.up, Vector3.up);
Vector3 axis;
float angle
deltaQuat.ToAngleAxis(out angle, out axis);
float dampenFactor = 0.8f; // this value requires tuning
hoverRB.AddTorque(-hoverRB.angularVelocity * dampenFactor, ForceMode.Acceleration);
float adjustFactor = 0.5f; // this value requires tuning
hoverRB.AddTorque(axis.normalized * angle * adjustFactor, ForceMode.Acceleration);
}
I have some problems with a rotating marble.
I've tried it with Matrix.CreateFromYawPitchRoll and Matrix.CreateRotation but there were some problems, I think it's due to the Gimbal lock effect.
So, I've tried using quaternions instead, but nothing changed.
When moving on only an axis it works fine, but when the rotation occurs on two different axes the marble still rotates on wrong axes.
Here's my code:
// declarations
Vector3 Position = Vector3.Zero;
Vector3 Rotation = Vector3.Zero;
Quaternion qRotation = Quaternion.Identity;
AbsoluteBoneTransforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo(AbsoluteBoneTransforms);
In the Update method:
Position += speed;
Rotation = speed * MathHelper.ToRadians(-1.5f);
Quaternion rot = Quaternion.CreateFromAxisAngle(Vector3.Right, Rotation.Z) *
Quaternion.CreateFromAxisAngle(Vector3.Backward, Rotation.X);
qRotation *= rot;
And in the Draw method:
effect.World = AbsoluteBoneTransforms[mesh.ParentBone.Index] *
Matrix.CreateFromQuaternion(qRotation) * Matrix.CreateTranslation(Position);
What's wrong? Is it wrong to use Quaternion.CreateFromAxisAngle on multiple axes?
EDIT
I've tried calculating directly the axis of rotation of my marble, instead of using combination of multiple axes:
angle += speed.Length() * angularVelocity;
qRotation = Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle);
qRotation.Normalize();
angle is a float that keeps track of the current movement.
This solution doesn't seem to create Gimbal lock, but marble rotations aren't correct, it seems that the rotating speed is not constant, but became faster and slower over time, I can't understand why.
If I "concatenate" the quaternions I get every frame using
qRotation *= Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle)
the Gimbal lock effect is still visible.
Here's how I've tackled that:
I'm assuming speed is a vector representing the direction the ball is rolling and whose magnitude represents the rate it is traveling in that direction.
Vector3 axis = Vector3.Cross(speed, Vector3.Up);
float angle = speed.Length();//factor by delta time if neccesary
Quaternion rotationThisFrame = Quaternion.CreateFromAxisAngle(axis, angle * (1/radiusOfBall));
then you can concatenate that to your qRotation. Also, you may need to normalize your quaternion after concatenation.
Update: The correct answer to this question/thread was reversing the order that quaternions concatenate in. With respect to XNA, matrices combine left to right, quaternions combine right to left.