Applying direction into current transform position - c#

I'm not quite sure whether I pick correct title for the question, feel free to edit it If you find it misleading.
I'm developing an action game which involving music as the core of its gameplay.
It contains a set of entities (game objects) that move into several directions; top, right, left, bottom and diagonal moves (e.g: top-left, top-right, bottom-left, etc etc).
To determine the position of these entities, there is a value which need to be calculated that involving the music tempo and several properties of the entities itself, which I able produce it in Unity.
I use following function to determine the position of transform of the entities:
private Vector3 _position;
private void DeterminePosition(float offset)
{
// In actual code, the _position is initialized under Start() method
// But for this simplification sake, I'll just put it here
_position = _position == null ? new Vector3(0, 0, 1f) : _position;
if (Direction == Direction.Up || Direction == Direction.RightUp || Direction == Direction.LeftUp)
{
_position.y = offset;
}
if (Direction == Direction.Down || Direction == Direction.RightDown || Direction == Direction.LeftDown)
{
_position.y = -offset;
}
if (Direction == Direction.Left || Direction == Direction.LeftDown || Direction == Direction.LeftUp)
{
_position.x = -offset;
}
if (Direction == Direction.Right || Direction == Direction.RightDown || Direction == Direction.RightUp)
{
_position.x = offset;
}
transform.position = _position;
}
Where the offset is the value that I was talking about before.
The code works perfectly as intended, however the game need to be change.
Instead of fixed direction (e.g: Up, Right, Left, Bottom, RightUp, DownLeft etc etc) we decide to use value of degree for the direction (0 to 360 degree).
And now I've no idea how to impement this. I've tried to use following codes, but it doesn't work:
_position = new Vector3(offset, offset, transform.position.z);
// Where the direction is between 0 .. 360
transform.position = Quaternion.Euler(0, direction, 0) * _position;
Can anybody else come up with solution?
Thanks in advance!

If offset represents the distance from the new position to (0, 0, transform.position.z) (calling that the origin since only x and y positions seem to change), and direction is the angle measured counterclockwise in degrees between the positive x-axis and the vector from the origin to the the new position, you can get the new x and y positions using offset and sin and cos of the direction:
_position.x = offset * Mathf.Cos(Mathf.Deg2Rad * direction);
_position.y = offset * Mathf.Sin(Mathf.Deg2Rad * direction);
_position.z = transform.position.z; //assuming the z position stays the same
transform.position = _position;
Edit
To account for scaling, I think you will need to replace offset depending on the direction.
float multiplier = 0f;
if(0 <= direction && direction <= 45)
{
multiplier = Mathf.Abs(offset / Mathf.Cos(Mathf.Deg2Rad * direction));
}
else if(45 < direction && direction <= direction <= 135)
{
multiplier = Mathf.Abs(offset / Mathf.Sin(Mathf.Deg2Rad * direction));
}
else if(135 < direction && direction <=225)
{
multiplier = Mathf.Abs(offset / Mathf.Cos(Mathf.Deg2Rad * direction));
}
else if(225 < direction && direction <= 315)
{
multiplier = Mathf.Abs(offset / Mathf.Sin(Mathf.Deg2Rad * direction));
}
else if(315 < direction && direction <= 360)
{
multiplier = Mathf.Abs(offset / Mathf.Cos(Mathf.Deg2Rad * direction));
}
_position.x = multiplier * Mathf.Cos(Mathf.Deg2Rad * direction);
_position.y = multiplier * Mathf.Sin(Mathf.Deg2Rad * direction);
_position.z = transform.position.z;
transform.position = _position;
This will break the coordinate plane into 4 90-degree sections (if you combine 315 < direction <= 360 and 0 <= direction < 45) where you are using sin or cos of direction to create a multiplier that should account for the regions where offset needs to be scaled depending on x and y components of the displacement.

Related

Unity 3D How to project a Vector3 onto a plane or get angle between two Vector3s in a specific plane?

