How to move the object (two-dimensional) based on its angle? - c#

I'm studying some details in unity where I ended up falling into a dead end. First of all I would like to know, is there probably a functionality for that, but how increasing speed to an object based on the angle it is looking at. Please could you demonstrate with examples because so far my ideas are dull. I tried to do this functionality using math (probably a bad idea), because I was curious to know how such a situation worked in mathematical practice, developed some theories about it:
First I thought, at the 90 degree angle, in one move, only the Y axis increases, so the X axis stays at 0. So to find out the exact variation of X and Y for a given angle, all you have to do is to find the angle secant and increment it, or is in another point? Probably my thinking is wrong, I'm "new" with trigonometry, but I would love to know what I was wrong, and if I was wrong, what is right to think.
If I said a lot of things wrong, let me know, because I know I need to study more :)
Below is the code of the function I created, which initially just made my character rotate on the Y axis, so just increment or decrement the speed value. So I added a method to generate a random angle based on the point of view that the object is looking at, so I just used different quadrants.
void invertMove()
{
if(!inverse)
{
float tempRot = Random.Range(180f, 360f);
zombieSpeed = -0.01f;
zombieBody.SetRotation(tempRot);
inverse = true;
}
else
{
float tempRot = Random.Range(0f, 180f);
zombieSpeed = 0.01f;
zombieBody.SetRotation(tempRot);
inverse = false;
}
}

We don't know what zombieSpeed does but assuming you are talking about Rigidbody2D.SetRotation here you probably wanted to assign a new velocity.
The velocity is in global space. You can't use e.g. transform.right as new direction since the transform isn't updated yet .. only the zombieBody is .. but you can use Rigidbody2D.GetRelativeVector in order to set the new local direction after rotating
Get a global space vector given the vector relativeVector in rigidBody local space.
// or Vector2.up depending on your setup
zombieBody.velocity = zombieBody.GetRelativeVector(Vector2.right * 0.01f);
So depending on your needs you wouldn't differ between a positive and negative speed but rather stay at 0.01f and use
For creating a new completely random direaction
void newRandomDirection()
float newRot = Random.Range(0f, 360f);
zombieBody.SetRotation(newRot);
zombieBody.velocity = zombieBody.GetRelativeVector(Vector2.right * 0.01f);
}
For actually inverting the direction you could simply do
void invertMove()
{
var newRot = zombieBody.rotation + 180f;
// optionally you can always fix it to a value 0-360 but that's not really required
//if(newRot > 360f)
//{
// newRot -= 360f;
//}
zombieBody.SetRotation(newRot);
zombieBody.velocity = zombieBody.GetRelativeVector(Vector2.right * 0.01f);
}
Or if what you want is getting a new random direction somewhere between -90(== +270) to 90 degrees to the current direction I would rather use
void invertMoveHemiSphere()
{
var newRot = zombieBody.rotation + Random.Range(90f, 270f);
// optionally you can always fix it to a value 0-360 but that's not really required
//if(newRot < 0f)
//{
// newRot += 360f;
//}
//else if(newRot > 360f)
//{
// newRot -= 360f;
//}
zombieBody.SetRotation(newRot);
zombieBody.velocity = zombieBody.GetRelativeVector(Vector2.right * 0.01f);
}
or sticking to what you did so far
void invertMove()
{
inverse = !inverse;
float newRot = inverse? Random.Range(0f, 180f) : Random.Range(180f, 360f);
zombieBody.SetRotation(newRot);
zombieBody.velocity = zombieBody.GetRelativeVector(Vector2.right * 0.01f);
}

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

'Transform.Rotate(Vector3, Space)' is a method, which is not valid in the given context

