Can't seem to get smooth 3D collision XNA - c#

I am making a 3D game. The player is the "camera". I want it not to go through walls which is achieved. But now I want it to be able to "glide" along the wall as in any other fps. Here is the code: and thanks in advance:
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
keyState = Keyboard.GetState();
camera.Update(gameTime);
if (keyState.IsKeyDown(Keys.W)) camera.moveVector.Z = 1;
if (keyState.IsKeyDown(Keys.S)) camera.moveVector.Z = -1;
if (keyState.IsKeyDown(Keys.A)) camera.moveVector.X = 1;
if (keyState.IsKeyDown(Keys.D)) camera.moveVector.X = -1;
if (keyState.IsKeyDown(Keys.Space)&&camera.Position.Y>=0.5f) camera.moveVector.Y = 0.5f;
if (camera.moveVector != Vector3.Zero)
{
//We don't want to make the player move faster when it is going diagonally.
camera.moveVector.Normalize();
//Now we add the smoothing factor and speed factor
camera.moveVector *= (dt * camera.cameraSpeed);
Vector3 newPosition = camera.PreviewMove(camera.moveVector);
bool moveTrue = true;
if (newPosition.X < 0 || newPosition.X > Map.mazeWidth) moveTrue = false;
if (newPosition.Z < 0 || newPosition.Z > Map.mazeHeight) moveTrue = false;
foreach (BoundingBox boxes in map.GetBoundsForCell((int)newPosition.X, (int)newPosition.Z))
{
if (boxes.Contains(newPosition) == ContainmentType.Contains)
{
moveTrue = false;
}
}
if (moveTrue) camera.Move(camera.moveVector);
base.Update(gameTime);
And here is the code for excecuting the movement:
//Updating the look at vector
public void UpdateLookAt()
{
//Built a rotation matrix to rotate the direction we are looking
Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);
// Build a look at offset vector
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
//Update our camera's look at the vector
cameraLookAt = (cameraPosition + lookAtOffset);
}
//Method to create movement and to check if it can move:)
public Vector3 PreviewMove(Vector3 amount)
{
//Create a rotation matrix to move the camera
Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);
//Create the vector for movement
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
// Give the value of the camera position +ze movement
return (cameraPosition+movement);
}
//Method that moves the camera when it hasnt'collided with anything
public void Move(Vector3 scale)
{
//Moveto the location
MoveTo(PreviewMove(scale), Rotation);
}
Already thought of using the invert method given by xna. But I can't seem to find the normal. And I have tried to move the camera parallel to the wall. But I was unable to achieve that. Any help is appreicated.

If you have found that a point intersects a bounding box, you have to check which of the six faces the entry point lies in. This can be done as follows: Construct a line segment between the old camera position and the new one:
p = (1 - t) * oldPos + t * newPos
, where you use only the dimension of oldPos and newPos, which is interesting for the face (e.g. for the left/right face, take the x-coordinate). p is the according coordinate for the face. Calculate t for every face and find the face for which t is maximal, ignoring faces behind which the point already lies (i.e. the dot product of the face normal and the direction from the face to the point is negative). This will be the face, in which your entry point lies. All you then need to do is adapt the relevant coordinate (again the x-coordinate for left/right face etc.), such that it does not lie within the bounds (e.g. set newPos.x = boundingBox.MaxX for the right face). This is equal to a projection of the point onto the bounding box surface and equivalent to using only the component of the movement vector that is parallel to the box if an intersection would occur).
Btw, the solution of the above formula is:
t = (p - oldPos) / (newPos - oldPos)

Related

How to realistically reflect a 3d sphere in Unity with C#