I am trying to program a third person character such that when a direction key, eg. D is pressed, if the character is currently facing same direction as camera, Right Quarter Turn animation is played, if it is facing the camera, Left Quarter Turn is played, similar to GTA V. But I am having trouble getting angles between camera and player in the Y plane. I tried this into the player control script:
void Right()
{
Vector3 pVec = Vector3.ProjectOnPlane(transform.forward, Vector3.up);
Vector3 cVec = Vector3.ProjectOnPlane(mainCam.transform.forward, Vector3.up);
print(cVec);
float angle = Vector3.Angle(pVec, cVec);
print(angle);
if(angle >= 345 && angle <= 15)
{
animator.Play("StandQuarterTurnRight");
}
else if(angle >= 255 && angle <= 285)
{
animator.Play("StandHalfTurnRight");
}
else if(angle >= 165 && angle <= 195)
{
animator.Play("StandQuarterTurnLeft");
}
else if(angle >=75 && angle <= 105)
{
float forw = Input.GetAxis("Horizontal");
if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
animator.SetFloat("Speed", forw);
}
}
But it is not working, I get wrong angles. I is giving me 90 when character faces right or left of camera forward, and wrong angles for others and it doesn't give 180+ at all, what am I doing wrong? Is there a better way to achieve what I am trying?
I would transform the viewdirection of the camera into the coordinate system of your character. Then it is very easy so see where you want him to turn or move.
Assuming your camera's rotation is at mainCam.transform.rotation, you can use this code:
float target_angle = 90.0f; // assuming want to turn 'right'.
// direction of camera, in worldspace.
Vector3 cam_dir = mainCam.transform.forward;
// now transform to a direction vector in character's local space.
cam_dir = transform.InverseTransformDirection(cam_dir);
// ignore y part, take X/Z to get the angle.
// 0 degrees is forward, 90 deg is toward positive X, so normally right.
float cam_angle = Mathf.Atan2(cam_dir.x,cam_dir.z)*Mathf.Rad2Deg;
// angle we need to turn
float turn_angle = target_angle - cam_angle;
// [.....] do it now.
Using the InverseTransformXxx() function is often very helpful if trying to think in some character's local view. You could also transform the camera's position into the character's space by using char.transform.InverseTransformPoint(mainCam.transform.position) and use that for some reference.
Here is a small fix to your code, which adjusts your result to work, without changing anything else:
void Right()
{
Vector3 pVec = Vector3.ProjectOnPlane(transform.forward, Vector3.up);
Vector3 cVec = Vector3.ProjectOnPlane(mainCam.transform.forward, Vector3.up);
print(cVec);
float angleA = Vector3.Angle(pVec, cVec); //Get the angle between the 2 vectors, projected on Y-plane
float perspectiveAngle = Vector3.Angle(transform.right, cVec); //Get the angle between camera and player's right vector
float angle = angleA; //In case angle is below 180, angle is AngleA
if (perspectiveAngle > 90f) //If angle between player's right vector and camera is > 90, then we need to adjust the angle, as it is equal to or greater than 180
angle = 360f - angleA;
print(angle);
if (angle >= 345 && angle <= 15)
{
animator.Play("StandQuarterTurnRight");
}
else if (angle >= 255 && angle <= 285)
{
animator.Play("StandHalfTurnRight");
}
else if (angle >= 165 && angle <= 195)
{
animator.Play("StandQuarterTurnLeft");
}
else if (angle >= 75 && angle <= 105)
{
float forw = Input.GetAxis("Horizontal");
if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
animator.SetFloat("Speed", forw);
}
}
See the comments above, which sort of describe how it works. Hopefully that helps
I would advise against using angles here as much as possible. Instead, You can use dot products between the camera's right and the player's local directions to determine which local direction is most in-line with the camera's right direction. Explanation in comments.
void Right()
{
float dotThreshold = Mathf.Sin(Mathf.PI * 0.25f);
// Take the dot products between the camera's right and
// each direction from the player.
// Either exactly one dot product will exceed this threshold
// (sin 45 degrees) or two will equal it.
// Either way, when we see one dot product >= the threshold,
// we know what direction we should face.
Vector3 camRight = mainCam.transform.right;
if(Vector3.Dot(camRight, transform.right) >= dotThreshold)
{
// camera's right ~ player's right
animator.Play("StandQuarterTurnRight");
}
else if(Vector3.Dot(camRight, -transform.forward) >= dotThreshold)
{
// camera's right ~ player's back
animator.Play("StandHalfTurnRight");
}
else if(Vector3.Dot(camRight, -transform.right) >= dotThreshold)
{
// camera's right ~ player's left
animator.Play("StandQuarterTurnLeft");
}
else
{
// camera's right ~ player's forward
float forw = Input.GetAxis("Horizontal");
if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
animator.SetFloat("Speed", forw);
}
}
If you can't assume that the player and the camera have the same y axis direction, you must project into the same plane as done in the question:
void Right()
{
float dotThreshold = Mathf.Sin(Mathf.PI * 0.25f);
// Take the dot products between the camera's right and
// each direction from the player.
// Either exactly one dot product will exceed this threshold
// (sin 45 degrees) or two will equal it.
// Either way, when we see one dot product >= the threshold,
// we know what direction we should face.
Vector3 camRight = Vector3.ProjectOnPlane(mainCam.transform.right, Vector3.up);
Vector3 playerRight = Vector3.ProjectOnPlane(transform.right, Vector3.up);
Vector3 playerForward = Vector3.ProjectOnPlane(transform.forward, Vector3.up);
if(Vector3.Dot(camRight, playerRight) >= dotThreshold)
{
// camera's right ~ player's right
animator.Play("StandQuarterTurnRight");
}
else if(Vector3.Dot(camRight, -playerForward) >= dotThreshold)
{
// camera's right ~ player's back
animator.Play("StandHalfTurnRight");
}
else if(Vector3.Dot(camRight, -playerRight) >= dotThreshold)
{
// camera's right ~ player's left
animator.Play("StandQuarterTurnLeft");
}
else
{
// camera's right ~ player's forward
float forw = Input.GetAxis("Horizontal");
if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
animator.SetFloat("Speed", forw);
}
}