So, I wanted my gameObject to rotate around the y axis until it reached a certain number, but I'm unsure how to procceed.
void Update()
{
if (target == 1) {
this.transform.position += new Vector3(1,0,1) * Time.deltaTime * speed;
// this next part is where I'm lost:
if (this.transform.rotation.eulerAngles.y < 30f) {
this.transform.Rotate.eulerAngles += (0,1,0, Space.Self);
}
}
Rotate is a method and you would use it like
void Update()
{
if (target == 1)
{
transform.position += new Vector3(1,0,1) * Time.deltaTime * speed;
if (this.transform.rotation.eulerAngles.y < 30f)
{
this.transform.Rotate(0, 1 * Time.deltaTime, 0);
}
}
Note that I changed two things:
your code is frame-rate dependent! Rather multiply the amount of rotation (just as you did with the position) by Time.deltaTime in order to convert it from value/frame into value/second. Of course this currently would rotate with only 1°/second .. you probably want something faster
Space.Self is the default value for the optional parameter space so you only have to pass it in if you want to use Space.World
However, there is another flaw
eulerAngles shouldn't be used at all for assigning or checking continously updated values!
When using the .eulerAngles property to set a rotation, it is important to understand that although you are providing X, Y, and Z rotation values to describe your rotation, those values are not stored in the rotation. Instead, the X, Y & Z values are converted to the Quaternion's internal format.
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation. See bottom scripting example for more information.
You might want to consider to rather pre-calculate the target rotation as a Quaternion e.g. like
Quaternion targetRotation;
private void Start ()
{
targetRotation = Quaternion.Euler(0, 30, 0);
}
and then later rather use e.g. Quaternion.RotateTowards
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, targetRotation, Time.deltaTime * rotationAbglesPerSecond);

Unity Vector in If Else Statements

I'm a beginner when it comes to coding so please bear with me. I'm trying to create a boomerang mechanic for a 2d platformer where the boomerang switches directions when it reached at a certain point in which I can set it in the inspector. The way I thought about it was if I subtract the coordinates of the boomerang and the point of destination I will get the distance in between and put it in an if statement and compare to see if the boomerangs position is greater than and equal to the point so it can change its direction. However I receive this error that says
operator cannot be applied to operands to float and vector 2.
below is my code for the boomerang:
public Transform Target;
public float speed; // speed it travels
public Vector2 returnDistance; // The point in where boomerang switches
direction
private bool keepGoing = false; // Update the frame to make it keep going
// Use this for initialization
void Start () {
//rb2d = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
float checkDistance = Vector2.Distance(transform.position,
returnDistance); // check the distance between two points
Debug.DrawLine(Target.position, transform.position, Color.red);
if (Input.GetMouseButton(0) || (keepGoing == true))
{
transform.Translate(Vector2.right * speed * Time.deltaTime); // move
the boomerang to the right
keepGoing = true;
}
if (checkDistance >= returnDistance)
{
transform.Translate(-Vector2.right * speed * Time.deltaTime); // move
the boomerang to the left
keepGoing = true;
}
}
}
So how should I go about this so that I cant take the value I set it up in the inspector for the boomerangs turning point and put it in an if statement?
You should compare checkDistance variable to some treshhold float value (close to zero, but not 0f) — 0.1f or 0.01f etc (you should figure it out by testing).
UPD: I think I should extend my answer to clarify the problem here. You are trying to compare float and [float; float]. Obviously it does not make sense. So you should compare two float values.
If you want boomerang to fly for some fixed distance from who is throwing it, you may campare distance from your character to boomerang with some float value (max distance of throw).
Or if you want boomerang to return after it reaches some point in space you should do as I wrote at the beginning — compare distance from boomerang to that point in space with some float value (close enough to consider it boomerang did hit the target)
You said "I'm trying to create a boomerang mechanic for a 2d platformer where the boomerang switches directions when it reached at a certain point". Then you can use Vector2.sqrMagnitude.
Here is an example of using this vector
public static float SqrMagnitude(Vector2 a)
{
return a.x * a.x + a.y * a.y;
}
So in your case it could be something like this
Vector2 dir = P2 - P1;
float length = dir.magnitude;
dir -= length;
Hope this help.
If ((Checkdistance - returndistance) > 0f)

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.