I've been trying to realistically reflect a 3d sphere on the walls of a box for a while now in Unity. For some reason, the reflection is generally correct, but when the ball hits a wall in certain directions, the reflection is incorrect.
To illustrate what happens to the ball upon hitting a wall: T = top wall, R = right wall, L = left wall, and B = bottom wall. Let r = the ball comes/goes to the right, l = for the left, and s = the ball stops/slows down significantly. The instructions below take this format: Xyz, where X = the wall the ball is about to hit, y = the ball's initial direction, z = the reflection. The game has a top-down perspective, and the instructions are based on the wall's perspective. I'm also new to C#, so the code is potentially eye burning.
Instructions: Tll, Trl; Bll, Brl; Rls or after hitting another wall Rlr, Rrl; Lls or after hitting another wall Llr, Lrl
Generally, when the ball stops, it jumps in the air. I wonder if this is because the angle reflects along the wrong axis, but why would this only sometimes happen? Also, when only one key is held, the ball bounces back and forth until it leaves the arena. I know about discrete and continuous hit detection, and the setting is on discrete, but the walls generally contain the ball well enough, with this case being the exception.
What I tried:
Figuring out how to use Vector3.Reflect. I do not understand what variables this function should contain and how to apply this to my code. I did look at the Unity Documentation page for help, but it did not answer my question.
Changing the negative signs, as the angle has to be a reflection on the y-axis, and this does change how the reflections work, but does not solve the problem. The current way the negatives are ordered are the most ideal I found.
Giving the ball a physics material for bounciness.
Adding a small number to the denominator in the arctan equation to help prevent a division by zero. This did not help at all.
Creating different equations (basically changing the negatives) for varying combinations of positive and negative accelerations. Since there is a certain positive or negative acceleration associated with each button press (see Movement script), and there seems to be an issue with said signs, I wondered if associating each acceleration with its own set of equations could solve the problem. It did not work.
Checked if the walls were at different angles.
Deleting the variables xA and yA, or placing them in different spots.
Experimented with finding the speed of the object, but had no idea how to implement it.
The code for the Movement script for the player named Controller:
public class Movement : MonoBehaviour
{
public static float xAcceleration = 0.0f;
public static float yAcceleration = 0.0f;
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.W)) //If the key W is pressed:
{
Vector3 position = this.transform.position; //Variable position is set to transform the players placement in the game.
if (yAcceleration >= -5 && yAcceleration <= 5) //If the y vector of the acceleration is >= -5 and <= 5:
{
yAcceleration = yAcceleration + 0.01f; //The y vector of the acceleration increases by 0.01 as long as the key W is pressed.
}
position.z = position.z + (0.1f * yAcceleration); //The position of the object on the z-axis (pretend it is the y-axis in the game world) is transformed by its original position plus its speed times its yAcceleration.
this.transform.position = position; //The gameObject is now transformed to a position equal to the variable position by the z-axis.
}
else //If the key W is let go of:
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position; //The position of the gameObject continues to update, but its acceleration does not change. Basically, it continues to move forward.
}
//The rest of the code is very similar to the above, but I included it just in case there was something wrong.
if (Input.GetKey(KeyCode.S))
{
Vector3 position = this.transform.position;
if (yAcceleration >= -5 && yAcceleration <= 5)
{
yAcceleration = (yAcceleration) - 0.01f;
}
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.A))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) - 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.D))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) + 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
}
}
This is the code for the collider and reflection:
public class Collider : MonoBehaviour
{
public float xA;
public float yA;
void OnCollisionEnter(Collision collision) //If a gameObject enters the collision of another object, this immediately happens once.
{
if (gameObject.tag == "Boundary") //If the gameObject has a tag named Boundary:
{
yA = -Movement.yAcceleration; //yA stores the value of yAcceleration after being called from script Movement as a negative. Its a reflection.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration)); //xAcceleration is changed based on this equation: A * artan(A_y / A_x). The 0.000001 was here, adding to A_x to help prevent a 0 as the denominator.
xA = Movement.xAcceleration; //This is declared now...
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA)); //This uses xA because Movement.xAcceleration is changed, and the yAcceleration calculation is based on the xAcceleration prior the collision.
}
}
void OnCollisionStay(Collision collision)
{
if (gameObject.tag == "Boundary")
{
yA = Movement.yAcceleration; //The same thing happens as before.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));
xA = Movement.xAcceleration;
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));
Movement.xAcceleration = -Movement.xAcceleration / 2; //On collision, the ball is reflected across the x-axis at half its speed.
Movement.yAcceleration = Movement.yAcceleration / 2; //yAcceleration is half its original value.
}
}
}
The picture below is the game setup. I apologize that it is a link; I do not have enough Reputation to merit a loaded image on this page. Also, if anything is unclear, please message me.
https://i.stack.imgur.com/VREV4.png
I would really appreciate the help. Thanks!
One very important note here: As soon as there is any Rigidbody involved you do not want to set any values through the .transform - This breaks the physics and collision detection!
Your Movement should rather alter the behavior of the Rigidbody e.g. by simply changing its Rigibody.velocity
I would then also place the collision check directly into the balls's component and check whether you hit a wall ("Boundary")
Then another note: Your code is currently frame-rate dependent. It means that if your target device runs with only 30 frames per second you will add 0.3 per second to the acceleration. If you run however on a more powerful device that manages to run with 200 frames per second then you add 2 per second.
You should rather define the de/increase per second and multiply it by Time.deltaTime
All together maybe something like this
public class Movement : MonoBehaviour
{
// Adjust these settings via the Inspector
[SerializeField] private float _maxMoveSpeed = 5f;
[SerializeField] private float _speedIncreasePerSecond = 1f;
// Already reference this via the Inspector
[SerializeField] private Rigidbody _rigidbody;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
// Get User Input in Update
private void Update()
{
var velocity = _rigidbody.velocity;
velocity.y = 0;
if (Input.GetKey(KeyCode.W) && velocity.z < _maxMoveSpeed)
{
velocity.z += _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.S) && velocity.z > -_maxMoveSpeed)
{
velocity.z -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A) && velocity.x > -_maxMoveSpeed)
{
velocity.x -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.D) && velocity.x < _maxMoveSpeed)
{
velocity.x += _speedIncreasePerSecond * Time.deltaTime;
}
// clamp to the max speed in case you move diagonal
if(velocity.magnitude > _maxMoveSpeed)
{
velocity = velocity.normalized * _maxMoveSpeed;
}
_rigidbody.velocity = velocity;
}
}
And then finally simply add a PhysicsMaterial with desired settings to the walls and ball.
I used Friction = 0f and Bounciness = 0.7f for ball and walls. For slow movements you also might want/have to adjust the Bounce Threshold in the Project's Physics Settings otherwise there will be no bouncing if the velocity is smaller then 2 by default.
This depends a bit on your definition of "realistic". I disabled gravity so the ball also has no rotation and angular friction:

How to smooth out 2D rotation in C#?

I'm making a 2D game in Unity and one feature I'm trying to implement is flipping the player if the mouse position is over 90 meaning he is always facing the direction of the mouse.
Vector3 mousePosition = UtilsClass.GetMouseWorldPosition();
Vector3 aimDirection = (mousePosition - transform.position).normalized;
float angle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg;
Vector3 aimLocalScale = Vector3.one;
if (angle > 90 || angle < -90)
{
rotate = true;
}
else
{
rotate = false;
}
gameObject.transform.localScale = aimLocalScale;
if (rotate == true)
{
transform.Rotate(0f, 180f, 0f);
}
else
{
transform.Rotate(0f, 0f, 0f);
}
However, one problem I am having is that one, it's very finicky and if you go too fast the player is looking in the wrong direction, and two, if you look straight up, he doesn't know exactly where to look and keeps snapping left and right really fast.
Does anyone know a way to fix this allowing for a smoother more functional flipping?
Try flipping the sprite by adjusting its scale from 1 to -1. Then implement that into a FlipSprite() method and just call it when you want to flip it
So if you want to flip and sprite one approach could be to inverse the localScale of the sprite, like this:
private void FlipSprite()
{
// Multiply the player's x local scale by -1.
Vector3 flippedScale = transform.localScale;
flippedScale.x *= -1;
transform.localScale = flippedScale;
}
I suggest you to keep a boolean variable like isFacingRight to keep tracking the direction the sprite is looking at!
The issue is you use the same rotate threshold for when the character is facing right and left, so when you straddle the threshold he flips a lot and its bad UX for the player.
Solution : make the rotate threshold dynamic so each time the player rotates the threshold gets pushed down.
float thresholdAngle = 90.0f;
change angle > 90 || angle < -90 to
if (angle > thresholdAngle || angle < -1.0f * thresholdAngle)
Then add in
if (rotate == true)
{
transform.Rotate(0f, 180f, 0f);
thresholdAngle = 110.0f;
}
else
{
transform.Rotate(0f, 0f, 0f);
thresholdAngle = 80.0f;
}
I'm not sure which way your character faces first so you may have to swap the thresholdAngle = 110.0f with the threshold = 80.0f;