XNA Negative Rotation?

I'm programming a starship-game and have just finished the part which manages the rotating. I used a simple if/else-statement to check if the ship must rotate positive or negative in order to face the target as fast as possible. But I saw that the Rotation-Value can get negative, and then the ship does rotate to the wrong direction (It still faces the target-point in the end, but it takes longer). Please tell me what I did wrong :(
The function:
public bool RotateOrMove(Vector2 position)
{
if (IsRotationg == null) //todo
{
Vector2 direction = Position - position;
direction.Normalize();
FinalRotation = (float)Math.Atan2(-direction.X, direction.Y);
IsRotationg = true;
}
if (Equals(FinalRotation, Rotation))
IsRotationg = false;
if (IsRotationg == false)
{
Position = position;
return true;
}
else
{
if (FinalRotation >= Rotation)
{
Rotation += RotationVelocity;
if (FinalRotation - Rotation < RotationVelocity)
{
Rotation = FinalRotation;
IsRotationg = false;
}
}
if (FinalRotation < Rotation)
{
Rotation -= RotationVelocity;
if (FinalRotation - Rotation > -RotationVelocity)
{
Rotation = FinalRotation;
IsRotationg = false;
}
}
return false;
}
}
The Player-Class owns the Ship. When the player press the right mouse-button, this method will be called once per frame until the ship reaches the position where the cursor is pointing at.
if (!Ship.RotateOrMove(Position))
Position -= Velocity;
So if the ship had to rotate and couldn't move, it will remove the velocity it added just before to ensure that the ship won't move.
Hope you understand my problem^^
Math.Atan2 return values from -pi to pi.
to get a smooth rotation you can use this code got from here
private float CurveAngle(float from, float to, float step)
{
if (step == 0) return from;
if (from == to || step == 1) return to;
Vector2 fromVector = new Vector2((float)Math.Cos(from), (float)Math.Sin(from));
Vector2 toVector = new Vector2((float)Math.Cos(to), (float)Math.Sin(to));
Vector2 currentVector = Slerp(fromVector, toVector, step);
return (float)Math.Atan2(currentVector.Y, currentVector.X);
}
private Vector2 Slerp(Vector2 from, Vector2 to, float step)
{
if (step == 0) return from;
if (from == to || step == 1) return to;
double theta = Math.Acos(Vector2.Dot(from, to));
if (theta == 0) return to;
double sinTheta = Math.Sin(theta);
return (float)(Math.Sin((1 - step) * theta) / sinTheta) * from + (float)(Math.Sin(step * theta) / sinTheta) * to;
}

