Lerp alpha based on distance between 2 objects - c#

So I am trying to increase an image alpha channel based on the fact that an object is getting closer and closer to the player. I am using Vector3.Distance()
to get the distance from the player to the object but I don't know how should I convert the distance so that the value of color.a will get bigger and bigger as the distance get's smaller and smaller.
Please point me in the right direction;
How can I make a number bigger based on the fact that another number is getting smaller?

See this post that explains how to lerp a color based on distance between two GameObjects. The only difference is that you want to lerp the alpha instead so everything written on that post should still be relevant to this. Just few modifications need to be made.
You just need to use Mathf.Lerp instead of Color.Lerp. Also, you need to enable fade mode on the material. You can do that from the Editor or script. The code below is a modified code from the linked answer that should accomplish what you are doing. It also enables fade mode from code in the Start function.
public GameObject obj1;
public GameObject obj2;
const float MAX_DISTANCE = 200;
Renderer mRenderer;
void Start()
{
mRenderer = GetComponent<Renderer>();
//ENABLE FADE Mode on the material if not done already
mRenderer.material.SetFloat("_Mode", 2);
mRenderer.material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
mRenderer.material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
mRenderer.material.SetInt("_ZWrite", 0);
mRenderer.material.DisableKeyword("_ALPHATEST_ON");
mRenderer.material.EnableKeyword("_ALPHABLEND_ON");
mRenderer.material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mRenderer.material.renderQueue = 3000;
}
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 Alpha between near and far color
Color lerpColor = mRenderer.material.color;
lerpColor.a = Mathf.Lerp(1, 0, lerp);
mRenderer.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;
}

I don't know how you want the effect to look, but it's basically a math question.
Something like:
color.a = 1.0 / distance
should get you started. Basically, the further from 1 distance gets (increasing), the closer to 0 color.a gets (decreasing). The opposite is thus true if distance decreases (color.a then increases).
You have to deal with values for distance getting inferior to 1 (if applicable), as it won't increase color.a anymore.
You also have to deal with color.a max possible value: is it 1.0 or 255 (or something else)? Replace the 1.0 in the formula with this max value. You may have to multiply distance by an arbitrary value so the effect isn't too fast or too slow.

Sounds like you want a function (in the mathematical sense) f(x) that maps an input (distance) value in the domain [0, infinity) to the output (alpha) range [1, 0]. One simple such function is 1/(1+x) (click the link to see an interactive graph).
You can use the interactive graph and online math resources to play with the equation to find one that looks good to you. Once you have that figured out, implementing it in code should be easy!

Related

How can I ensure that an object using a cosine equation to determine speed meets its lowest valley at the same time it hits a waypoint?

I'm working on a game for game jam and part of it is to make platforms that move smoothly. They slow down at the ends of their movement before turning around. The platforms simply move side to side or up and down between two waypoints(which are just empty transforms). I have code that uses cosine to determine the speed which works well except it doesn't align with the waypoints, the platforms tend to slow and change direction before ever reaching the waypoints. I need a way to use the distance between the waypoints as a variable in determining how the cosine equation changes speed so that the platforms slow and reverse direction exactly at the waypoints.
Here is what I have so far:
void Side_to_side()
{
if (waypointIndex < horWaypoints.Length)
{
platformSpeed = (1f * (float)Mathf.Cos(2f * (float)Mathf.PI * 1f * totalTime));
Vector3 targetPosition = horWaypoints[waypointIndex].position;
float delta = platformSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, targetPosition, delta);
if (transform.position.x == targetPosition.x && transform.position.y == targetPosition.y)
{
if (waypointIndex + 1 == horWaypoints.Length)
waypointIndex = 0;
else
waypointIndex++;
}
}
else
{
waypointIndex = 0;
}
//Translate platform back and forth between two waypoints
}
As I have said this code moves the platforms in the motions i want but they don't use the waypoints as turn around points. I understand I could do away with the waypoints and just calculate how far I would like each platform to go before turning around individually but that would take time to do it for each platform whereas I'd like to quickly put down waypoint pairs for them to use and the script calculates what the perfect values would be to match the waypoint locations.
If I understand you correctly you want to move an object forth and back between exactly two positions and apply some smoothing to the movement.
I would rather use a combination of Vector2.Lerp with a Mathf.PingPong as factor and you can then apply ease in and out using additionally Mathf.SmoothStep.
This could look like e.g.
public Transform startPoint;
public Transform endPoint;
// Desired duration in seconds to go exactly one loop startPoint -> endPoint -> startPoint
public float duration = 1f;
private void Update ()
{
// This will move forth and back between 0 and 1 within "duration" seconds
var factor = Mathf.PingPong(Time.time / (2f * duration), 1f);
// This adds additional ease-in and -out near to 0 and 1
factor = Mathf.SmoothStep(0, 1, factor);
// This interpolates between the given positions according to the given factor
transform.position = Vector2.Lerp(startPoint, endPoint, factor);
}
you could of course still use cosine if necessary, basically any function that returns a value between 0 and 1. You just have to use the correct multiplier in order to achieve the desired duration in seconds.
Note: Typed on the phone and not 100% sure on the math but I hope the idea gets clear

