Rotation over time in 3D - c#

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

Related

Trying to create enter the gungeon type weapons however stuck on making weapons rotate to face mouse

Im making a game similar to Enter The Gungeon and am working in a similar space to them in unity. I'm using unity 3D and making a 2.5D game to give it the same perspective as gungeon. However I'm trying to make it so the weapon will face the mouse and its not working. Ive tried multiple tutorials and none of them have worked.
The issue is that it changes the rotation of all other axes to 0 when the game is in a top down perspective so I rotated the X of the quad to 90. From my experimenting I need the Y axis to follow the mouse. Unless there is a better way to go about this how can I do this with my current setup?
here's the code ive been using that somewhat worked but it resets all axes but Z.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MouseFollow : MonoBehaviour
{
private void Start()
{
}
private void Update()
{
var dir = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
}
The simplest solution I can think of is to create an empty object and make it the parent of the gun model. This will ensure that the gun (child GameObject) can keep its own rotation without resetting it while the parent GameObject can be rotated on the y axis using the method you described above.
If this is not an option, you could create another Quaternion for all of the rotations that you want to keep, which are independent of the mouse direction. Then, for each frame, multiply the direction you generate by it to receive the rotation you want.
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward) * thePersistingQuaternion
I created this function once while trying to accomplish something similar to this.
The function takes in 2 Vector3 points and returns the Quaternion required for the object to 'Look At' the other point.
p1 is the origin point.
p2 is the point you want to look at.
For your solution you could use the transform of the gun object as the origin point, and use your mouse position as the point you want to look at.
public static Quaternion LookAt(Vector3 p1, Vector3 p2)
{
float angle = 0;
float opp;
float adj;
if (p1.x == p2.x)
{
//Looking down
if (p1.y > p2.y)
{
angle = 0;
}
//Looking up
else
{
angle = 180;
}
}
else if (p1.y == p2.y)
{
//Looking right
if (p1.x < p2.x)
{
angle = 90;
}
//Looking left
else
{
angle = 270;
}
}
else
{
if (p1.x < p2.x)
{
//First quadrant angle +0
if (p1.y > p2.y)
{
angle = 0;
opp = Mathf.Abs(p1.x - p2.x);
adj = Mathf.Abs(p1.y - p2.y);
}
//Second quadrant angle +90
else
{
angle = 90;
adj = Mathf.Abs(p1.x - p2.x);
opp = Mathf.Abs(p1.y - p2.y);
}
}
//if (p1.x > p2.x)
else
{
//Third quadrant angle +180
if (p1.y <= p2.y)
{
angle = 180;
opp = Mathf.Abs(p1.x - p2.x);
adj = Mathf.Abs(p1.y - p2.y);
}
//Forth quadrant angle +270
else
{
angle = 270;
adj = Mathf.Abs(p1.x - p2.x);
opp = Mathf.Abs(p1.y - p2.y);
}
}
float a = Mathf.Atan(opp / adj) * 180f / Mathf.PI;
angle += a;
}
Quaternion rotation = Quaternion.Euler(0, 0, angle);
return rotation;
}

Strange outputs for a moving platform in Unity