Monogame - Making the camera stop when my player collides

I have come to a position with my 3D game where I am now trying to perfect the camera. What I ultimately want is a working first person camera that stops when it collides with a wall. I assume you do this by having the camera move with a model and have the model collide to stop the camera also, i am just having issues seeing how to do that.
I have the model working so it stops, i just need to lock the camera from moving also. So far this is the code I have, making the model stop with collison:
if (player_health != 0)
{
if (kb.IsKeyDown(Keys.Left))
{
player_x = player_x + 0.05f;
world_player = Matrix.CreateTranslation(new Vector3(player_x, player_y, player_z));
if (IsCollision(ball, world_player, ball, world_spike))
{
player_x = player_x - 0.05f;
world_player = Matrix.CreateTranslation(new Vector3(player_x, player_y, player_z));
player_health = 0;
}
if (IsCollision(ball, world_player, ball, world_bullet[0]))
{
player_health = 0;
}
// use this code for any standard collision
if ((IsCollision(ball, world_player, ball, world_cannon)) || (IsCollision(ball, world_player, ball, walls[0])) || (IsCollision(ball, world_player, ball, walls[1])))
{
player_x = player_x - 0.05f;
world_player = Matrix.CreateTranslation(new Vector3(player_x, player_y, player_z));
}
}
My camera added into the game1 class:
camera = new Camera(this, new Vector3(10f, 3f, 5f), Vector3.Zero, 5f);
Components.Add(camera);
And the camera class itself (I have been told this is rather over complicated? for a camera class, but the simpler ones didnt work):
namespace Game7
{
class Camera : GameComponent
{
private Vector3 cameraPosition;
private Vector3 cameraRotation;
private float cameraSpeed;
private Vector3 cameraLookAt;
private Vector3 mouseRotationBuffer;
private MouseState currentMouseState;
private MouseState previousMouseState;
// Properties
public Vector3 Position
{
get { return cameraPosition; }
set
{
cameraPosition = value;
UpdateLookAt();
}
}
public Vector3 Rotation
{
get { return cameraRotation; }
set
{
cameraRotation = value;
UpdateLookAt();
}
}
public Matrix Projection
{
get;
protected set;
}
public Matrix View
{
get
{
return Matrix.CreateLookAt(cameraPosition, cameraLookAt, Vector3.Up);
}
}
//Constructor
public Camera(Game game, Vector3 position, Vector3 rotation, float speed)
: base(game)
{
cameraSpeed = speed;
// projection matrix
Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4,
Game.GraphicsDevice.Viewport.AspectRatio,
0.05f,
1000.0f);
// set camera positiona nd rotation
MoveTo(position, rotation);
previousMouseState = Mouse.GetState();
}
// set Camera's position and rotation
private void MoveTo(Vector3 pos, Vector3 rot)
{
Position = pos;
Rotation = rot;
}
//update the look at vector
private void UpdateLookAt()
{
// build rotation matrix
Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);
// Look at ofset, change of look at
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
// update our cameras look at vector
cameraLookAt = cameraPosition + lookAtOffset;
}
// Simulated movement
private Vector3 PreviewMove(Vector3 amount)
{
// Create rotate matrix
Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);
// Create a movement vector
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
return cameraPosition + movement;
}
// Actually move the camera
private void Move(Vector3 scale)
{
MoveTo(PreviewMove(scale), Rotation);
}
// updat method
public override void Update(GameTime gameTime)
{
// smooth mouse?
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
currentMouseState = Mouse.GetState();
KeyboardState ks = Keyboard.GetState();
// input
Vector3 moveVector = Vector3.Zero;
if (ks.IsKeyDown(Keys.W))
moveVector.Z = 1;
if (ks.IsKeyDown(Keys.S))
moveVector.Z = -1;
if (ks.IsKeyDown(Keys.A))
moveVector.X = 1;
if (ks.IsKeyDown(Keys.D))
moveVector.X = -1;
if (moveVector != Vector3.Zero)
{
//normalize it
//so that we dont move faster diagonally
moveVector.Normalize();
// now smooth and speed
moveVector *= dt * cameraSpeed;
// move camera
Move(moveVector);
}
// Handle mouse input
float deltaX;
float deltaY;
if(currentMouseState != previousMouseState)
{
//Cache mouse location
deltaX = currentMouseState.X - (Game.GraphicsDevice.Viewport.Width / 2);
deltaY = currentMouseState.Y - (Game.GraphicsDevice.Viewport.Height / 2);
// smooth mouse ? rotation
mouseRotationBuffer.X -= 0.01f * deltaX * dt;
mouseRotationBuffer.Y -= 0.01f * deltaY * dt;
if (mouseRotationBuffer.Y < MathHelper.ToRadians(-75.0f))
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(-75.0f));
if (mouseRotationBuffer.Y > MathHelper.ToRadians(75.0f))
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(75.0f));
Rotation = new Vector3(-MathHelper.Clamp(mouseRotationBuffer.Y, MathHelper.ToRadians(-75.0f), MathHelper.ToRadians(75.0f)), MathHelper.WrapAngle(mouseRotationBuffer.X), 0);
deltaX = 0;
deltaY = 0;
}
// Alt + F4 to close now.
// Makes sure the mouse doesn't wander across the screen (might be a little buggy by showing the mouse)
Mouse.SetPosition(Game.GraphicsDevice.Viewport.Width / 2, Game.GraphicsDevice.Viewport.Height / 2);
previousMouseState = currentMouseState;
base.Update(gameTime);
}
}
}
The Preview Move part of the camera class is meant to be where it detects for collision, at least the guide i followed said so, i just dont see how to integrate it. Thanks for any help or input, even if it is just a link to another guide or resource, would be very appreciated!
If your hero model (player) already has move functionality and collision functionality, and you want the camera to follow the model, then you can simplify the camera update tremendously.
Assuming after moving the player, the new position and/or orientation of the player is represented in player_world, simply borrow the location and orientation information stored in the player_world matrix to build a view matrix each frame.
//after moving player and setting its matrix accordingly:
view = Matrix.Invert(player_world);
And that is your complete camera class update. This creates a view matrix that is in the same position as the player and facing the same direction he is. If the player model stops because it hits a wall, then the camera (view matrix) stops too, because it is built from the player's 'stopped' matrix. No need to create a whole class for moving and rotating the camera around when the movement and rotation is the same as the player.
Sometimes the local player model origin would be located at the player's feet or belly and you want the camera up in the head where the eyes are. If so, simply apply something like this:
//after moving player and setting its matrix accordingly:
Matrix camera_world = player_world;
camera_world *= Matrix.CreateTranslation(Vector3.Up * ??f);// set ??f to the height difference between feet and head
view = Matrix.Invert(camera_world);