Rotation over time in 3D

Im working with Xna 4, doing a game where i have a game object (spaceship) that moves in a 3D world on the Y axis = 0 plane.. Aka 2.5D..
Until now i used a very complex angle calculation to create a rotation between 2 vectors, yet that algorithm lacks the ability to take into account that the object already is rotated. so the results get funkey..
Therefore i was hoping that someone, could show me a smart and easily implementable way to use Matrices and vector math, to do such a rotation over time thingy.
What i noticed in previous searches, is that people have the following variables in their object classes:
- Position vector3
- right vector3
- up vector3
- Rotation matrix
- transformMatrix matrix
- velocity vector3
- etc..
often i ask myself why its needed to have that many variables for a simple current position.. or maybe im not understanding.. anyways..
I have the position, rotation and transformsMatrix currently, and would like to know what else i need and HOW to calculate it, and then how you would implement JUST the rotation over time.
The method that is called by my right-click movement command trig sends a vector3 position on the Y = 0 plane of where the click happened.
public void MoveCommand(Vector3 pos){ }
ive tested this, and the "pos" given is accurate. Any help will be apreciated ..
You should check the Matrix.CreateRotationX Y or Z acording to the rotation that you want.
X,Y or Z is the axis of the rotation,
If you choose Y you will see a "2D" rotation (yaw) because that is the axis that you are using as depth.
If you choose X or Z axis you will see "3D" rotations (pitch and roll)
The code should look like this:
WorldMatrix = Rotations * Translation
where
Rotations = Matrix.CreateRotationX(angleRadians)
and
Translation = Matrix.CreateTranslation(Position);
The world matrix is the matrix that is affecting your model, the view and projection depends on the camera
Now if you want to know the angle between vectors you should check the dot product or the atan2 function because you are in 2D
Vector3 Position;
float Rotation;
Matrix World
{
get
{
return Matrix.CreateRotationZ(Rotation) * Matrix.CreateTranslation(Position);
}
}
public void RotateInstantly(Vector3 position)
{
Rotation = Math.Atan2(Position.Y - position.Y, Position.x - position.x);
}
public void RotateIncremently(Vector3 position, float maxStep)
{
float targetRotation = Math.Atan2(Position.Y - position.Y, Position.x - position.x);
float diff = targetRotation - Rotation;
if (Math.Abs(diff) > maxStep)
{
if (targetRotation > Rotation)
Rotation += maxStep;
else
Rotation -= maxStep;
}
else
Rotation = targetRotation;
}
You can use the RotateIncremently like this:*
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
float maxRotationVelocity = Math.TwoPi; //2* Pi is one revolution.
RotateIncremently(target.Position, maxRotationVelocity * dt);
Thanks to Stig-Rune SkansgÄrd's reply (if your danish hi5), i fixed my old Angle calculation and got it to work in every case.. So i thought i would answer my own question with the solution that works for me, so that future visitors can benefit of it.. This is a snippet of a very large Ship class + a helper method to calculate the angle:
public static float CalculateAngleFromVectors(Vector3 v1, Vector3 v2) {
float x = 0;
float X = v2.X - v1.X,
Z = v2.Z - v1.Z;
if (Z == 0) {
x = (X < 0 ? 90 : 270);
} else if (X == 0) {
x = (Z < 0 ? 180 : -180);
} else {
float temp = MathHelper.ToDegrees((float)Math.Atan(X / Z));
if (X < 0) {
x = (Z < 0 ? Math.Abs(temp) : 180 - Math.Abs(temp));
} else {
x = (Z < 0 ? 360 - Math.Abs(temp) : Math.Abs(temp) + 180);
}
}
return x;
}
this method gets you the float angle (in degrees) to rotate your ship (from the standard 0 degrees starting point)..
to use it, simply make some update / animation method in your class that does something like this:
float desiredRotation = 0, currentRotation = 0, totalElapsed = 0, timePerFrame = 0.05f;
if (desiredRotation != 0) {
totalElapsed += elapsed;
if (totalElapsed > timePerFrame) {
if (isRotationComplete()) {
rotX += MathHelper.ToRadians(desiredRotation);
currentRotation = desiredRotation = 0;
} else if (desiredRotation > currentRotation) {
currentRotation += shipTurnSpeed;
} else if (desiredRotation < currentRotation) {
currentRotation -= shipTurnSpeed;
}
totalElapsed -= timePerFrame;
}
}
EDIT: and the completion check:
private bool isRotationComplete() {
bool b = false;
if (desiredRotation > currentRotation && currentRotation + shipTurnSpeed > desiredRotation) {
b = true;
} else if (desiredRotation < currentRotation && currentRotation - shipTurnSpeed < desiredRotation) {
b = true;
}
return b;
}
essentially what this does is to always check wether or not DesiredRotation is bigger than 0.. if it is, then that means the player has given the command to rotate (or the AI).. CurrentRotation in my example is ofc how much has been rotated since a rotation command was last given, and is set to 0 once the rotation is complete.. You should have a Rotations matrix that uses a different variable to display the rotation with.. mine is this:
public float rotX { get; set; }
public float rotY { get; set; }
public Vector3 position { get; set; }
public Matrix Transform {
get {
return (Matrix.Identity *
Matrix.CreateScale(scale) *
Matrix.CreateRotationY(MathHelper.Pi) *
Rotation *
Matrix.CreateTranslation(position));
}
}
public float ShipCurRotation { get { return (rotX + MathHelper.ToRadians(currentRotation)); } }
public Matrix Rotation { get { return (Matrix.CreateRotationY(ShipCurRotation) * Matrix.CreateRotationX(rotY)); } }
The rotX variable is set in my animation when the rotation is complete, and also at Init.. And here is how i use the rotation angle that my first code snippet generates for me:
public void MoveToPosition(Vector3 pos) {
desiredRotation = (CalculateAngleFromVectors(position, pos) - MathHelper.ToDegrees(rotX));
isMoving = true;
}
.. this makes a smooth customizable rotation, transforms and movement setup.. In a XZ plane ofc.. the Y axis is UP and always 0..
Feel free to comment on this, if you have suggestions or changes or ideas to make things even better.. Im always open for improvements.. Thanks for the replies, and hope this helps alot of new developers, took me forever to gather this stuff from the web..
PS. the rotation can be applied directly to rotX for an instant rotation, bypassing the animation and turnspeed..
WithRegards
MatriXz

