Trying to figure out a way to increment the rotation count after the spinner passes 360 degrees. Though this never works because eulerAngles reset at 360. What's a good way to do this? I tried doing > 355, but that is't a great solution.
Spinner.transform.Rotate(0, 0, speed);
float angleZ = Spinner.rotation.eulerAngles.z;
if (angleZ > 360)
{
Rotations++;
}
You would need to check the angle before & after the rotation.
According to the documentation "speed" is the number of degrees to rotate, so add the integer division of speed by 360 to Rotations & then check for the final value having been reset.
float angleZ1 = Spinner.rotation.eulerAngles.z;
Spinner.transform.Rotate(0, 0, speed);
float angleZ2 = Spinner.rotation.eulerAngles.z;
float angleZDiff = angleZ2 - angleZ1;
Rotations += (int)(speed / 360);
if ((speed >0) && (angleZDiff < 0))
Rotations++
else if ((speed < 0) && (angleZDiff > 0))
Rotations--;
You can divide the angle by 360 and cast it to an int to get the number of rotations.
Rotations = (int)(angle / 360f);
You could add two colliders at varying distances then the collision check points will give extra data points that are correct regardless of the speed
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
I'm making a game in a custom engine using C#. (Not Unity)
I've got a large grid and the x/y coordinates of two objects. The Player object and the Destination object. Along with the player's current rotation in degrees (0-360).
I've become too reliant on existing game engines and cannot work out how to find the rotation I need to put the player at to face the target.
playerRotation;//0 to 360 degrees.
playerX double = 47.43;
playerY double = 43.36;
targetX double = 52.15;
targetY double = 38.67;
My method at the moment is to try and get the distance between the objects by:
float distanceX = Math.Abs(playerX - destinationX);
float distanceY = Math.Abs(playerY - destinationY);
Which seems to work fine. Then I need to rotate the player to face the destination and have them move towards it until distanceX/Y are <= 0.
Edit: I've been messing with Atan2 to try and get the answer.
Vector2 playerCoords = new Vector2(playerX, playerY);
Vector2 targetCoords = new Vector2(targetX, targetY);
double theta = Math.Atan2((targetCoords.yValue - playerCoords.yValue), (targetCoords.xValue - playerCoords.xValue));
theta = theta * (180 / Math.PI);//Convert theta to degrees.
double sigma = playerRotation;//Direction in degrees the player is currently facing.
double omega = sigma - theta;
OutputLog("omega: " + omega);
My output log should be showing me the degrees my player needs to be facing to be facing the target. But it's giving me the wrong results.
Player: (4782, 4172) and
Target: (4749, 4157)
Angle should be about 286~.
But Theta = -155 and omega = 229.
Vector math can be very helpful, and it's not that complicated.
First vectors would be your player position and destination's:
Vector2 playerPos = new Vector2(playerX, playerY);
Vector2 destinationPos = new Vector2(destinationX, destinationY);
Now you can just subtract both vectors, to get a vector which points from one position to the other.
Vector2 delta = destination - playerPos; // Note, it might be the other way around: playerPos - destination
That delta vector has a length, and that is the distance between both points. There is usually a Length and a LengthSquared property available on the Vector class. Be aware however that calculating the length is quite CPU intensive because it uses a square root. If you want to compare that distance to a fixed distance like 200, just use the length squared and compare it to (200 * 200) which is way faster.
You can also use that delta, to let a bullet fly from one position to the other. You just need to normalize delta, there's a method for it, and you have it scaled down to length one. You can now use that delta, multiplied with a speed on each physics cycle to change the bullets position.
Now to get the angle, you can just use:
double angle = Math.Atan2 (delta.Y, delta.X); // Note that y and x are reversed here, and it should be like that.
Note that this angle is in radians, not degrees. A circle in radians starts at -PI and ends at PI. A full circle therefore is 2 * PI. To convert radians to degrees, you can see this question
Edit
I always assume that 12 o'clock is 0 degrees, 3 o'clock is 90, 6 o'clock is 180 and 9 o'clock is 270.
But actually in the cartesian coordinate system things are a bit different. I also made this false assumption in the code below.
But it turned out that I was wrong. See this picture
Now if you look at my sourcecode, all my variables are named incorrectly. If you look at their values however you can see that they match up with the picutre. Therefore the Atan2 correction is as it's supposed to be.
// Using Vector2 from System.Numerics;
internal static double RadianToDegree(double rad)
{
double thetaDegree = rad * (180.0 / Math.PI);
// Convert negative angles into positive ones
// https://stackoverflow.com/a/25725005/7671671
double thetaDegree2 = (thetaDegree + 360) % 360;
return thetaDegree2;
}
internal void Run()
{
// Player: (4782, 4172) and Target: (4749, 4157)
Vector2 player = new Vector2(4782, 4172);
Vector2 target = new Vector2(4749, 4157);
Vector2 delta = target - player;
double theta = Math.Atan2(delta.Y, delta.X);
double thetaDegree = RadianToDegree(theta);
// Given cartesian coordinate system
// positive y is up, negative is down
// positive x is right, negative is left
// Falsely assuming up is 0
// Falsely assuming right is 90
// Falsely assuming down is 180
// Falsely assuming left is 270
Vector2 v0 = new Vector2(0, 1);
Vector2 v45 = new Vector2(0.5f, 0.5f);
Vector2 v90 = new Vector2(0.5f, 0);
Vector2 v180 = new Vector2(0, -1);
Vector2 v270 = new Vector2(-1, 0);
double theta0 = Math.Atan2(v0.Y, v0.X);
double theta45 = Math.Atan2(v45.Y, v45.X);
double theta90 = Math.Atan2(v90.Y, v90.X);
double theta180 = Math.Atan2(v180.Y, v180.X);
double theta270 = Math.Atan2(v270.Y, v270.X);
double result0 = RadianToDegree(theta0);
double result45 = RadianToDegree(theta45);
double resultv90 = RadianToDegree(theta90);
double resultv180 = RadianToDegree(theta180);
double resultv270 = RadianToDegree(theta270);
// result 0 --> 90
// result 45 --> 45
// result 90 --> 0
// result 180 --> 270
// result 270 --> 180
}
I'm working on a simple game and I'm trying to simplify part of the 2D collision reaction in the game. When certain objects hit walls, I'm calculating a collision normal (collisionPoint - objectCenter) and reflecting based on that normal. I'm interested in rounding that normal vector to its nearest 15° but I'm not sure of a good way to go about that.
My current thought is doing something like this
float angle = atan2(normal.Y, normal.X) * Rad2Deg;
float newAngle = ((int)(angle + 7.5f) / 15) * 15.0f * Deg2Rad;
vector2 newNormal = vector2(cos(newAngle), sin(newAngle));
Is this a reasonable way to do it? Is there a better way?
Try this:
float roundAngle = 15 * Deg2Rad;
float angle = (float)Math.Atan2(normal.Y, normal.X);
Vector2 newNormal;
if (angle % roundAngle != 0)
{
float newAngle = (float)Math.Round(angle / roundAngle) * roundAngle;
newNormal = new Vector2((float)Math.Cos(newAngle), (float)Math.Sin(newAngle));
}
else
{
newNormal = Vector2.Normalize(normal);
}
You don't need to add 7.5, take this example:
// 4 degrees should round to 0
(4 + 7.5) / 15 == 11.5 / 15 == 0.77
// When this gets rounded up to 1 and multiplied by 15 again, it becomes 15 degrees.
// Don't add 7.5, and you get this:
4 / 15 == 0.27
// When rounded, it becomes 0 and, as such the correct answer
// Now how about a negative number; -12
-12 / 15 == -0.8
// Again, when rounded we get the correct number
actually this is more correct if you want the nearest 15 degree angle :
do this:
newangle% = INT(((angle%+7.5)/15)*15)
INT ALWAYS rounds DOWN by default this should properly give you the nearest angle in any case that is positive or negative have fun!!
and add the part where you use degree to rad and rad to degree if needed INSIDE the parens (like right next to angle% if that angle is not given in degrees then use some sort of rad2deg multiplier inside there
this is more like how you would do this in basic, with some modification It will work in c code or such, well good luck!!
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
Heads up: Even though this problem arose while I was working with Unity, it has nothing specific to Unity, and is more about programming logic, so please don't shy away.
I'm using Unity and rotating an object by script. The thing is, if I rotate it to, say, 180 degrees, the object does not rotate exactly to that much and tends to stop at between 179 and 181 degrees. So, to check if rotation is complete I check if the rotation angle is targetAngle +/- 1, which works.
I check using
if (transform.eulerAngles.z > lowerLimit && transform.eulerAngles.z < upperLimit)
where
lowerLimit = targetAngle-1;
upperLimit = targetAngle + 1;
Now, the problem arises when the targetAngle is 0. In this case, my script checks if rotation angle is between -1 and 1. But, -1 should really be 359, so it needs to check if the angle lies between 359 and 1.
How can I implement this?
In other words, I guess I'm asking how to implement a wrap-around number system.
EDIT
Found one work-around. If targetAngle is 0, I treat is specially. It works, but isn't the most elegant.
if (targetAngle == 0.0)
{
if ((transform.eulerAngles.z > 359.0 && transform.eulerAngles.z <= 360.0) || (transform.eulerAngles.z >= 0.0 && transform.eulerAngles.z <= 1))
{
rotate = false;
}
}
else
{
if (transform.eulerAngles.z > targetAngle - 1 && transform.eulerAngles.z < targetAngle + 1)
{
rotate = false;
}
}
You could do ...
lowerLimit = (targetAngle % 360) + 359; //360 - 1;
upperLimit = (targetAngle % 360) + 361; //360 + 1;
if (((transform.eulerAngles.z + 360) % 360) > lowerLimit
&& ((transform.eulerAngles.z + 360) % 360) < upperLimit)
This moves the check away from the zero and you wouldn't have to deal with positive/negative checking.
EDIT
The % operator on the targetAngle restricts the rotating to +/-359 degrees, so a target angle of 721 would come down to 1, and a target angle of -359 would come down to 1. This should do nicely for all cases I think.
EDIT 2
To fix the last case you mentioned in your comment, I guess you'd need to apply the same wrapping logic to your transform.eulerAngles.z values. Probably best to put this wrapping in an extra function now, so try this:
int wrapNumber(int input) // replace int with whatever your type is
{
// this will always return an angle between 0 and 360:
// the inner % 360 restricts everything to +/- 360
// +360 moves negative values to the positive range, and positive ones to > 360
// the final % 360 caps everything to 0...360
return ((input % 360) + 360) % 360;
}
lowerLimit = wrapNumber(targetAngle) + 359; //360 - 1;
upperLimit = wrapNumber(targetAngle) + 361; //360 + 1;
if (wrapNumber(transform.eulerAngles.z) + 360 > lowerLimit
&& wrapNumber(transform.eulerAngles.z) + 360 < upperLimit)
Depending on how often you need to use this, checking for some cases might remove unneeded overhead. For example, the final % 360 within wrapNumber is only needed if the input was positive. If you're calling this ten times per minute it probably won't matter. If you're calling it a hundred times per second, you may want to check how it performs in this situation.
This may be an old thread but after looking at many different snippets all trying to deal with Wrapping I found that Unity has a nice builtin function that simply takes care of business, At least in my case that the end result i required was a lerp so i only had to change it to LerpAngle and it returned a solid result.
Mathf.LerpAngle is your friend.. solved all my issues with popping etc..
http://docs.unity3d.com/ScriptReference/Mathf.LerpAngle.html