Collision Detection between player and tiles - c#

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.

Related

Calculate bounce velocity on a line

I'm trying to implement bounce physics on a ball in my game using MonoGame c#. I've googled plenty but I'm unable to understand how to do this.
The circle should be able to hit any of the red lines and bounce realistically (not just invert the velocity).
I'm using this code to detect collision:
public bool IntersectCircle(Vector2 pos, float radius, out Vector2 circleWhenHit)
{
circleWhenHit = default;
// find the closest point on the line segment to the center of the circle
var line = End - Start;
var lineLength = line.Length();
var lineNorm = (1 / lineLength) * line;
var segmentToCircle = pos - Start;
var closestPointOnSegment = Vector2.Dot(segmentToCircle, line) / lineLength;
// Special cases where the closest point happens to be the end points
Vector2 closest;
if (closestPointOnSegment < 0) closest = Start;
else if (closestPointOnSegment > lineLength) closest = End;
else closest = Start + closestPointOnSegment * lineNorm;
// Find that distance. If it is less than the radius, then we
// are within the circle
var distanceFromClosest = pos - closest;
var distanceFromClosestLength = distanceFromClosest.Length();
if (distanceFromClosestLength > radius)
return false;
// So find the distance that places the intersection point right at
// the radius. This is the center of the circle at the time of collision
// and is different than the result from Doswa
var offset = (radius - distanceFromClosestLength) * ((1 / distanceFromClosestLength) * distanceFromClosest);
circleWhenHit = pos - offset;
return true;
}
And this code when the ball wants to change position:
private void GameBall_OnPositionChange(object sender, GameBallPositionChangedEventArgs e)
{
foreach(var boundary in mapBounds)
{
if (boundary.IntersectCircle(e.TargetPosition, gameBall.Radius, out Vector2 colVector))
{
var normalizedVelocity = Vector2.Normalize(e.Velocity);
var velo = e.Velocity.Length();
var surfaceNormal = Vector2.Normalize(colVector - e.CurrentPosition);
e.Velocity = Vector2.Reflect(normalizedVelocity, surfaceNormal) * velo;
e.TargetPosition = e.CurrentPosition;
break;
}
}
}
This code gives a decent result but I'm not using my boundary positions to calculate an angle.
How do I proceed to take those into account?
EDIT:
I've removed the event based update. I've added collision between players and the ball. This is now my map-update method:
foreach (var entity in circleGameEntities)
{
for (int i = 0; i < interpolatePos; i++)
{
entity.UpdatePosition(gameTime, interpolatePos);
var intersectingBoundaries = mapBounds
.Where(b =>
{
var intersects = b.IntersectCircle(entity.Position, entity.Radius, 0f, out _);
if (intersects)
averageNormal += b.Normal;
return intersects;
}).ToList();
if (intersectingBoundaries.Count > 0)
{
averageNormal.Normalize();
var normalizedVelocity = Vector2.Normalize(entity.Velocity); // Normalisera hastigheten
var velo = entity.Velocity.Length();
entity.Velocity = Vector2.Reflect(normalizedVelocity, averageNormal) * velo * entity.Bounciness;
entity.UpdatePosition(gameTime, interpolatePos);
}
foreach (var otherEntity in circleGameEntities.Where(e => e != entity))
{
if (entity.CollidesWithCircle(otherEntity, out Vector2 d))
{
Vector2 CMVelocity = (otherEntity.Mass * otherEntity.Velocity + entity.Mass * entity.Velocity) / (otherEntity.Mass + entity.Mass);
var otherEntityNorm = otherEntity.Position - entity.Position;
otherEntityNorm.Normalize();
var entityNorm = -otherEntityNorm;
var myVelocity = entity.Velocity;
myVelocity -= CMVelocity;
myVelocity = Vector2.Reflect(myVelocity, otherEntityNorm);
myVelocity += CMVelocity;
entity.Velocity = myVelocity;
entity.UpdatePosition(gameTime, interpolatePos);
var otherEntityVelocity = otherEntity.Velocity;
otherEntityVelocity -= CMVelocity;
otherEntityVelocity = Vector2.Reflect(otherEntityVelocity, entityNorm);
otherEntityVelocity += CMVelocity;
otherEntity.Velocity = otherEntityVelocity;
otherEntity.UpdatePosition(gameTime, interpolatePos);
}
}
}
entity.UpdateDrag(gameTime);
entity.Update(gameTime);
}
This code works quite well but sometimes the objects get stuck inside the walls and eachother.
CircleGameEntity class:
class CircleGameEntity : GameEntity
{
internal float Drag { get; set; } = .9999f;
internal float Radius => Scale * (Texture.Width + Texture.Height) / 4;
internal float Bounciness { get; set; } = 1f;
internal float Mass => BaseMass * Scale;
internal float BaseMass { get; set; }
internal Vector2 Velocity { get; set; }
internal float MaxVelocity { get; set; } = 10;
internal void UpdatePosition(GameTime gameTime, int interpolate)
{
var velocity = Velocity;
if (velocity.X < 0 && velocity.X < -MaxVelocity)
velocity.X = -MaxVelocity;
else if (velocity.X > 0 && velocity.X > MaxVelocity)
velocity.X = MaxVelocity;
if (velocity.Y < 0 && velocity.Y < -MaxVelocity)
velocity.Y = -MaxVelocity;
else if (velocity.Y > 0 && velocity.Y > MaxVelocity)
velocity.Y = MaxVelocity;
Velocity = velocity;
Position += Velocity / interpolate;
}
internal void UpdateDrag(GameTime gameTime)
{
Velocity *= Drag;
}
internal bool CollidesWithCircle(CircleGameEntity otherCircle, out Vector2 depth)
{
var a = Position;
var b = otherCircle.Position;
depth = Vector2.Zero;
float distance = Vector2.Distance(a, b);
if (Radius + otherCircle.Radius > distance)
{
float result = (Radius + otherCircle.Radius) - distance;
depth.X = (float)Math.Cos(result);
depth.Y = (float)Math.Sin(result);
}
return depth != Vector2.Zero;
}
}
The surfaceNormal is not the boundary normal, but the angle between the collision point and the center of the circle. This vector takes the roundness of the ball into account and is the negative of the ball's direction(if normalized) as if it hit the surface head-on, which is not needed unless the other surface is curved.
In the Boundary class calculate the angle and one of the normals in the constructor and store them as public readonly:
public readonly Vector2 Angle; // replaces lineNorm for disabiguity
public readonly Vector2 Normal;
public readonly Vector2 Length;
public Boundary(... , bool inside) // inside determines which normal faces the center
{
// ... existing constructor code
var line = End - Start;
Length = line.Length();
Angle = (1 / Length) * line;
Normal = new Vector2(-Angle.Y,Angle.X);
if (inside) Normal *= -1;
}
public bool IntersectCircle(Vector2 pos, float radius)
{
// find the closest point on the line segment to the center of the circle
var segmentToCircle = pos - Start;
var closestPointOnSegment = Vector2.Dot(segmentToCircle, End - Start) / Length;
// Special cases where the closest point happens to be the end points
Vector2 closest;
if (closestPointOnSegment < 0) closest = Start;
else if (closestPointOnSegment > Length) closest = End;
else closest = Start + closestPointOnSegment * Angle;
// Find that distance. If it is less than the radius, then we
// are within the circle
var distanceFromClosest = pos - closest;
return (distanceFromClosest.LengthSquared() > radius * radius); //the multiply is faster than square root
}
The change position code subset:
// ...
var normalizedVelocity = Vector2.Normalize(e.Velocity);
var velo = e.Velocity.Length();
e.Velocity = Vector2.Reflect(normalizedVelocity, boundary.Normal) * velo;
//Depending on timing and movement code, you may need add the next line to resolve the collision during the current step.
e.CurrentPosition += e.Velocity;
//...
This updated code assumes a single-sided non-moving boundary line as prescribed by the inside variable.
I am not a big fan of C# events in games, since it adds layers of delay (both, internally to C# and during proper use the cast of sender.)
I would be remiss not to mention your abuse of the e variable. e should always be treated as a value type: i.e. read-only. The sender variable should be cast(slow) and used for writing purposes.