First off, sorry it this isn't written very well, I've spend hours debugging this and I'm very stressed. I'm trying to make a moving platform in unity that can move between way-points, I don't want to have to have tons of gameobjects in the world taking up valuable processing power though so I'm trying to use something I can just add to the script through the editor.
The only problem is that it seems to be doing this at an incredible speed:
Black = The Camera View, Blue = The platform and where it should be going based on waypoints, Red = What it is currently doing.
I've spend hours trying to find a fix but I have no idea why it's doing this.
My Script on the Platform:
public Vector3[] localWaypoints;
Vector3[] globalWaypoints;
public float speed;
public bool cyclic;
public float waitTime;
[Range(0, 2)]
public float easeAmount;
int fromWaypointIndex;
float percentBetweenWaypoints;
float nextMoveTime;
void Start()
{
globalWaypoints = new Vector3[localWaypoints.Length];
for (int i = 0; i < localWaypoints.Length; i++)
{
globalWaypoints[i] = localWaypoints[i] + transform.position;
}
}
void Update()
{
Vector3 velocity = CalculatePlatformMovement();
transform.Translate(velocity);
}
float Ease(float x)
{
float a = easeAmount + 1;
return Mathf.Pow(x, a) / (Mathf.Pow(x, a) + Mathf.Pow(1 - x, a));
}
Vector3 CalculatePlatformMovement()
{
if (Time.time < nextMoveTime)
{
return Vector3.zero;
}
fromWaypointIndex %= globalWaypoints.Length;
int toWaypointIndex = (fromWaypointIndex + 1) % globalWaypoints.Length;
float distanceBetweenWaypoints = Vector3.Distance(globalWaypoints[fromWaypointIndex], globalWaypoints[toWaypointIndex]);
percentBetweenWaypoints += Time.deltaTime * speed / distanceBetweenWaypoints;
percentBetweenWaypoints = Mathf.Clamp01(percentBetweenWaypoints);
float easedPercentBetweenWaypoints = Ease(percentBetweenWaypoints);
Vector3 newPos = Vector3.Lerp(globalWaypoints[fromWaypointIndex], globalWaypoints[toWaypointIndex], easedPercentBetweenWaypoints);
if (percentBetweenWaypoints >= 1)
{
percentBetweenWaypoints = 0;
fromWaypointIndex++;
if (!cyclic)
{
if (fromWaypointIndex >= globalWaypoints.Length - 1)
{
fromWaypointIndex = 0;
System.Array.Reverse(globalWaypoints);
}
}
nextMoveTime = Time.time + waitTime;
}
return newPos - transform.position;
}
struct PassengerMovement
{
public Transform transform;
public Vector3 velocity;
public bool standingOnPlatform;
public bool moveBeforePlatform;
public PassengerMovement(Transform _transform, Vector3 _velocity, bool _standingOnPlatform, bool _moveBeforePlatform)
{
transform = _transform;
velocity = _velocity;
standingOnPlatform = _standingOnPlatform;
moveBeforePlatform = _moveBeforePlatform;
}
}
void OnDrawGizmos()
{
if (localWaypoints != null)
{
Gizmos.color = Color.red;
float size = .3f;
for (int i = 0; i < localWaypoints.Length; i++)
{
Vector3 globalWaypointPos = (Application.isPlaying) ? globalWaypoints[i] : localWaypoints[i] + transform.position;
Gizmos.DrawLine(globalWaypointPos - Vector3.up * size, globalWaypointPos + Vector3.up * size);
Gizmos.DrawLine(globalWaypointPos - Vector3.left * size, globalWaypointPos + Vector3.left * size);
}
}
}
UPDATE: Upon further testing I found that if the first object in my localWaypoint array is set to 0,0,0 and my 2nd object is set to 1,0,0 then the platform will spiral to the right, making sure to hit the waypoints as it's spiraling, and then spiraling out into nowhere like in the image above. But if I set my first object to 0,0,0 and my second object to -1,0,0 then the object will act the same way as before, but will spiral to the left as displayed in this image. (The second image has also bee updated to display how the platfrom makes sure to hit both waypoints before is spirals out into nowhere).
I've also noticed that if I set both waypoints to 0,0,0 then the platform stays still, these 2 things prove that it has somthing to do with the way the waypoints are being handled and not some other script or parent object interfering.
Using the updated numbers ([0,0,0], [1,0,0]) works in my test app. However, if I put a rotation on the object's Y axis, then I see behavior like you are seeing. In Update, if you change:
transform.Translate(velocity);
to
transform.Translate(velocity, Space.World);
You should see your desired behavior. Note that "transform.Translate(velocity)" is the same as "transform.Translate(velocity, Space.Self)". Your translation is being rotated.
If you are curious, take a look at this for more information on how the values in the transform are applied:
https://gamedev.stackexchange.com/questions/138358/what-is-the-transformation-order-when-using-the-transform-class