How to convert lerp to cubic easing in?

I need to convert the following code from using lerp to using cubic easing in, but I don't know what to do with the returned float value from the cubic easing in function, because I'm using Vector3 instead of floats. Can someone please give me a hint?
if (Vector3.Distance(activeTween.Target.position, activeTween.EndPos) > 0.1f)
{
float fraction = (Time.time - activeTween.StartTime) / activeTween.Duration;
activeTween.Target.position = Vector3.Lerp(activeTween.StartPos, activeTween.EndPos, fraction);
}
You should probably just use it in place of your fraction in the Vector3.Lerp.
Lerp is just the linear interpolation of vectors, i.e. 0.3 means 30% from one and 70% from the other. If we want non linear interpolation we can just apply some arbitrary function to the input value. For example, that 0.3 may be transformed to 0.027, and that can then be used as the input to Lerp to get 2.7% / 97.3% blend instead.
You need a cubic function that is 0 at the start and 1 and the end.
I would try something like:
public float EaseIn(float t)
{
return t * t * t;
}
if (Vector3.Distance(activeTween.Target.position, activeTween.EndPos) > 0.1f)
{
float fraction = (Time.time - activeTween.StartTime) / activeTween.Duration;
activeTween.Target.position = Vector3.Lerp(activeTween.StartPos, activeTween.EndPos, EaseIn(fraction));
}
Assuming that fraction was already going from 0 to 1.

Camera is not centering between 2 gameobjects correctly

I have a button that makes the MainCamera switch between being in the middle of several objects.
For example, there's the [StartPoint , CheckPoint , CheckPoint , EndPoint]
I want the camera to switch continuously across the checkpoints like the comma between the checkpoints. ^^
This code is the actual switching the position placing for the MainCamera (where the error might be)
public void SwitchCameraBetween(GameObject nextPoint, GameObject afterNextPoint)
{
Vector3 centerPoint = (afterNextPoint.transform.position - nextPoint.transform.position) / 2;
centerPoint.z = -10;
float cameraSize = (afterNextPoint.transform.position - nextPoint.transform.position).magnitude - nextPoint.transform.localScale.x;
mainCamera.transform.position = centerPoint;
mainCamera.orthographicSize = cameraSize / 3;
}
The camera switches perfect for the first 2 checkpoints (either StartPoint & EndPoint or StartPoint & CheckPoint) after the camera stays in same position or off by a couple values. I checked by using Debug.Log(); to see if the camera has the correct gameobjects between and it does so why does it not work?
The center point should be the average of all points, so you need to add your points, not subtract them, and then divide the result by the number of points included for the average.
center = sum(points) / count(points), so center = (a + b) / 2
Alternatively, you could use Vector3.Lerp(a, b, 0.5f) if you find that more readable, although technically this would be slower, since it's both a method call and more operations, unless the compiler is doing fancy things behind the scenes...

