Move a game object between two positions in a sin wave - c#

I have two positions in a unity scene and I want a sphere game object to move between them. This is easy enough to do with the move towards command, but what I want to happen is the ball to accelerate when leaving one, then decelerate when approaching the next in a controlled curve. I need it to osculate between the two positions in a manor similar to that of a ball being rolled.
I was told that I could do this with a sin wave, however I'm unsure how. Is there a simple way to do this ?

Preamble
There are a number of ways to solve your problem, not only by using sine function.
Sine function example
Here's an example:
public float frequency = 1.0f; // in Hz
public Vector3 positionA;
public Vector3 positionB;
private float elapsedTime = 0.0f;
public void Update()
{
elapsedTime += Time.deltaTime;
float cosineValue = Mathf.Cos(2.0f * Mathf.PI * frequency * elapsedTime);
transform.position = positionA + (positionB - positionA) * 0.5f * (1 - cosineValue);
}
Cosine is used here instead of sine just for convenience.

I faced with almost the same task. My objects were located in random positions, the line between two of them was not parallel to axis. So, I created common solution which can helps anybody.
Here is demo video, source code on Github.
The script is very simple by itself. All we need to do is calculate two vectors - vector of main direction and orthogonal vector. After this we need to subordinate orthogonal vector by sine function, and then perform addition of these two vectors, so the sum will move object following exact sine curve.
void FixedUpdate () {
float t = Time.time - startTime;
rb.velocity = direction * speed + orthogonal * amplitude * Mathf.Sin (frequency * t);
}
You can find more details here - how to setup scene, components, etc.

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

Unity3D make object follow another but to move further

So, here is what i have:
SteamVR's hand gameobject
3D sphere.
what i want:
The sphere to move to same direction/position as the hand does, but it to move further with a multiplier. E.g. i move the VR controller and the hand moves 1 unit. I want that the sphere moves to the same direction in a same amount of time but e.g. 2 units. how do i do this?
i tried simple
sphere.transform.position = ControllerToFollow.position +2f;
but then the sphere is always offset.
position is a Vector3, which is essentially 3 floats - you can't plus a Vector3 with a float unless you overload the + operator. Otherwise what you can do is the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f,
ControllerToFollow.position.y + 2f,
ControllerToFollow.position.z + 2f);
sphere.transform.position = followPos;
If you only want it to follow on one axis, then you can do the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f, // Follow on x Axis
ControllerToFollow.position.y, // Y axis is the same
ControllerToFollow.position.z); // X Axis is the same
sphere.transform.position = followPos;
Edit: I think I understand your problem better now. Here's a better version.
if (Vector3.Distance(sphere.transform.position, ControllerToFollow.position) >= 2f)
{
// Code that makes the sphere follow the controlling
}
Just track the movement Delta of the hand and multiply it by a certain multiplier.
At the beginning of the manipulation store
private Vector3 lastControllerPosition;
...
lastControllerPosition = ControllerToFollow.position;
then in every frame compare
var delta = ControllerToFollow.position - lastHandPosition;
// Don't forget to update lastControllerPosition for the next frame
lastControllerPosition = ControllerToFollow.position;
Now in delta you have a movement of the controller since the last frame. So you can assign it to the sphere with a multiplier using Transform.Translate
sphere.transform.Translate(delta * multiplier, Space.World);
or simply using
sphere.transform.position += delta * multiplier;

Moving a sprite using math in Unity

I am currently making a game in Unity, where characters are moving towards a point at the bottom of the screen, from a random x-position at the top of the screen. As they have to keep a constant speed (and later on possibly be able to move in specific patterns), I cannot use Vector3.Lerpfor this.
Instead, I tried using some simple math. StaticInfo.instance.GetPlayerPosition() is the targeted position. The code happens in the character's FixedUpdate().
float aVal = (myTransform.localPosition.y - StaticInfo.instance.GetPlayerPosition().y) /
(myTransform.localPosition.x - StaticInfo.instance.GetPlayerPosition().x);
float degrees = Mathf.Atan(aVal) * Mathf.Rad2Deg;
Vector3 newPos = new Vector3(myTransform.localPosition.x - (distance * speed * Mathf.Cos(degrees)),
myTransform.localPosition.y - (distance * speed * Mathf.Sin(degrees)),
1);
myTransform.localPosition = newPos;
My problem is when these characters are created (instantiated from prefab), they make a small 180-degrees loop before moving in the wanted direction. After that, they move exactly as wanted.
Is using math the best solution? And if it is, why does it do a funky drift when initialized?
This may be a more simple solution to what you are trying to achieve:
Vector3 moveDirection = (StaticInfo.instance.GetPlayerPosition() - myTransform.localPosition).normalized;
Vector3 newPos = myTransform.localPosition + moveDirection * distance * speed;
myTransform.localPosition = newPos;
If you want the object to point in the direction of the movement you can do:
myTransform.rotation = Quaternion.Euler(0, 0, Mathf.atan2(moveDirection.y, moveDirection.x)*Mathf.Rad2Deg - 90); // may not need to offset by 90 degrees here;

Translate object until collision