How to move 2D Object within camera view boundary

I have a scene that my camera doesn't follow my player. When player reaches the end of camera I want player to can't go further (out of camera view). How can I do this?
My codes for movement
public class PlayerBlueController : MonoBehaviour {
public float speed;
private float x;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
x = Input.GetAxis ("Horizontal") / 100 * speed;
transform.Translate (x,0,0);
}
}
As you can see from this. It gets out of camera's view.
I noticed you used a Collider2D. You should be using Rigidbody2D.MovePosition instead of transform.Translate or you'll likely run into issues when transform.Translate is used.
1.Take the final move position and convert it to new position in ViewPortPoint with Camera.main.WorldToViewportPoint
2.Apply a limit with Mathf.Clamp to the result in #1.
3.Convert the ViewPortPoint back to world point with Camera.main.ViewportToWorldPoint.
4.Finally, move it with Rigidbody2D.MovePosition.
The code below is modified from this answer to include restriction to screen boundary.
Move without Rigidbody:
Use only if collision and physics are NOT required:
public float speed = 100;
public Transform obj;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//Move only if we actually pressed something
if ((h > 0 || v > 0) || (h < 0 || v < 0))
{
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
Vector3 newPos = obj.transform.position + tempVect;
checkBoundary(newPos);
}
}
void checkBoundary(Vector3 newPos)
{
//Convert to camera view point
Vector3 camViewPoint = Camera.main.WorldToViewportPoint(newPos);
//Apply limit
camViewPoint.x = Mathf.Clamp(camViewPoint.x, 0.04f, 0.96f);
camViewPoint.y = Mathf.Clamp(camViewPoint.y, 0.07f, 0.93f);
//Convert to world point then apply result to the target object
obj.position = Camera.main.ViewportToWorldPoint(camViewPoint);
}
Move Object with Rigidbody2D:
Use if collision and physics are required:
public float speed = 100;
public Rigidbody2D rb;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//Move only if we actually pressed something
if ((h > 0 || v > 0) || (h < 0 || v < 0))
{
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
//rb.MovePosition(rb.transform.position + tempVect);
Vector3 newPos = rb.transform.position + tempVect;
checkBoundary(newPos);
}
}
void checkBoundary(Vector3 newPos)
{
//Convert to camera view point
Vector3 camViewPoint = Camera.main.WorldToViewportPoint(newPos);
//Apply limit
camViewPoint.x = Mathf.Clamp(camViewPoint.x, 0.04f, 0.96f);
camViewPoint.y = Mathf.Clamp(camViewPoint.y, 0.07f, 0.93f);
//Convert to world point then apply result to the target object
Vector3 finalPos = Camera.main.ViewportToWorldPoint(camViewPoint);
rb.MovePosition(finalPos);
}
image not respond .
but you can check player location
x = Input.GetAxis ("Horizontal") / 100 * speed;
if(gameobject.transform.x > someValue)
x=0
gameobject will be OBJECT in scene that u attach class to it.
another way is place 2 empty gameobject with collider as invisibleWall and get collider to player
Possible solution is:
1.Get the coordinates of your screen cornes (top left, top right, bottom left, bottom right). You can get this coordinates using Screen.height and Screen.width.
2.Convert this coordinates using the camera you need with Camera.ScreenToWorldPoint.
3.Get your player coordinates and check that they are inside the rect which is formed by 4 coordinates of the screen corners.
You need to limit your transform's position based on the edges of the camera. Here is an answer describing the different coordinate systems in unity
You're probably looking to do something like this:
float xMin = Camera.main.ViewportToWorldPoint(Vector3.zero).x;
float xMax = Camera.main.ViewportToWorldPoint(Vector3.one).x;
Vector3 currentPos = transform.position;
float dx = Input.GetAxis ("Horizontal") / 100 * speed;
Vector3 desiredPos = new Vector3(currentPos.x + dx, currentPos.y, currentPos.z);
Vector3 realPos = desiredPos;
if(desiredPos.x > xMax)
realPos.x = xMax;
else if(desiredPos.x < xMin)
realPos.x = xMin;
transform.position = realPos;
Read up here for more info on ViewportToWorldPoint(), it's extremely useful to become comfortable with the different coordinate spaces and how you can convert between them.

