Mathf.PingPong from 1 to 0 - c#

I have created a helper to PingPong from 1 to 0, but I'm having a hard time inverting this, so that the value goes from 1 to 0.
Below you can see the code, I'm currently using. I'm also not sure if this is even possible.
_lerpPulse = PingPong(Time.time * 0.5f, 0f, 1f);
And the helper
float PingPong(float aValue, float aMin, float aMax)
{
return Mathf.PingPong(aValue, aMax - aMin) + aMin;
}
Thanks in advance!

Mathf.PingPong creates a graph like this
So, to begin at a peak, you can add the distance to the peak along the x axis to the time parameter, which will be sqrt(2)/2 * the length, or 0.707 * length
float PingPong1To0(float aValue, float aMin, float aMax)
{
float offset 0.707f * (aMax - aMin); // sqrt(2)/2
return Mathf.PingPong(aValue +offset, aMax - aMin) + aMin;
}
Should hit the falling side of that triangle

Related

How to get the signed angle of a quaternion on an arbitrary axis

I'm trying to make an airplane controller, I am kind of aiming for something between arcade and realistic, so I want the plane to turn with a force proportional to the roll.
I haven't coded in any adjustments and I'm still prototyping the whole thing, but I encountered a problem with getting the signed rotation angle while using quaternions, I had a look at Determining if quarternion rotation is clockwise or counter clockwise here on SO but I am having trouble generalizing the solution to the (almost) arbitrary plane the rotation can be at.
What I made by now:
private void FixedUpdate()
{
float desiredYaw = _yaw * _rotationSpeed * Time.fixedDeltaTime;
float desiredPitch = -_pitch * _rotationSpeed * Time.fixedDeltaTime;
float rotationStepSize = _throttle * Time.fixedDeltaTime;
Quaternion toRotate = Quaternion.Euler(desiredPitch, 0, desiredYaw);
Quaternion straighRotation = Quaternion.LookRotation(_transform.forward, Vector3.up );
_rotation = _transform.rotation * toRotate;
float turningForce = Quaternion.Angle( _rotation, straighRotation );
_rigidbody.MoveRotation( _rotation );
_rigidbody.AddTorque( turningForce * _rotationForce * rotationStepSize * Vector3.up );
_rigidbody.AddRelativeForce( _speed * rotationStepSize * Vector3.forward );
}
EDIT: I realized I'm calculating the turning force using the roll rather then the yaw, that was intended just wrong wording, corrected now.
Since all you need is a factor that describes how downward the plane's right is, you can just use the y component of the plane's right for that. No need to bring in quaternions or even trigonometry. Explanation in comments:
private void FixedUpdate()
{
// ...
// Calculate how downward local right is in range [-1,1]
// The more downward, the more tilted right the plane is
// positive = tilted right
// negative = tilted left
float turnFactor = -_transform.right.y;
// Could do things to modify turnFactor to affect easing here.
// For instance, if turning rate should start slower then rapidly increase:
// turnFactor = Mathf.Sign(turnFactor) * turnFactor * turnFactor;
// Use factor and _rotationForce member to calculate torque, apply along
// global up.
// We expect to call this every fixed frame so we can just use the default
// ForceMode of ForceMode.Force which multiplies fixed delta time inside.
_rigidbody.AddTorque(_rotationForce * turnFactor * Vector3.up);
// ...
}

What's Happening When Lerping Using Time.deltaTime?

I've just recently been introduced to Time.deltaTime and lerping and have been kinda stuck for the past few days.
So when I plug in Time.deltaTime for the last float and then multiply by another float variable called speed, what exactly is happening? What is Time.deltaTime doing to speed each frame that gives the end result?
Thank you!!
public class CameraMovement : MonoBehaviour
{
public GameObject followTarget;
public float moveSpeed;
void Update()
{
if (followTarget != null)
{
transform.position = Vector3.Lerp(transform.position, followTarget.transform.position, moveSpeed * Time.deltaTime);
}
}
}
Vector3.Lerp expects a factor between 0 and 1 and interpolates the two positions so
0 would mean transform.position
1 would mean followTarget.transform.position
any other value between those the interpolation between them meaning something like
transform.position + (followTarget.transform.position - transform.position) * factor;
Time.deltaTime = time past since last frame rendered so most likely a value close to 0.017 (for 60FPS). You can calculate by yourself what value this gives you in average for moveSpeed * 0.017.
It looks like what you are actually looking for is rather Vector3.MoveTowards
void Update()
{
if (followTarget != null)
{
transform.position = Vector3.MoveTowards(transform.position, followTarget.transform.position, moveSpeed * Time.deltaTime);
}
}
Using Time.deltaTime * scalar in Lerp is perfectly fine, but it's not the "expected" way to use it.
LERP means "Linear intERPolation". With this mathematic tool, you are able to interpolate a value between two others using a linear function ( y = ax + b ) and a coefficient t between 0 and 1.
Supposing you are trying to get a position (C) between two others (A and B) (like your problem), you have the folowing drawing :
1°) If the initial and destination position does not change, having the parameter t vary between 0 and 1, the movement curve will be perfectly straight : the speed will be the same over time.
transform.position = Vector2.Lerp(InitialPosition, DestinationPosition, t ); // t from 0 to 1
2°) However, having t (almost) not vary over time (with Time.deltaTime), but chaning the position of A, the movement will decelerate over time, because you take a fixed percentage of the remaining distance each time. Since the distance decrease over time, you travel less distance over time too
transform.position = Vector2.Lerp(transform.position, DestinationPosition, 0.017 ); // 0.017, approx of Time.deltaTime at 60FPS

