I am working in unity/C# and trying to make a function that keeps an character in certain x range (-3 to 3). Below isthe code I got to work. Is there a way to simplify it?
//function creation to limit movement in the x axis
float rangeBoundX(int upperBound, int lowerBound, Vector3 i, float horMoveSpe = 0)
{
//will change velocity to keep the x value in the desired range. - velocity to mvoe away from the upper bound and positive velocity goes away from the lowerBound.
if (i.x > upperBound)
{
horMoveSpe = -1;
}
else if (i.x < lowerBound)
{
horMoveSpe = 1;
}
return horMoveSpe;
}
'private void FixedUpdate()'
{
Vector3 enemyforwardMove = transform.forward * enemySpeed * Time.fixedDeltaTime;
Vector3 horizontalMove = transform.position;
magn = rangeBoundX(3, -3, horizontalMove, magn);
horizontalMove = transform.right * magn * freq;
enemyRB.MovePosition(enemyRB.position + enemyforwardMove + horizontalMove);
}
Yes you can use Math.Clamp to bound any value Like this.
void update(){
float xPos = Mathf.Clamp(transform.position.x, -3, 3);
transform.position = new Vector3(xPos, transform.position.y, transform.position.z);
}
using this code you can bound the position of object horizontaly.
Yes, there is a way to simplify it. You can use Mathf.Clamp method https://docs.unity3d.com/ScriptReference/Mathf.Clamp.html
example usage: float xPos = Mathf.Clamp(xValue, xMin, xMax);
Problem; my player acts like im always holding the 'w' key
So I have tried using first person all in one as well as easy fps player controllers. I have double checked the input system using the input system manager and visualizers, have unplugged every usb device aside from my keyboard and mouse and found no inputs out of the ordinary being detected. Even with no usb devices plugged in the player walks.
So, multiple player prefabs in multiple project files, both urp and 3d, will act like a forward walk input is detected even if i have unplugged every usb device. Im at a loss
Make sure that you have not set the velocity as always increasing. Make it increase only when the “w” key is pressed. Here is the code that I use for my player movement -
//Input
float x, y;
bool jumping, sprinting, crouching;
//Movement
public float moveSpeed = 4500;
public float maxSpeed = 20;
private void Update()
{
MyInput();
}
private void MyInput()
{
x = Input.GetAxisRaw("Horizontal");
y = Input.GetAxisRaw("Vertical");
}
private void Movement()
{
//Extra gravity
rb.AddForce(Vector3.down * Time.deltaTime * 10);
Vector2 mag = FindVelRelativeToLook();
float xMag = mag.x, yMag = mag.y;
CounterMovement(x, y, mag);
//Set max speed
float maxSpeed = this.maxSpeed;
//If speed is larger than maxspeed, cancel out the input so you don't go over max speed
if (x > 0 && xMag > maxSpeed) x = 0;
if (x < 0 && xMag < -maxSpeed) x = 0;
if (y > 0 && yMag > maxSpeed) y = 0;
if (y < 0 && yMag < -maxSpeed) y = 0;
//Some multipliers
float multiplier = 1f, multiplierV = 1f;
// Movement in air
if (!grounded)
{
multiplier = 0.3f;
multiplierV = 0.3f;
}
//Apply forces to move player
rb.AddForce(orientation.transform.forward * y * moveSpeed * Time.deltaTime * multiplier * multiplierV);
rb.AddForce(orientation.transform.right * x * moveSpeed * Time.deltaTime * multiplier);
}
Hope so this helps.
Thanks.
I need to shoot a ball from any height and make it bounce on a target position defined by the user. The angle of launch is also given. I've tried a couple of solutions so far:
Vector3 calcBallisticVelocityVector(Vector3 source, Vector3 target, float angle) {
Vector3 direction = target - source;
float h = direction.y;
direction.y = 0;
float distance = direction.magnitude;
float a = angle * Mathf.Deg2Rad;
direction.y = distance * Mathf.Tan(a);
distance += h/Mathf.Tan(a);
// calculate velocity
float velocity = Mathf.Sqrt(distance * Physics.gravity.magnitude / Mathf.Sin(2*a));
return velocity * direction.normalized;
}
Vector3 calcBallisticVelocityVector2(Vector3 source, Vector3 target, float angle) {
float distance = (target.Planar() - source.Planar()).magnitude;
float a = target.y - source.y - distance;
float halfGravity = -Physics.gravity.magnitude * 0.5f;
float distanceSquared = distance * distance;
float theta = Mathf.Deg2Rad * angle;
float cosSquared = Mathf.Cos(theta) * Mathf.Cos(theta);
float b = distanceSquared / cosSquared;
float speed = Mathf.Sqrt((halfGravity * b) / a);
Vector3 velocity = (target.Planar() - source.Planar()).normalized * Mathf.Cos(theta);
velocity.y = Mathf.Sin(theta);
return velocity * speed;
}
The results I'm getting is that even the ball does go into the direction is expected, it falls earlier than it should be so the speed calculated by these methods seems to be lower than what is actually required to hit the target position.
Rigidbody's mass is set to 1, Gravity is (0, -98, 0), rigid body's drag and angular drag is set to 0. What other variables could be affecting this behavior?
EDIT: One thing I forgot to mention is that I'm setting the resulting vector as rigid body's velocity, so I'm not using via the apply force method.
I adapted code gotten from here: https://answers.unity.com/questions/1131176/projectile-motion.html and now I'm getting the results I was expecting. I can always hit the target position at whatever angle I input.
private Vector3 calcBallisticVelocityVector(Vector3 initialPos, Vector3 finalPos, float angle)
{
var toPos = initialPos - finalPos;
var h = toPos.y;
toPos.y = 0;
var r = toPos.magnitude;
var g = -Physics.gravity.y;
var a = Mathf.Deg2Rad * angle;
var vI = Mathf.Sqrt (((Mathf.Pow (r, 2f) * g)) / (r * Mathf.Sin (2f * a) + 2f * h * Mathf.Pow (Mathf.Cos (a), 2f)));
Vector3 velocity = (finalPos.Planar() - initialPos.Planar()).normalized * Mathf.Cos(a);
velocity.y = Mathf.Sin(a);
return velocity * vI;
}
I want to lerp between two rotations with different velocities on three different axis (yaw/pitch/roll) in unity3d, and tried to achieve that with Quaternion.LookRotation().
Quaternion.LookRotation() takes a direction Vector as first parameter, so i thought that i could lerp the direction first and then look at it with a lerped up-vector.
It should be no problem with Vector3.lerp(), but in this case i need to lerp the direction with different velocities on two axis (X and Y) relative to the initial direction.
So for example i have a camera facing a target, then the target moves up and right a bit, and now i want the camera to tilt slowly to the right too, but a bit faster up to the targets position (keeping its own position).
How to lerp the direction vector with different speeds on both axis to use it in Quaternion.LookRotation()?
EDIT:
Changed the title from "Lerp between Vector3 with different velocities on X/Y" to "Quaternion lerp with different velocities for yaw/pitch/roll" and modified the question to match the topic.
Thanks to minorlogic and the CjLib, i tried the following:
public Quaternion QuaternionLerpOn3Axis(
Quaternion rot1,
Quaternion rot2,
Vector3 lerpSpeed
) {
if (rot1 != rot2) {
float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
// Lerp up direction
Vector3 vecUp = Vector3.Slerp(
rot1 * Vector3.up,
rot2 * Vector3.up,
lerpSpeedRoll
);
// Get new rotation with lerped yaw/pitch
Quaternion rotation = QuaternionUtil.Sterp(
rot1,
rot2,
rot1 * Vector3.right,
lerpSpeedYaw,
lerpSpeedPitch,
QuaternionUtil.SterpMode.Slerp
);
// Look at new direction and return rotation
return Quaternion.LookRotation(
rotation * rot1 * Vector3.forward,
vecUp
);
} else {
return rot1;
}
}
To try this without downloading CjLib, here is the whole code including the relevant parts for decoding the swing/twist:
public Quaternion QuaternionLerpOn3Axis(
Quaternion rot1,
Quaternion rot2,
Vector3 lerpSpeed
) {
if (rot1 != rot2) {
float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
// Lerp up direction
Vector3 vecUp = Vector3.Slerp(
rot1 * Vector3.up,
rot2 * Vector3.up,
lerpSpeedRoll
);
// Get difference between two rotations
Quaternion q = rot2 * Quaternion.Inverse(rot1);
// Decompose quaternion into two axis
Quaternion rotYaw;
Quaternion rotPitch;
DecomposeSwingTwist(
q,
rot1 * Vector3.right,
out rotYaw,
out rotPitch
);
// Lerp yaw & pitch
rotYaw = Quaternion.Slerp(Quaternion.identity, rotYaw, lerpSpeedYaw);
rotPitch = Quaternion.Slerp(Quaternion.identity, rotPitch, lerpSpeedPitch);
// Look at new direction and return rotation
return Quaternion.LookRotation(
rotPitch * rotYaw * rot1 * Vector3.forward,
vecUp
);
} else {
return rot1;
}
}
public static void DecomposeSwingTwist(
Quaternion q,
Vector3 twistAxis,
out Quaternion swing,
out Quaternion twist
) {
Vector3 r = new Vector3(q.x, q.y, q.z); // (rotation axis) * cos(angle / 2)
float Epsilon = 1.0e-16f;
// Singularity: rotation by 180 degree
if (r.sqrMagnitude < Epsilon) {
Vector3 rotatedTwistAxis = q * twistAxis;
Vector3 swingAxis = Vector3.Cross(twistAxis, rotatedTwistAxis);
if (swingAxis.sqrMagnitude > Epsilon) {
float swingAngle = Vector3.Angle(twistAxis, rotatedTwistAxis);
swing = Quaternion.AngleAxis(swingAngle, swingAxis);
} else {
// More singularity: rotation axis parallel to twist axis
swing = Quaternion.identity; // no swing
}
// Always twist 180 degree on singularity
twist = Quaternion.AngleAxis(180.0f, twistAxis);
return;
}
// Formula & proof:
// http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
Vector3 p = Vector3.Project(r, twistAxis);
twist = new Quaternion(p.x, p.y, p.z, q.w);
twist = Normalize(twist);
swing = q * Quaternion.Inverse(twist);
}
public static Quaternion Normalize(Quaternion q) {
float magInv = 1.0f / Magnitude(q);
return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
}
public static float Magnitude(Quaternion q) {
return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}
By now this is the only way i could achieve a quaternion (s)lerp with different velocities on three different axis with a reasonably acceptable result.
But in my opinion it is not a real mathematical solution, it does not work really well if the lerp values are below ~1.5f (especially the Z/Roll-axis), and it has much overhead.
Any ideas how to solve this puzzle with less/better code?
...another approach:
Now i tried to extend the concept of decomposing the swing/twist to decomposing yaw/pitch/roll.
This works fine (?) if the target does not flip over 180°, and it still needs some input/feedback from someone who really knows how to deal with quaternion rotations.
public Quaternion QuaternionLerpYawPitchRoll(
Quaternion rot1,
Quaternion rot2,
Vector3 lerpSpeed
) {
if (rot1 != rot2) {
float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
// Decompose quaternion into yaw/pitch/roll
Quaternion rotYaw;
Quaternion rotPitch;
Quaternion rotRoll;
DecomposeYawPitchRoll(rot1, rot2, out rotYaw, out rotPitch, out rotRoll);
// Lerp swing & twist
rotYaw = Quaternion.Slerp(Quaternion.identity, rotYaw, lerpSpeedYaw);
rotPitch = Quaternion.Slerp(Quaternion.identity, rotPitch, lerpSpeedPitch);
rotRoll = Quaternion.Slerp(Quaternion.identity, rotRoll, lerpSpeedRoll);
// Combine yaw/pitch/roll with current rotation
return Quaternion.LookRotation(
rotPitch * rotYaw * rot1 * Vector3.forward,
rotRoll * rot1 * Vector3.up
);
} else {
return rot1;
}
}
public static void DecomposeYawPitchRoll(
Quaternion rot1,
Quaternion rot2,
out Quaternion yaw,
out Quaternion pitch,
out Quaternion roll
) {
Vector3 pitchAxis = rot1 * Vector3.right;
Vector3 rollAxis = rot1 * Vector3.forward;
Vector3 yawAxis = rot1 * Vector3.up;
// Get difference between two rotations
Quaternion diffQ = rot2 * Quaternion.Inverse(rot1);
Vector3 r = new Vector3(diffQ.x, diffQ.y, diffQ.z); // (rotation axis) * cos(angle / 2)
float Epsilon = 1.0e-16f;
// Singularity: rotation by 180 degree
if (r.sqrMagnitude < Epsilon) {
Vector3 rotatedPitchAxis = diffQ * pitchAxis;
Vector3 rotatedYawAxis = Vector3.Cross(pitchAxis, rotatedPitchAxis);
Vector3 rotatedRollAxis = diffQ * rollAxis;
if (rotatedYawAxis.sqrMagnitude > Epsilon) {
float yawAngle = Vector3.Angle(pitchAxis, rotatedPitchAxis);
yaw = Quaternion.AngleAxis(yawAngle, rotatedYawAxis);
} else {
// More singularity: yaw axis parallel to pitch axis
yaw = Quaternion.identity; // No yaw
}
if (rotatedRollAxis.sqrMagnitude > Epsilon) {
float rollAngle = Vector3.Angle(yawAxis, rotatedYawAxis);
roll = Quaternion.AngleAxis(rollAngle, rotatedRollAxis);
} else {
// More singularity: roll axis parallel to yaw axis
roll = Quaternion.identity; // No roll
}
// Always twist 180 degree on singularity
pitch = Quaternion.AngleAxis(180.0f, pitchAxis);
} else {
// Formula & proof:
// http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
pitch = GetProjectedRotation(diffQ, pitchAxis);
roll = GetProjectedRotation(diffQ, rollAxis);
yaw = diffQ * Quaternion.Inverse(pitch);
}
}
public static Quaternion GetProjectedRotation(Quaternion rotation, Vector3 direction) {
Vector3 r = new Vector3(rotation.x, rotation.y, rotation.z);
Vector3 proj = Vector3.Project(r, direction);
rotation = new Quaternion(proj.x, proj.y, proj.z, rotation.w);
return Normalize(rotation);
}
public static Quaternion Normalize(Quaternion q) {
float magInv = 1.0f / Magnitude(q);
return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
}
public static float Magnitude(Quaternion q) {
return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}
Author of CjLib here.
It sounds like you actually don't need swing-twist decomposition.
I'd say just get the decomposed yaw/pitch/row for the current quaternion and desired quaternion. And then update the yaw/pitch/row values depending on how fast you want them to individually track the target value, and generate an updated quaternion from that set of yaw/pitch/row values.
Lerping with a maximum speed cap (which I refer to as "seeking") might be fine, but it would not look smooth. I recommend using critically-damped numeric springing. And here's a shameless placement of a 3-part series I wrote on this topic.
So I'm trying to create a realistic trampoline jump instead of the player falling through the trampoline and then slingshotting back up, whilst instead allowing the player to instantly shoot upon contact with the trampoline and come down a relative gravity.
Where am I going wrong and what can I do to fix it?
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(CharacterController))]
public class small_bounce_script: MonoBehaviour {
public float speed = 6.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
private Vector3 bounce = Vector3.zero;
void Update() {
CharacterController controller = GetComponent<CharacterController>();
if (controller.isGrounded) {
if (bounce.sqrMagnitude > 0) {
moveDirection = bounce;
bounce = Vector3.zero;
} else {
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
}
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
void OnTriggerEnter(Collider other) {
Debug.Log ("Controller collider hit");
Rigidbody body = other.attachedRigidbody;
// Only bounce on static objects...
if ((body == null || body.isKinematic) && other.gameObject.controller.velocity.y < -1f) {
float kr = 0.5f;
Vector3 v = other.gameObject.controller.velocity;
Vector3 n = other.normal;
Vector3 vn = Vector3.Dot(v,n) * n;
Vector3 vt = v - vn;
bounce = vt -(vn*kr);
}
}
}
A trampoline reacts like a spring device. Let's assume gravity is in Y direction and the trampoline surface is positionied in the X,Z plane.
Then your Y coordinate y is proportional to a sine function during OnTriggerStay. Velocity v in Y direction as 1st derivative of y is then a cosine function, while X and Z velocity remain constant.
y (t) = yMax * sin (f * t)
v (t) = yMax * f * cos (f * t)
Considering conservation of energy, we have:
E = 0.5 * m * vMax² = 0.5 * k * yMax²
=> yMax = ± SQRT (k / m) * vMax
vMax := speed in Y direction when hitting the trampoline. ± because for landing and starting
yMax := maximum amplitude when v == 0, i.e. hwo deep should the player sink before returning
k := spring constant defining trampoline behaviour i.e. how strong it is
m := player's mass
f := SQRT (k / m)
So all you need to do is playing around with the spring constant and have something like this in your Update method:
Vector3 velocity = rigidbody.velocity;
float elapsedTime = Time.time - timestampOnEnter;
velocity.y = YMax * FConst * Mathf.cos (FConst * elapsedTime);
rigidbody.velocity = velocity;
Member var timestampOnEnter is taken in OnTriggerEnter, FConst is the constant we called f in the maths part.