I want my object to keep moving in the target's direction forever or until it collides, the collision part I have already handled it; However, I am having problems with the movement part.
I first try to rotate my target using these lines of code
Vector2 diff = target - transform.position;
float angle = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, angle);
This works perfect and my object rotates in the direction I want it to.
In my Update method I have the following
if (isMoving)
{
Vector2 f = transform.forward;
transform.position = Vector3.MoveTowards(transform.position, target + Vector3.forward, speed * Time.deltaTime);
}
Now this runs but fails to accomplish the goal and I know why, it makes sense but not sure how to fix it. The object moves to the point in the correct direction but I don't want it to stop at the target, I want it to keep going.
I also tried
rb.MovePosition(rb.position + f * Time.deltaTime * speed);
rb is a rigidbody2D
as well as
rb.AddForce(rb.position + f * Time.deltaTime * speed);
But in both cases the object rotates but never moves
I also used translate and same behavior as MovePosition
P.S. It's a 2D game
After looking over the internet, I didn't find anything that really does what I am looking for, you are not able to translate a kinematic object forever (Obviously you have to handle that the object should get destroyed at some point or something, but that's not the issue). So I went ahead and decided to write my own library to make this simple. I was able to achieve my goal using the line equation. Simple I took these steps to solve my issue.
At start I get the slope between 2 points
Calculate the y-intercept
translate the object using the line equation with a fixed X value (step), every x value find the corresponding y value and translate the object to it.
Repeat step 3 in a FixedUpdate and that's it.
Of course there is more to it, handling cases where x = 0 which will give a 0 division for the slope or y = 0, etc... I solved all of that in the library. For anyone interested you can check it out here EasyMovement
If you don't want the library then here is a simple code that will do it.
//Start by Defining 5 variables
private float slope;
private float yintercept;
private float nextY;
private float nextX;
private float step;
private Vector3 direction; //This is your direction position, can be anything (e.g. a mouse click)
In the Start Method
//step can be anything you want, how many steps you want your object to take, I prefer 0.5f
step = 0.5f;
//Calculate Slope => (y1-y2)/(x1-x2)
slope = (gameObject.transform.position.y - direction.y) / (gameObject.transform.position.x - direction.x);
//Calculate Y Intercept => y1-x1*m
yintercept = gameObject.transform.position.y - (gameObject.transform.position.x * slope);
Now in FixedUpdate
//Start by calculating the nextX value
//nextX
if (direction.x > 0)
nextX = gameObject.transform.position.x + step;
else
nextX = gameObject.transform.position.x - step;
//Now calcuate nextY by plugging everything into a line equation
nextY = (slope * nextX) + yintercept;
//Finally move your object into the new coordinates
gameObject.transform.position = Vector3.MoveTowards(gameObject.transform.position, new Vector3(nextX, nextY, 0), speed * Time.deltaTime);
Keep in mind this won't do the heavy lifting, there are a lot of conditions that needs to be taken into consideration.

Using Vector3.slerp

I just want to know if I'm using vector3.slerp correctly as I have some issues.
I have set up a cube in a scene and I want it move smoothly from its current position to one being passed into by the program. Before I was trying to use the slerp I simply had my cube moving from its current point to its new point like this (as I said, the math all works):
cubeFour.transform.localPosition = new Vector3((B2*C1 - B1*C2)/delta,0,(A1*C2 - A2*C1)/delta);
But when I put it in a slerp call, my cube is no longer on the screen. This is how I'm calling it:
Vector3 targetPosition = new Vector3(lerpX, lerpY, lerpZ);
cubeFour.transform.localPosition = Vector3.Slerp(cubeFour.transform.localPosition, targetPosition, Time.deltaTime);
LerpX, LerpY & LerpZ are local variables I've set to contain the X, Y & Z of the first Vector3 I created in my first attempt.
Have I set up the slerp correctly or have I made a kerfuffle somewhere?
Slerp is best for directions, Lerp is best for positions. You should probably be using Lerp. And Time.deltaTime is almost certainly the wrong choice for t. You should be giving it a number that moves from 0 to 1 over the time that you want the cube to move, e.g.
float moveTimeInSeconds = 2;
cubeFour.transform.localPosition = Vector3.Lerp(cubeStartingPosition, targetPosition, (Time.time - startTime) / moveTimeInSeconds);
Or use MoveTowards if it makes more sense to define the speed of motion, and to move towards a target regardless of a starting point, instead of via starting and ending positions and times.
float step = speed * Time.deltaTime;
cubeFour.transform.localPosition = Vector3.MoveTowards(cubeFour.transform.localPosition, targetPosition, step);
Lerp uses a percentage to animate!
This means you must plug in a percentage, not a value near the same thing each frame.
Example Update():
var animationSpeedInSeconds=1;
var animationCounter:float=0;
function Update(){
if(animationCounter>=0 && animationCounter<=1){
animationCounter+=Time.deltaTime/animationSpeedInSeconds;
cubeFour.transform.localPosition = Vector3.MoveTowards(cubeFour.transform.localPosition, targetPosition, animationCounter);
}
}
I hope this helps!

Categories