Leap Motion Get Finger Bending (C#)

Hello I tried a lot diffrent ways to get bending angle in Leap Motion. But I couldn't get true values. I used this method for reading. Thanks in advance.
Bone bone1 = finger.Bone(Bone.BoneType.TYPE_INTERMEDIATE);
Bone bone2 = finger.Bone(Bone.BoneType.TYPE_PROXIMAL);
double angle = 180 - ((bone1.Direction.AngleTo(bone2.Direction) / Math.PI) * 180) * 2;
In this example I'll use array accessors as a quicker way of accessing bones from Finger objects.
For reference, bone indices are: 0 = metacarpal, 1 = proximal, 2 = intermediate, 3 = distal. You can validate this by looking at the definition of BoneType.
(Careful, the thumb has one fewer bone than the other fingers.)
Vector3 bone0Dir = finger.bones[0].Direction.ToVector3();
Vector3 bone1Dir = finger.bones[1].Direction.ToVector3();
float angle = Vector3.Angle(bone0Dir, bone1Dir);
This example retrieves the angle in degrees between the metacarpal bone and the proximal bone of the finger object.
Note that Vector3.Angle returns the unsigned angle between the two bones; if you desire a signed angle, you can use the SignedAngle function instead. You'll need to pass it a reference axis; Vector3.Cross(hand.PalmarAxis(), hand.DistalAxis()) would be suitable for this.
EDIT: Ah, apologies, the answer is a bit different if you're outside of the Unity engine. In that case, Leap.Vector.AngleTo is sufficient, but there's a simpler way to convert radians to degrees:
Vector bone0Dir = finger.bones[0].Direction;
Vector bone1Dir = finger.bones[1].Direction;
float angle = bone0Dir.AngleTo(bone1Dir) * Vector.RAD_TO_DEG;
Again, this will return the unsigned angle, but fingers don't usually bend backwards, so this should be sufficient for your use-case. You can also use Math.Asin((bone0Dir.Cross(bone1Dir).Magnitude)) * RAD_TO_DEG to get a (right-handed) signed angle.

What's wrong with this XNA RotateVector2 function?

I know this is probably a very simple question, but I can't seem to figure it out. First of all, I want to specify that I did look over Google and SO for half an hour or so without finding the answer to my question(yes, I am serious).
Basically, I want to rotate a Vector2 around a point(which, in my case, is always the (0, 0) vector). So, I tried to make a function to do it with the parameters being the point to rotate and the angle(in degrees) to rotate by.
Here's a quick drawing showing what I'm trying to achieve:
I want to take V1(red vector), rotate it by an angle A(blue), to obtain a new vector (V2, green). In this example I used one of the simplest case: V1 on the axis, and a 90 degree angle, but I want the function to handle more "complicated" cases too.
So here's my function:
public static Vector2 RotateVector2(Vector2 point, float degrees)
{
return Vector2.Transform(point,
Matrix.CreateRotationZ(MathHelper.ToRadians(degrees)));
}
So, what am I doing wrong? When I run the code and call this function with the (0, -1) vector and a 90 degrees angle, I get the vector (1, 4.371139E-08) ...
Also, what if I want to accept a point to rotate around as a parameter too? So that the rotation doesn't always happen around (0, 0)...
Chris Schmich's answer regarding floating point precision and using radians is correct. I suggest an alternate implementation for RotateVector2 and answer the second part of your question.
Building a 4x4 rotation matrix to rotate a vector will cause a lot of unnecessary operations. The matrix transform is actually doing the following but using a lot of redundant math:
public static Vector2 RotateVector2(Vector2 point, float radians)
{
float cosRadians = (float)Math.Cos(radians);
float sinRadians = (float)Math.Sin(radians);
return new Vector2(
point.X * cosRadians - point.Y * sinRadians,
point.X * sinRadians + point.Y * cosRadians);
}
If you want to rotate around an arbitrary point, you first need to translate your space so that the point to be rotated around is the origin, do the rotation and then reverse the translation.
public static Vector2 RotateVector2(Vector2 point, float radians, Vector2 pivot)
{
float cosRadians = (float)Math.Cos(radians);
float sinRadians = (float)Math.Sin(radians);
Vector2 translatedPoint = new Vector2();
translatedPoint.X = point.X - pivot.X;
translatedPoint.Y = point.Y - pivot.Y;
Vector2 rotatedPoint = new Vector2();
rotatedPoint.X = translatedPoint.X * cosRadians - translatedPoint.Y * sinRadians + pivot.X;
rotatedPoint.Y = translatedPoint.X * sinRadians + translatedPoint.Y * cosRadians + pivot.Y;
return rotatedPoint;
}
Note that the vector arithmetic has been inlined for maximum speed.
So, what am I doing wrong? When I run the code and call this function with the (0, -1) vector and a 90 degrees angle, I get the vector (1, 4.371139E-08) ...
Your code is correct, this is just a floating point representation issue. 4.371139E-08 is essentially zero (it's 0.0000000431139), but the transformation did not produce a value that was exactly zero. This is a common problem with floating point that you should be aware of. This SO answer has some additional good points about floating point.
Also, if possible, you should stick with radians instead of using degrees. This is likely introducing more error into your computations.

Categories