XNA make sprite follow the mouse pointer but with a delay?

Here I updated my code with your help.
Anyway it still does not do what is supposed to do, following the mouse pointer with a delay.
The balloon (the sprite) is flying diagonally and does not stop once the mouse pointer is met, only slows down then keeps moving and then speeds up.
I added an if condition once the balloonPosition is equal to mouse pointer, to have the velocity = 0, but that does not stop the balloon.
I added a portion of code for keeping the balloon (the sprite) in the screen.
protected override void Update(GameTime gameTime)
{
MouseState currentMouseState = Mouse.GetState();
//balloonPosition = new Vector2(currentMouseState.X, currentMouseState.Y);
//System.Windows.Input.MouseState currentMouseState = System.Windows.Input.Mouse.GetState();
// Get the current mouse position
Vector2 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
// Get the distance between the balloon and the mouse.
float distance = Vector2.Distance(mousePosition, balloonPosition);
// You can change the standard velocity / or the max distance to make the sprite move faster or slower.
// Currently it may move to fast or to slow for you to know a difference.
balloonVelocity = StandardVelocity * (distance/MaxDistance);
// Set the balloons position to the new velocity.
balloonPosition += balloonVelocity;
if (balloonPosition == mousePosition)
{
balloonVelocity = new Vector2(0);
}
//Keep the balloon in the screen
if (balloonPosition.X < balloon.Width / 2)
balloonPosition.X = balloon.Width / 2;
if (balloonPosition.Y < balloon.Height / 2)
balloonPosition.Y = balloon.Height / 2;
if (balloonPosition.X > Window.ClientBounds.Width - balloon.Width / 2)
balloonPosition.X = Window.ClientBounds.Width - balloon.Width / 2;
if (balloonPosition.Y > Window.ClientBounds.Height - balloon.Height / 2)
balloonPosition.Y = Window.ClientBounds.Height;
}
NewPosition = Vector2.Lerp(CurrentPosition, DesiredPosition, Speed);
Here's what I'd go with off the top of my head:
public List<Tuple<TimeSpan, Vector2>> Points { get; set; }
public Vector2 CurrentPoint { get; set; }
public void Update(GameTime gameTime)
{
Points.Add(new Tuple<TimeSpan, Vector2>(TimeSpan.FromSeconds(1), mouseCoords));
foreach(var point in Points)
{
point.Item1 -= gameTime.ElapsedTimeSpan;
if (point.Item1 < TimeSpan.Zero)point
CurrentPoint = point.Item2;
}
}
I'm pretty sure I wouldn't need to explain the code, right?
In the draw loop, you'd simply always draw the CurrentPoint
You can use a list or an array to keep the delay. Store the mouse position every 0.03125 seconds in a list. Then set timer for the delay (integer counter should work). When the delay is up start reading from the list at the same pace it was out in the list. (0.03125 in this case) and move your sprite towards that point.
Since you are looking for how to change the speed this is one way to do it.
Vector2 balloonOrigin, balloonPosition;
Vector2 balloonVelocity;
private float MaxDistance = 1920; // Reduce this value to slow down the balloon.
public static float AngleBetweenVectors(Vector2 v1, Vector2 v2) { return (float)Math.Atan2(v2.Y - v1.Y, v2.X - v1.X); }
public void test()
{
//System.Windows.Input.MouseState currentMouseState = System.Windows.Input.Mouse.GetState();
// Get the current mouse position
Vector2 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
// Get the distance between the balloon and the mouse.
float distance = Vector2.Distance(mousePosition, balloonPosition);
// You can change the max distance to make the sprite move faster or slower.
// Currently it may move to fast or to slow for you to know a difference.
balloonVelocity = AngleBetweenVectors(balloonPosition, mousePosition) * (distance / MaxDistance);
// Set the balloons position to the new velocity.
balloonPosition += balloonVelocity;
}