Collision Detection between player and tiles

I am trying to implement basic (for now) collision detection into my platformer. I have tiles that are each 16 x 16 in size. The character is 32 x 32 pixels in size and has its own bounding box. Now, in my Tile class, I have a bool, isSolid. Each of these tiles in my array also have a rect for their respective bounding boxes.
I am checking to see if there's an intersection between the player and tiles by doing:
if (player.GetBoundingBox().Intersects(map.tiles[(int)player.position.Y / 16,
(int)player.position.X / 16].bounds) && map.tiles[(int)player.position.Y / 16,
(int)player.position.X / 16].isSolid)
{
...
}
Now, my problem is that this is extremely inaccurate as I'm rounding off the position. I'm tired as heck right now and for the life of me I can't figure out how to properly do this. What is the best way to approach this issue?
Well this might not be exactly "basic", It works very nicely and dosen't have any problems because it calculates the X axis and Y axis seperatley, this collision structure will help you later on. (I switched to this from the old Platformer Starter kit code, which was very glitchy)
Assuming you already have methods for gravity, lets get started.
This should be after your falling and velocity logic, It will see what axises need to be checked.
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; //If you havent already, get the elapsed time
if (velocity.X != 0f)
{
Position += velocity.X * Vector2.UnitX * elapsed;
HandleCollisions(CollisionDirection.Horizontal);
}
if (velocity.Y != 0f)
{
Position += velocity.Y * Vector2.UnitY * elapsed;
HandleCollisions(CollisionDirection.Vertical);
}
Now for the very important HandleCollisons method
private void HandleCollisions(CollisionDirection direction)
{
// Get the player's bounding rectangle and find neighboring tiles.
Rectangle bounds = player.GetBoundingBox();
int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;
// Reset flag to search for ground collision.
isOnGround = false;
// For each potentially colliding tile,
for (int y = topTile; y <= bottomTile; ++y)
{
for (int x = leftTile; x <= rightTile; ++x)
{
Rectangle tileBounds = Level.GetBounds(x, y);
// If this tile is collidable,
bool IsSolid = map.tiles[x,y].IsSolid;
Vector2 depth;
if (isSolid && TileIntersectsPlayer(BoundingRectangle, tileBounds, direction, out depth))
{
if ((collision == ItemCollision.Platform && movement.Y > 0))
continue;
isOnGround = true;
if (isSolid || isOnGround)
{
if (direction == CollisionDirection.Horizontal)
{
position.X += depth.X;
}
else
{
isOnGround = true;
position.Y += depth.Y;
}
}
}
}
}
// Save the new bounds bottom.
previousBottom = bounds.Bottom;
}
public static bool TileIntersectsPlayer(Rectangle player, Rectangle block, CollisionDirection direction, out Vector2 depth)
{
depth = direction == CollisionDirection.Vertical ? new Vector2(0, player.GetVerticalIntersectionDepth(block)) : new Vector2(player.GetHorizontalIntersectionDepth(block), 0);
return depth.Y != 0 || depth.X != 0;
}
Thats it for that! It will detect collisons, but we need to allow it to figure out how much to push the player back up once it collides! You will need these two extension methods.
public static float GetHorizontalIntersectionDepth(this Rectangle rectA, Rectangle rectB)
{
// Calculate half sizes.
float halfWidthA = rectA.Width / 2.0f;
float halfWidthB = rectB.Width / 2.0f;
// Calculate centers.
float centerA = rectA.Left + halfWidthA;
float centerB = rectB.Left + halfWidthB;
// Calculate current and minimum-non-intersecting distances between centers.
float distanceX = centerA - centerB;
float minDistanceX = halfWidthA + halfWidthB;
// If we are not intersecting at all, return (0, 0).
if (Math.Abs(distanceX) >= minDistanceX)
return 0f;
// Calculate and return intersection depths.
return distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
}
public static float GetVerticalIntersectionDepth(this Rectangle rectA, Rectangle rectB)
{
// Calculate half sizes.
float halfHeightA = rectA.Height / 2.0f;
float halfHeightB = rectB.Height / 2.0f;
// Calculate centers.
float centerA = rectA.Top + halfHeightA;
float centerB = rectB.Top + halfHeightB;
// Calculate current and minimum-non-intersecting distances between centers.
float distanceY = centerA - centerB;
float minDistanceY = halfHeightA + halfHeightB;
// If we are not intersecting at all, return (0, 0).
if (Math.Abs(distanceY) >= minDistanceY)
return 0f;
// Calculate and return intersection depths.
return distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
}
Note you may need to modify this a bit, as the players position is the BOTTOM left. Also a collision enum is needed, for vertical and horizontal. Please tell me if anything seems missing in this.