Lerp color based on distance between 2 objects

I would like to know how to make a smooth color lerp based on distance between 2 objects. The color should lerp from green to red to green to red... Cube far = color red, cube near = color green.
I already got everything working, but the only thinks that isn't working is the fact that the color doesn't lerp smooth. This is how it looks like at the moment.
https://i.gyazo.com/a85852e76d2418ab7d44c18e152647c0.mp4
I am using this script for the color change:
FindClosestCube ();
float lerpProgress = 0f;
GameObject cubeChildTop = null;
GameObject closestCube = FindClosestCube ();
cubeChildTop = closestCube.transform.Find("Top").gameObject;
if (cubeDiffX >= 0.8f || cubeDiffX <= -0.8f)
{
lerpProgress = 0.5f;
}
if (cubeDiffX <= 0.8f || cubeDiffX <= -0.8f)
{
lerpProgress = 1f;
}
if (cubeDiffX >= 1.6f || cubeDiffX <= -1.6f)
{
lerpProgress = 0f;
}
if(closestCube != GameObject.Find("Cube (1)2"))
{
cubeChildTop.GetComponent<Renderer>().material.color = Color.Lerp(redColor, greenColor, lerpProgress);
}
So... how do I make it lerping smooth from red to green?
This is really easy.
1.Find the max distance that you think both of those Objects can travel apart.
The first thing you need to do is determine the max distance value those two GameObjects will be apart from. You need to pass that value into the inValueMax parameter of the mapValue function fro #2.
You can determine that max value with this code:
public GameObject obj1;
public GameObject obj2;
void Update()
{
UnityEngine.Debug.Log(getSqrDistance(obj1.transform.position, obj2.transform.position));
}
public float getSqrDistance(Vector3 v1, Vector3 v2)
{
return (v1 - v2).sqrMagnitude;
}
Run it, manually move each Object/Cube then get the highest value both Objects can travel from each other with the Debug.Log message.
Looking that the video you posted I estimated that the distance value of 200 is fine for that but you still have to do your experiment with the script above if you want a perfect result.
2.Use the map to convert 0 and that MAX_DISTANCE distance range to 0f and 1f range
float mapValue(float mainValue, float inValueMin, float inValueMax, float outValueMin, float outValueMax)
{
return (mainValue - inValueMin) * (outValueMax - outValueMin) / (inValueMax - inValueMin) + outValueMin;
}
It scales values between some certain point to another.
For example, you need to use the Lerp function to do this and the Lerp function takes 0 to 1 values. The mapValue function can scale any number to range between 0 and 1 that the Lerp function need.
For me, I will scale 0 to 200 range values to 0f and 1f range with the mapValue function.
3.Finally, use Color.Lerp(near, far, lerp); to lerp between Colors. The lerp value is the result value from #2.
In Code:
Once you find #1, plug that value to the MAX_DISTANCE variable from the script below should work:
public GameObject obj1;
public GameObject obj2;
Color near = Color.green;
Color far = Color.red;
const float MAX_DISTANCE = 200;
void Update()
{
//Get distance between those two Objects
float distanceApart = getSqrDistance(obj1.transform.position, obj2.transform.position);
UnityEngine.Debug.Log(getSqrDistance(obj1.transform.position, obj2.transform.position));
//Convert 0 and 200 distance range to 0f and 1f range
float lerp = mapValue(distanceApart, 0, MAX_DISTANCE, 0f, 1f);
//Lerp Color between near and far color
Color lerpColor = Color.Lerp(near, far, lerp);
obj1.GetComponent<Renderer>().material.color = lerpColor;
}
public float getSqrDistance(Vector3 v1, Vector3 v2)
{
return (v1 - v2).sqrMagnitude;
}
float mapValue(float mainValue, float inValueMin, float inValueMax, float outValueMin, float outValueMax)
{
return (mainValue - inValueMin) * (outValueMax - outValueMin) / (inValueMax - inValueMin) + outValueMin;
}
Result:
It seems that you are confused with the concept of lerp. When you apply lerp, lerpProgress should be continous values in [0, 1] to make it change smoothly. In your caes, you are setting them to 0, 0.5, 1.0, and that's where the colour is not appearing smoothly.
Remember that lerp (linear interpolation) is summing up two values with each having different contribution ratio. So if you have Color A and Color B that you want to lerp, you are saying that I want 40% from A and 60% from B and mix them up.
How much contribution ratio each instance should have is entirely up to you, and it is where you are designing your game to be.
One way I could think of is in terms of distance. If Z is moving between X and Y, then there are two distance ratios of XZ and YZ. If Z is 40% (0.4f) toward X and 60% (0.6f) toward Z, you could take 40% of X's colour value and 60% of Y's colour value, for example.
The idea is not limited to only 2 objects, but can be extended to N objects.
Edit:
Here's an example on how you could get a ratio from only X values. In this example I am assuming two predefined values of minX and maxX.
The idea is to consider the leftX to be the starting point. If x == left.x, it returns 0, and if x == right.x, it returns 1.
float GetDistanceRatio()
{
float maxDistance = maxX - minX;
float distanceFromLeft = currX - minX;
float distanceFromLeftRatio = distanceFromLeft / maxDistance;
return distanceFromLeftRatio;
}