Smooth Collision (Wall sliding)

I recently started learning XNA and C# because of a school project I am doing. I followed this tutorial on YouTube to learn how XNA works with 3D:
http://www.youtube.com/watch?v=XkpZLzT5OV4
It works pretty well actually, and I already made some modification/added some features to my project. I recently started to implement the collision detection, which works well using BoundingBoxes, but the camera literally stops when it collides. I would like it to kinda slide on the wall, as every other first person games do. Basically, the camera stops if the movement is directed to the wall. I would like the camera to "slide" on the wall, removing the movement directed to the wall, and making it only going parallel to the wall. I hope that makes sense. This way, the player will be able to still move around while he's touching the wall.
I looked at a lot of posts on google, and I saw that I needed to play with the player's velocity. However, I don't have any velocity implemented in my code, and I don't know how to add it.
Here are my main classes:
Here is my Camera class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace Deimos
{
class Camera : GameComponent
{
// ...
// Constructor
public Camera(Game game, Vector3 position, Vector3 rotation, float speed)
: base(game)
{
CameraSpeed = speed;
// Setup projection matrix
Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4,
Game.GraphicsDevice.Viewport.AspectRatio,
0.05f,
1000.0f // Draw distance
);
// Set the camera position and rotation
moveTo(position, rotation);
PreviousMouseState = Mouse.GetState();
}
// Set camera position and rotation
private void moveTo(Vector3 position, Vector3 rotation)
{
// Thanks to the properties set at the beginning, setting up these values will execute
// the code inside the property (i.e update our vectors)
Position = position;
Rotation = rotation;
}
// Update the look at vector
private void updateLookAt()
{
// Build a rotation matrix
Matrix rotationMatrix = Matrix.CreateRotationX(CameraRotation.X) * Matrix.CreateRotationY(CameraRotation.Y);
// Build look at offset vector
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
// Update our camera's look at vector
CameraLookAt = CameraPosition + lookAtOffset;
}
// Methods that simulate movement
private Vector3 previewMove(Vector3 amount)
{
// Create a rotate matrix
Matrix rotate = Matrix.CreateRotationY(CameraRotation.Y);
// Create a movement vector
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
// Return the value of camera position + movement vector
if (Collision.CheckCollision(CameraPosition + movement)) // Testing for the UPCOMING position
{
return CameraPosition;
}
else
{
return CameraPosition + movement;
}
}
// Method that actually moves the camera
private void move(Vector3 scale)
{
moveTo(previewMove(scale), Rotation);
}
// Update method, overriding the original one
public override void Update(GameTime gameTime)
{
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
CurrentMouseState = Mouse.GetState();
// Let's get user inputs
KeyboardState ks = Keyboard.GetState();
// Handle basic key movement
Vector3 moveVector = Vector3.Zero;
if (ks.IsKeyDown(ForwardKey))
{
moveVector.Z = 1;
}
if (ks.IsKeyDown(BackKey))
{
moveVector.Z = -1;
}
if (ks.IsKeyDown(LeftKey))
{
moveVector.X = 1;
}
if (ks.IsKeyDown(RightKey))
{
moveVector.X = -1;
}
if (ks.IsKeyDown(Keys.Up))
{
moveVector.Y = 1;
}
if (ks.IsKeyDown(Keys.Down))
{
moveVector.Y = -1;
}
if (moveVector != Vector3.Zero) // If we are actually moving (if the vector changed depending on the ifs)
{
// Normalize that vector so that we don't move faster diagonally
moveVector.Normalize();
// Now we add in move factor and speed
moveVector *= dt * CameraSpeed;
DebugScreen.Log(moveVector.ToString());
// Move camera!
move(moveVector);
}
// Handle mouse movement
float deltaX;
float deltaY;
if (CurrentMouseState != PreviousMouseState)
{
// Cache mouse location
deltaX = CurrentMouseState.X - (Game.GraphicsDevice.Viewport.Width / 2); // We devide by 2 because mouse will be in the center
deltaY = CurrentMouseState.Y - (Game.GraphicsDevice.Viewport.Height / 2);
MouseRotationBuffer.X -= MouseSpeed * deltaX * dt;
MouseRotationBuffer.Y -= MouseSpeed * deltaY * dt;
// Limit the user so he can't do an unlimited movement with his mouse (like a 7683°)
if(MouseRotationBuffer.Y < MathHelper.ToRadians(-75.0f))
MouseRotationBuffer.Y = MouseRotationBuffer.Y - (MouseRotationBuffer.Y - MathHelper.ToRadians(-75.0f));
if(MouseRotationBuffer.Y > MathHelper.ToRadians(75.0f))
MouseRotationBuffer.Y = MouseRotationBuffer.Y - (MouseRotationBuffer.Y - MathHelper.ToRadians(75.0f));
float mouseInverted = (MouseInverted == true) ? 1 : -1;
Rotation = new Vector3(
mouseInverted * MathHelper.Clamp(
MouseRotationBuffer.Y,
MathHelper.ToRadians(-75.0f),
MathHelper.ToRadians(75.0f)
),
MathHelper.WrapAngle(MouseRotationBuffer.X),
// This is so the camera isn't going really fast after some time
// (as we are increasing the speed with time)
0
);
// Resetting them
deltaX = 0;
deltaY = 0;
}
// Putting the cursor in the middle of the screen
Mouse.SetPosition(Game.GraphicsDevice.Viewport.Width / 2, Game.GraphicsDevice.Viewport.Height / 2);
PreviousMouseState = CurrentMouseState;
base.Update(gameTime);
}
}
}
And here is my Collision class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Deimos
{
class Collision
{
// ...
public Boolean CheckCollision(Vector3 cameraPosition)
{
// Creating the sphere of the camera for later collisions checks
BoundingBox cameraBox = new BoundingBox(
new Vector3(
cameraPosition.X - (PlayerDimention.X / 2),
cameraPosition.Y - (PlayerDimention.Y),
cameraPosition.Z - (PlayerDimention.Z / 2)
),
new Vector3(
cameraPosition.X + (PlayerDimention.X / 2),
cameraPosition.Y,
cameraPosition.Z + (PlayerDimention.Z / 2)
)
);
// Let's check for collision with our boxes
if (CollisionBoxesArray != null)
{
for (int i = 0; i < CollisionBoxesArray.Length; i++)
{
if (CollisionBoxesArray[i].Contains(cameraBox) != ContainmentType.Disjoint) // If our player is inside the collision region
return true;
}
}
if (CollisionSpheresArray != null)
{
// And with our spheres
for (int i = 0; i < CollisionSpheresArray.Length; i++)
{
if (CollisionSpheresArray[i].Contains(cameraBox) != ContainmentType.Disjoint)
return true;
}
}
return false;
}
}
}
I really don't know what to do. Thanks a lot.
EDIT: I updated my post removing some unnecessary code so it's more readable for you.
I also explained better my problem.
I think something like this might work. This is basically the same thing you did, except the previewMove returns a Vector3 that should allow you to slide along the wall. What the function does now is returns a value that checks to see if the camera can move in any of the X, Y, or Z directions of the movement vector, and if the camera can move in the X, Y, and/or Z, it adds that(those) value(s) to the CameraPosition.
private Vector3 previewMove(Vector3 amount)
{
// Create a rotate matrix
Matrix rotate = Matrix.CreateRotationY(CameraRotation.Y);
// Create a movement vector
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
// Return the value of camera position + movement vector
return CameraPosition + new Vector3(
Collision.CheckCollision(CameraPosition + new Vector3(movement.X, 0, 0)) ? 0 : movement.X,
Collision.CheckCollision(CameraPosition + new Vector3(0, movement.Y, 0)) ? 0 : movement.Y,
Collision.CheckCollision(CameraPosition + new Vector3(0, 0, movement.Z)) ? 0 : movement.Z);
}
I haven't tried the code, so I don't know how well this will work. But hopefully it will help.

Categories