Applying direction into current transform position

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.

Bounding the camera to the map

I have a 2D game in unity, and I have a camera that follow my main character.
Currently, when I, for example, move the character to the left, I can see what's beyond the map, like so:
Say the map size is 15X15. I have a script generating 15X15 blocks of size 1X1, meaning the map bounds are (-1, -1) -> (15, 15).
There are two things I want to accomplish:
1) Have the camera not go out of the map bounds
2) When the map size is SMALLER than the camera, have the camera change its size.
Example for point 2)
The map is 5 columns and 15 rows, thus it's very narrow compared to the camera, like so:
or even 10 columns and 3 rows, like so:
I would want the size of the camera to change so it won't be neither wider or taller than the map.
How do I do those two things?
This is the github to the current scripts in the game. The CameraController script is where the additions are supposed to be
The orthographic size defines half of the vertical size, the horizontal size is based on aspect ratio.
From there you can define what should be the biggest value when centered.
The script below should get you going. As long as your level are rectangle (that obviously includes square), it will go fine:
public float speed = 2f;
public Transform min;
public Transform max;
private float aspect;
private float maxSize;
private void Start ()
{
this.aspect = Camera.main.aspect;
this.maxSize = max.position.x <= max.position.y ? max.position.x /2f / this.aspect :max.position.y / 2f;
}
private void Update ()
{
float size = Input.GetAxis ("Mouse ScrollWheel");
Camera.main.orthographicSize += size;
if (Camera.main.orthographicSize > maxSize)
{
Camera.main.orthographicSize = maxSize;
}
float x = Input.GetAxis ("Horizontal");
float y = Input.GetAxis ("Vertical");
Vector3 position = this.transform.position;
position.x += x * Time.deltaTime * this.speed;
position.y += y * Time.deltaTime * this.speed;
float orthSize = Camera.main.orthographicSize;
if (position.x < (this.min.position.x + orthSize * this.aspect))
{
position.x = this.min.position.x + orthSize * this.aspect;
}
else if (position.x > (this.max.position.x - orthSize * this.aspect))
{
position.x = this.max.position.x - orthSize * this.aspect;
}
if (position.y < (this.min.position.y + orthSize))
{
position.y = this.min.position.y + orthSize;
}
else if(position.y > (this.max.position.y - orthSize))
{
position.y = this.max.position.y - orthSize;
}
this.transform.position = position;
}
the idea is that you have two empty game objects place at bottom left and upper right. Bottom left is dragged into min and upper right goes int max. The speed variable is just how fast the camera translates. This is attached to the camera object. There is no limit for min size of camera, but you can do it.
The point is just that you get this going for your own project as this is more generic.
The rest is just considering the camera position, current size and aspect ratio (you cannot change that at runtime or you'd have to modify the code).
EDIT:
If you wish to bound the camera zoom to the movement,remove the first lines about size and add the following after you get the horizontal/vertical movement:
if (x != 0f || y != 0f) {
Camera.main.orthographicSize = Mathf.MoveTowards (Camera.main.orthographicSize, largeSize, Time.deltaTime);
} else {
Camera.main.orthographicSize = Mathf.MoveTowards (Camera.main.orthographicSize, size, Time.deltaTime);
}
Think of declaring the size, largeSize variables and set them to your need.

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

Enemy following player in XNA and stop within 20 pixels

I'm trying to get my enemies to follow the player and stop within 20 pixels, I have tried a number of algorithms including the Vector2.Lerp(); method to try and fix this but it keeps breaking the build. Any help would be greatly appreciated. The code is below.
public void Update(GameTime gameTime)
{
if (this.IsAlive)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
double distanceToPlayer = Math.Sqrt(Math.Pow(Level.Player.Position.X - this.Position.X, 2) + Math.Pow(Level.Player.Position.Y - this.Position.Y, 2));
// Calculate tile position based on the side we are walking towards.
float posX = Position.X + localBounds.Width / 2 * (int)direction;
int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
int tileY = (int)Math.Floor(Position.Y / Tile.Height);
if (waitTime > 0)
{
// Wait for some amount of time.
waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
if (waitTime <= 0.0f)
{
// Then turn around.
direction = (FaceDirection)(-(int)direction);
}
}
else
{
// If we are about to run into a wall or off a cliff, start waiting.
if (Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Impassable || Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Passable) //is the enemy is close and is not attacking, attack and turn!
{
waitTime = MaxWaitTime;
}
else
{
// Move in the current direction.
Vector2 velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
position = position + velocity;
}
}
dtAttack += gameTime.ElapsedGameTime;
AttackPlayer();
}
else
{
dt += gameTime.ElapsedGameTime;
if (dt.TotalSeconds > (sprite.Animation.FrameCount * sprite.Animation.FrameTime))
this.Remove = true;
}
}
Does it have to be 20 pixels on the screen? Seems strange.
You could try to calculate the euclidean distance between the player and the enemy, using the Vector2.Distance method. If the distance is 20 or lower, stop the enemy. If not, keep following the player.

Categories

Resources