Gravity with air-time, acceleration and speed gaining

I am trying to accomplish a gravity, where airtime is included, and also acceleration.I have tried using usual gravity, which looks something like this:
velocity += gravity * dt;
position += velocity * dt;
This would probably work good enough for a normal platformer game, but I am trying to make a game inspired by "The helicopter game", where you have to navigate through a tunnel, without touching the walls.what I want to do different, is that I want to be able to save up speed on the way down, which will be used on the way up again, so I will have some acceleration at the beginning.I also want some kind of airtime, so when you hit the top it would not force you down as fast as It would, if I had used the gravity from the code sample.This image illustrates the curve I would like to have:Please note that the whole controlling is done by one key, so for example you would fly up if you held down space, and dive if you released it.The character also never moves left or right, since it will have a static X position on the screen, so vectors can't be used.I have spent hours trying to make it work, but with no success. I have also tried searching on the internet, but without any luck.The game "Whale Trails" got the gravity I kind of want.Here is a link for a video of the game: http://www.youtube.com/watch?v=5OQ0OWcuDJsI'm not that big of a physics guy, so it would be cool if you could give an example of the actual code I hope anyone can help me figure this out.
Gravity is the force that pulls objects down. Your player is the force that pulls objects up. Accordingly your code must be:
if(keyPressed) {
velocity += POWER_OF_PLAYER;
}
velocity += G;
position += velocity;
This is enough to create a curve like you illustrated. Of course POWER_OF_PLAYER must be of a different sign and the absolute value must be greater to make this work.
G = -9.81
POWER_OF_PLAYER = 20
Saving power is then a simple check.
if(keyPressed) {
if(powerSaved > 0) {
velocity += POWER_OF_PLAYER;
powerSaved -= SOMETHING;
}
} else if (velocity >= SOME_MINIMUM_SPEED_BEFORE_GETTING_POWER) {
powerSaved += SOMETHING;
}
SOME_MINIMUM_SPEED_BEFORE_GETTING_POWER should be something less or equal 0.
P.S. I assumed your Y axis starts at ground and shoots into the sky. Signs put accordingly.
It looks like the velocity of the fish is constant.
Try the following:
velocity is fixed and should not change (unless the fish eats a speed power-up).
angle = 0 is equivalent to level flight.
angle -= gravity * dt;
if (angle < - Math.PI / 2) {
angle = Math.PI / 2;
}
if (keyPressed) {
angle += power * dt;
}
if (angle < - Math.PI / 2) {
// Stop the player from doing a looping
angle = - Math.PI / 2;
}
if (angle > Math.PI / 2) {
// Stop the player from doing an inverted looping
angle = Math.PI / 2;
}
// Move the fish (vertical component of velocity)
position += velocity * dt * Math.sin(angle);
// Move the background (horizontal component of velocity)
background_position -= velocity * dt * Math.sin(angle);
It sounds like incorporating 'lift' that is based on horizontal speed and have the button press trigger a 'noseup' movement would work sufficiently well.
So lift would be some constant k multiplied by the horizontal speed Vx and vertical speed Vy would be the difference of gravity and lift times the change in time dt
lift = k * Vx
Vy += ( lift - gravity ) * dt
def noseup
k = 0.01 #some small chunk
dx = k * Vx
dy = k * Vy
Vx -= dy
Vy += dx
When the plane (or whatever) noses up it basically lowers the velocity on one axis while increasing it on the other.
Probably wouldn't be a bad idea to throw drag in there somewhere as well now that I think about it, would have to be dependent on the absolute velocity V = ( Vx**2 + Vy**2 )**0.5...and weight is a better word than gravity in this case (less confusing, units wise) but it works I guess.
Not exactly "physics" but a close approximation that should work fairly well. Play around with the values of k and see if you can make it do what you want.
BTW sorry for the uber crappy psuedocode :-P

Categories