Unity3D C# Negative Lerp?

I am wanting to lerp the variable that controls my animations speed so it transitions fluidly in the blend tree.
void Idle()
{
_agent.SetDestination(transform.position);
//Something like the following line
_speed = Mathf.Lerp(0.0f, _speed, -0.01f);
_animationController._anim.SetFloat("Speed", _speed);
}
I'v read that you can't lerp negatives, so how do I do this?
I think you're using Lerp bad.
As written in Unity Documentation (http://docs.unity3d.com/ScriptReference/Mathf.Lerp.html), you should pass 3 floats, the minimum as first argument, the maximum as second and the time as third.
This third should be beetwen 0 and 1.
Aside from that, you can always make the number negative after lerping it.
Some examples :
In case you want from -10 to 10, lerp from 0 to 20 and then substract 10.
float whatever = Mathf.Lerp(0f, 20f, t);
whatever -= 10f;
In case you want from 0 to -50, lerp from 0 to 50 and then make it negative.
float whatever = Mathf.Lerp(0f, 50f, t);
whatever = -whatever;
The t value in a lerp function between a and b typically needs to be in the 0-1 range. When t is 0, the function returns a and when t is 1, the function returns b. All the values of t in between will return a value between a and b as a linear function. This is a simplified one-dimensional lerp:
return a + (b - a) * t;
In this context, negative values of t don't make sense. For example, if your speed variable is 4.0f, the result of lerp(0, 4, -0.01f) would be:
0 + (4 - 0) * -0.01
which returns -0.04.
The simplest way to solve this issue is to simply flip a and b instead of trying to use a negative t.
_speed = Mathf.Lerp(_speed, 0.0f, 0.01f);
Another suggestion would be to use Mathf.SmoothStep and store the original speed instead of applying the function on the constantly changing _speed to get a really smooth transition.
Well, you cannot actually.
A psuedo-codish implementation of a Lerp function would look like this:
float Lerp(float begin, float end, float t)
{
t = Mathf.Clamp(t, 0, 1);
return begin * (1 - t) + end * t;
}
Therefore, any attempt to give a t value outside of [0, 1], ie. an extrapolate attempt, would be clamped to the valid range.

Getting a proper rotation from a vector direction

I currently have this code in my game:
Vector2 pixelpos = new Vector2(x, y);
Vector2 center = new Vector2(t.Width / 2, t.Height / 2);
Vector2 pixelposWorld = (pixelpos - center);
float rotation = (float)Math.Atan2(pixelposWorld.Y, pixelposWorld.X);
float rotationPercent = (MathHelper.ToDegrees(rotation) / 360);
My goal is to end up with rotationPercent to be a value between 0.0 and 1.0, 0 degrees being 0.0, 180 being 0.5 and 360 being 1.0.
Currently, rotationPercent only comes out as 1.0.
What can I do to fix this?
First of all, in your case, there is no need to convert it to degrees, since Math.aTan2 returns the angle in radians, just divide your rotation variable by (2*Pi).
Secondly, check what you are doing at "t.Width / 2, t.Height / 2", as you haven't specified in your question what 't' is, make sure it's members are not integers.
Now as far as your problem itself, there is not enough information supplied. Where does this contain your rotation information? Is the 'pixelpos' vector your world space position, or do you also use that as rotation?
Brought back to a minimum, the following code works roughly like like you described?
Vector2 pixelpos = new Vector2(0, 1);
float rotation = (float)(Math.Atan2(pixelpos.Y, pixelpos.X) / (2 * Math.PI));
Which results 0.25, or 90 degrees.
The best way to calculate this would be through the use of dot products though:
Vector2 pixelposWorld = Vector2.Normalize(pixelpos - center);
float dot = Vector2.Dot(Vector2.UnitX, pixelposWorld);
float rotation = (pixelposWorld.Y >= 0)
? (1f - dot) / 4f
: (dot + 3f) / 4f;
Just saying, it is on average 120% faster (I ran the tests).
I think you forgot to use float in 360, it should be 360f.

Categories

Resources