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)
Related
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
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);
I cannot figure out how to make a tree with a random Y-rotation fall after cutting it down. I want it to fall until it is perpendicular to the ground (90 degrees), but I want it to be random which direction it falls in.
The pivot is on the bottom of the tree, so if I just rotate Z-axis to 90 degrees then it looks like it falls, but I want to randomize the direction, I tried some stuff but its not doing what I expect:
public void Fall(float duration)
{
int xOrZ = Random.Range(0, 1);
float randomRot = Random.Range(0,90);
Vector3 rotation = Vector3.zero;
if (xOrZ == 0)
rotation = new Vector3(90, 0, randomRot);
else
rotation = new Vector3(randomRot, 0, 90);
mTransform.DORotate(rotation, duration);
}
I figured if I make sure one of the axis is 90 then it will always fall to the ground but that didnt work at all.
Since the pivot of your object is at the bottom, it is easier to Lerp the transform.up vector of the tree GameObject in a way that it starts to tilt. You can pick a point inside a unit circle using Unity's Random.InsideUnitCircle. This would return a Vector2, then you can multiply each component with your right and forward vectors to find where the tip of the tree will be when it falls. Now, the vector between the tree object's position and the falling point should be your new up vector for the tree object. Then you can run a simple Coroutine that Lerps the up vector of the tree object to the newly calculated up vector. The Lerp allows you to define the duration and you can cut the Coroutine off once the distance between the desired up vector and the current up vector is below a certain threshold. If you use this code and call the Fall function just as you desired it would work:
using System.Collections; // for IEnumerator
public GameObject treeObject; // assuming this is your tree object
public void Fall(float duration)
{
// pick a random point on the circle to match the up vector
Vector2 pointOnCircle = UnityEngine.Random.insideUnitCircle * treeObject.transform.localScale.y;
// find the fall point, assuming the pivot of the object is at the bottom
Vector3 fallPoint = treeObject.transform.position +
pointOnCircle.x * treeObject.transform.right +
pointOnCircle.y * treeObject.transform.forward;
// find the target up vector
Vector3 updatedUpVector = Vector3.Normalize(fallPoint - treeObject.transform.position);
// Start the coroutine to tilt the up vector to the desired target
StartCoroutine(UpdateUpVector(treeObject, updatedUpVector, duration, 0.001f));
}
public IEnumerator UpdateUpVector(GameObject target, Vector3 upVector, float duration, float threshold = 0.001f)
{
// the target vector and up vector would get closer to each other until the threshold is hit
while(Vector3.Distance(upVector, target.transform.up) > threshold)
{
target.transform.up = Vector3.Lerp(target.transform.up, upVector, duration * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
}
The code above results with this, using a cylinder whose pivot is at the bottom. Notice that the tree is falling towards the random point on the unit circle that is around the transform.position of the tree object.:
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);
}
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.