Unity - Decrease/Increase the "Z axis" angle axis on Update()

[See my answer below! Thanks for the comments]
What is the best way to Decrease/Increase an angle without using "eulerAngles"?
I need turn my transform between 30 and 325, according to my "Y axis".
If it is less than zero we reduce the value of "Z axis" is greater if we increase the value of "Z axis".
I already tried:
if ( airPlanePlayerRigidbody2D.velocity.y < 0 ) {
var rotationVector = airPlanePlayerTransform.rotation;
rotationVector.z -= 2f;
Vector3 vec = new Vector3(rotationVector.x,rotationVector.y,rotationVector.z);
airPlanePlayerTransform.rotation = Quaternion.Euler(vec);
}
And:
public void Rotate(float angle)
{
airPlanePlayerTransform.Rotate(0, 0, angle); // this rotate forever
}
Thank you in advance.
UPDATE
In fact I already have a code that does what I need, but it is too long. I'm looking for some simpler. The result I want is this:
A more smoothed animation:Smooth animation down
And what I can do so far with the comments posted here, is this:Without animation
I think eulerAngeles is a good way to go with, and I successfully did the similar rotation effect in Unity by using eulerAngeles. You probably didn't find the correct way when you used it.
I used something like that:
Vector3 newRotationAngles = transform.rotation.eulerAngles;
if (Input.GetKey(KeyCode.LeftArrow)) {
newRotationAngles.z += 1;
} else if (Input.GetKey(KeyCode.RightArrow)) {
newRotationAngles.z -= 1;
}
transform.rotation = Quaternion.Euler (newRotationAngles);
Output: when you press the left/right arrow keys, the gameObject will rotate, but it is not always rotating. Hope this is helpful.
You want something like this:
public float maxAngle = 325f; // Max Angle for Z Axis
public float minAngle = 30f; // Min Angle for Z Axis
public float rotationDelta = 1f; // you can control your rotation speed using this
float tempAngle;
void Update()
{
// If AirPlane is rising, velocity in y is greater than 0
if(rigidbody2D.velocity.y > 0)
{
tempAngle = Mathf.LerpAngle(transform.eulerAngles.z, maxAngle, Time.time * rotationDelta);
}
// If AirPlane is falling, velocity in y is less than 0
else if(rigidbody2D.velocity.y < 0)
{
tempAngle = Mathf.LerpAngle(transform.eulerAngles.z, minAngle, Time.time * rotationDelta);
}
// If AirPlane is going straight in horizontal.
else
{
tempAngle = 0;
}
transform.eulerAngles = new Vector3(0, 0, tempAngle);
}
Researching the topic, I managed to get the result. Thanks for the replies.
if (airPlanePlayerRigidbody2D.velocity.y < 0) { //falling
var myNewQuaternion = Quaternion.Euler (new Vector3 (0, 0, -30));
var myQuaternion = Quaternion.Euler(new Vector3(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z));
transform.rotation = Quaternion.Slerp (myQuaternion , myNewQuaternion , speed );
}
else if (airPlanePlayerRigidbody2D.velocity.y > 0) { // rising
var myNewQuaternion = Quaternion.Euler (new Vector3 (0, 0, 30));
var myQuaternion = Quaternion.Euler(new Vector3(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z));
transform.rotation = Quaternion.Slerp (myQuaternion , myNewQuaternion , speed );
}

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.

Categories

Resources