How to gradually rotate an object to face another turning the shortest distance

I'm currently trying to rotate a sprite depending on how many degrees(or rads, I prefer degrees) it differs from facing straight towards a target, the problem is when the target reaches a certain position the sprites decides to do rotate a full 360 to other way instead of doing the 10 extra. This picture probably explains the problem better:
Blue square = target
Red square = the object
Green line = rotation it wants
Black line = current rotation
Brown arrow = how it rotates to achieve this
Red arrow = how I want it to rotate.
Note that Case 1 always work, depending what way it rotates, but Case 2 it always does that rotation, no matter if it is to the "right" or "left" of the current rotation.
This is the code I'm using to rotate the object.
Vector2 distance = new Vector2(target.worldPos.X - this.worldPos.X, target.worldPos.Y - this.worldPos.Y);
float wantRot = (float)Math.Atan2(distance.Y, distance.X);
if (this.rotation < wantRot)
this.rotation += MathHelper.ToRadians(45) * Time.deltaTime;
if (this.rotation > wantRot)
this.rotation -= MathHelper.ToRadians(45) * Time.deltaTime;
What i want to achieve is have it rotate (in Case 2) according to the red arrow instead of the brown one.
NOTE: I'm not an expert at programming, I've only done it from time to time for the past year(Mainly simple 2D Shooter/shoot'em up kind of games), so in depth explanation would be appreciated. I'm also a student learning programming.
PS: Suggestions for Title would also be appreciated as I had absolutely no idea what to put there.
Your problem is that the target could be at angle 5, and the object could be facing 355 degrees (for example). According to your test, 5 is less than 355, so go anticlockwise.
What you should do is test whether the target is within 180 degrees to your left, or within 180 degrees to your right, then move accordingly.
The tricky part is getting the check to 'wrap' around 360 <-> 0. It looks like 0 degrees is left in your case, so the hard test is for when the wantRot is on the side that has 0 degrees within it.
To visualise draw a circle as below, then place your object on the left of where we're facing. You'll see that you have to check the 2 shaded areas separately.
Method 1
Check all cases separately.
Note: Code below is in my head and untested. You'll need to change degrees to radians.
int MoveDir = 0;
var BehindMe = this.rotation - 180;
if (BehindMe < 0)
BehindMe += 360;
if (wantRot != this.rotation)
{
if (wantRot == BehindMe)
MoveDir = 1; // or randomly choose
else if ((wantRot > BehindMe && wantRot < this.rotation) ||
(this.rotation < 180 && (wantRot > BehindMe ||
wantRot < this.rotation)))
MoveDir = -1;
else if ((wantRot < BehindMe && wantRot > this.rotation) ||
(this.rotation > 180 && (wantRot < BehindMe ||
wantRot > this.rotation))
MoveDir= 1;
this.rotation += MoveDir * MathHelper.ToRadians(45) * Time.deltaTime;
}
Method 2
From looking at the image, you may realise that you could just check whether the object on the right, then if not, assume it's on the left (since as long as the current angle is less than 180 degrees checking its on the right is easy). If the current angle is more than 180 degrees, then reverse the concept - check whether it's on the left and if not assume right. Something like below:
int MoveDir = 0;
var BehindMe = this.rotation - 180;
if (BehindMe < 0)
BehindMe += 360;
if (wantRot != this.rotation)
{
if (this.rotation <= 180)
{
if (wantRot > this.rotation && wanrRot < BehindMe)
MoveDir = 1;
else
MoveDir = -1;
}
else
{
if (wantRot < this.rotation && wanrRot > BehindMe)
MoveDir = -1;
else
MoveDir = 1;
}
this.rotation += MoveDir * MathHelper.ToRadians(45) * Time.deltaTime;
}

Categories

Resources