Smooth Collision (Wall sliding) - c#

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.

Related

Unity/C# - Cannot move backwards

I'm new to Unity/C#, working on a script for PS4 controller, to get my character to be able to move in any directions using left stick and aim using right stick.
I used raycast to prevent my avatar from going through walls; that solved my issue of walking directly through walls.
However, when I aimed with right stick and moved in an opposite direction with my left, it still passed through the walls.
To tackle that, I adjusted the raycast parameter by adding the direction of the left stick (movement).
That worked, but it gave birth to a bug - I couldn't move backwards directly at all.
Diagonal-backs were fine, but I simply cannot move backwards in an open space.
Here is my code:
using System.Collections; using System.Collections.Generic; using UnityEngine;
public class NewMove : MonoBehaviour
{
Rigidbody m_Rigidbody;
public float m_Speed = 10f;
public float DashStamina = 100f;
void Start()
{
//Fetch the Rigidbody from the GameObject with this script attached
m_Rigidbody = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
// Left Stick Input - Movement
float HorizontalMovementInput = Input.GetAxis("LStickHorizontal");
float VerticalMovementInput = Input.GetAxis("LStickVertical");
// Right Stick Input - Aim
float RSHorizontalMovementInput = Input.GetAxis("RStickHorizontal");
float RSVerticalMovementInput = Input.GetAxis("RStickVertical");
Vector3 CharDirection = new Vector3(HorizontalMovementInput, 0, VerticalMovementInput);
if (Input.GetButton("SubMovement") && DashStamina > 1)
// Dash speed
{
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position + CharDirection, fwd, 3) == false)
{
//Store user input as a movement vector
Vector3 m_Input = new Vector3(Input.GetAxis("LStickHorizontal"), 0, Input.GetAxis("LStickVertical"));
//Apply the movement vector to the current position, which is //multiplied by deltaTime and speed for a smooth MovePosition
m_Rigidbody.MovePosition(transform.position + m_Input * Time.fixedDeltaTime * (m_Speed * 2));
DashStamina -= 0.5f;
}
}
else
// Normal running speed
{
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position + CharDirection, fwd, 3) == false)
{
Vector3 m_Input = new Vector3(Input.GetAxis("LStickHorizontal"), 0, Input.GetAxis("LStickVertical"));
m_Rigidbody.MovePosition(transform.position + m_Input * Time.fixedDeltaTime * m_Speed);
DashStamina += 0.05f;
}
}
//Faces Archer to last direction of left stick
transform.LookAt(transform.position + CharDirection);
//Faces Archer to aiming direction of right stick; will override above
Vector3 CharAim = new Vector3(RSHorizontalMovementInput, 0, RSVerticalMovementInput);
transform.LookAt(transform.position + CharAim);
DashStamina = Mathf.Clamp(DashStamina, 0, 100);
}
}
Please advise.

Jitter when moving around a circular path

This is a pretty strange bug. I created the following script intended to calculate positions to shoot projectiles in my game by moving around a circular path and facing the mouse:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProjectileFire : MonoBehaviour
{
[System.Serializable]
public class ButtonDirectionSet {
public string button;
public float direction;
public bool degrees = true;
}
public GameObject projectile;
public Vector2 centerPoint;
public float radius = 1.0f;
public string fireButton = "Fire1";
public bool followMouse = false;
public bool followKeyboard = true;
public List<ButtonDirectionSet> buttonDirectionMap = new List<ButtonDirectionSet>();
public bool degrees = true;
public float direction;
private readonly float maxDirection = 360 * Mathf.Deg2Rad;
// Start is called before the first frame update
void Start()
{
foreach (ButtonDirectionSet set in buttonDirectionMap) {
if (set.degrees) {
set.direction *= Mathf.Deg2Rad;
}
}
if (degrees) {
direction *= Mathf.Deg2Rad;
}
}
void Update()
{
foreach (ButtonDirectionSet set in buttonDirectionMap) {
if (Input.GetButtonDown(set.button)) {
direction = set.direction;
}
}
if ((followKeyboard && followMouse && Input.GetMouseButton(0)) || !followKeyboard) {
Vector3 mouse = Input.mousePosition;
mouse.z = transform.position.z - Camera.main.transform.position.z;
Vector3 mousePosition = transform.InverseTransformPoint(Camera.main.ScreenToWorldPoint(mouse));
float xPos = mousePosition.x - centerPoint.x;
if (Mathf.Abs(xPos) < 0.1f)
{
xPos = 0.1f * Mathf.Sign(xPos);
}
float yPos = mousePosition.y - centerPoint.y;
if (Mathf.Abs(yPos) < 0.1f)
{
yPos = 0.1f * Mathf.Sign(yPos);
}
direction = Mathf.Atan2(yPos, xPos);
}
if (direction < 0) direction += maxDirection;
transform.localPosition = centerPoint + (new Vector2(Mathf.Cos(direction), Mathf.Sin(direction)) * radius);
}
}
If you set both "follow keyboard" and "follow mouse" to true, the object should be positioned toward the mouse when you hold down the left click button. For the most part, this works, but for some reason, there's jitter in the sense that the object rapidly changes between two different positions for a period of time, even when I don't move the mouse at all. Additionally, the "direction" value switches itself at the same rate, even if I don't move my mouse at all. This does not happen all the time, but it does happen pretty frequently at times that appear to be random.
Is there anything I can do to mitigate or eliminate this behavior?
Since the direction also jitters, there is something wrong with the way you calculate the direction.
The reason your direction jitters is because of the transform.InverseTransformPoint. Since the position of the object changed, the position of the mouse relative to the object also changed with every movement so the direction also changed.
Here are two ways to do it:
1:
Vector2 direction;
if ((followKeyboard && followMouse && Input.GetMouseButton(0)) || !followKeyboard)
{
Vector3 mouseOnScreen = Camera.main.ScreenToWorldPoint(Input.mousePosition) - new Vector3(centerPoint.x, centerPoint.y, 0);
direction = new Vector2(mouseOnScreen.x, mouseOnScreen.y);
}
transform.localPosition = centerPoint + (direction.normalized * radius);
This is a simple way to do it without using angles. It gets the position of the mouse on screen in world space. Then it makes it into a vector 2 so the z value doesn't affect it. Then, it sets the new position to the center plus the direction to the mouse with a magnitude of the radius
2:
if ((followKeyboard && followMouse && Input.GetMouseButton(0)) || !followKeyboard)
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - new Vector3(centerPoint.x, centerPoint.y, 0);
direction = Mathf.Atan2(mousePosition.y, mousePosition.x);
}
transform.localPosition = centerPoint + (new Vector2(Mathf.Cos(direction), Mathf.Sin(direction)) * radius);
This is the same way you did it but a little cleaned up. Instead of using transform.InverseTransformPoint, this way subtracts the centerPoint. That returns the vector from the centerPoint to the mouse, setting the centerPoint as the origin. This is also in the firsts solution.

Change location of pivot based on Input.GetTouch.position to zoom in

So i am trying so to make a script for zooming in and out on a ui element(image), and so far i am doing it by scaling the image based on differences in magnitudes between touches from frame to frame, the only problem is that is zooms from where the pivot is.
The solution would be to move the pivot to the point the is the middle of the line that connects the touches. I tried but it puts my pivot way out of the screen due to the fact that i get values greater than 1 from the formula i used.
Basically, i don't know how to move the pivot so it matches the point the would be the middle of the line that connects the touches.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Zoom : MonoBehaviour
{
public GameObject image1, image2;
public RectTransform tran1, tran2;
public float zoomSpeed = 0.0090f;
public bool startZooming = false;
void Start()
{
tran1 = image1.GetComponent<RectTransform>();
tran2 = image2.GetComponent<RectTransform>();
}
void Update()
{
if(Input.touchCount == 0)
{
//Remember if the player is zooming to be able to change pivot point
startZooming = false;
}
// If there are two touches on the device...
if (Input.touchCount == 2)
{
// Store both touches.
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
//Find pivot point, the middle of the line that connects the touch points
if (deltaMagnitudeDiff < 0 && startZooming == false)
{
float xpivot, ypivot;
xpivot = (touchZero.position.x + touchOne.position.x) / 2;
ypivot = (touchOne.position.y + touchZero.position.y) / 2;
tran1.pivot = new Vector2(xpivot, ypivot);
tran2.pivot = new Vector2(xpivot, ypivot);
startZooming = true; // player is currently zooming, don't change the pivot point
}
float x, y;
x = tran1.localScale.x - deltaMagnitudeDiff * zoomSpeed;
y = tran1.localScale.y - deltaMagnitudeDiff * zoomSpeed;
// Make sure the localScale size never goes below 1 or above 5
x = Mathf.Clamp(x, 1.0f, 5.0f);
y = Mathf.Clamp(y, 1.0f, 5.0f);
// ... change the localScale size based on the change in distance between the touches.
tran1.localScale = new Vector3(x, y, tran1.localScale.z);
tran2.localScale = new Vector3(x, y, tran2.localScale.z);
}
}
}
The RectTrasnform.pivot you are trying to set is
The normalized position in this RectTransform that it rotates around.
this would be a vector between 0,0 and 1,1,!
You can use ScaleAroundRelative from this post
public static void ScaleAround(Transform target, Vector3 pivotInWorldSpace, Vector3 newScale)
{
// pivot
var localPivot = target.InverseTransformPoint(pivotInWorldSpace);
// diff from object pivot to desired pivot/origin
Vector3 pivotDelta = target.transform.localPosition - pivot;
Vector3 scaleFactor = new Vector3(
newScale.x / target.transform.localScale.x,
newScale.y / target.transform.localScale.y,
newScale.z / target.transform.localScale.z );
pivotDelta.Scale(scaleFactor);
target.transform.localPosition = pivot + pivotDelta;
//scale
target.transform.localScale = newScale;
}
then I would implement it using something like
private float initialTouchDistance;
private Vector2 pivot1;
private Vector2 pivot2;
private Vector2 initialScale1;
private Vector2 initialScale2;
void Update()
{
// If there are two touches on the device...
if (Input.touchCount == 2)
{
// Store both touches.
var touchZero = Input.GetTouch(0);
var touchOne = Input.GetTouch(1);
var touchZeroPosition = touchZero.position;
var touchOnePosition = touchOne.position;
var currentDistance = Vector2.Distance(touchZeroPosition, touchOnePosition);
// Is this a new zoom process? => One of the two touches was added this frame
if(touchZero.phase == TouchPhase.Began || touchOne.phase == TouchPhase.Began)
{
// initialize values
initialTouchDistance = currentDistance;
initialScale1 = tran1.localScale;
initialScale2 = tran2.localScale;
// get center between the touches
// THIS IS STILL IN PIXEL SPACE
var zoomPivot = (touchZeroPosition + touchOnePosition) / 2f;
// Get the position on the RectTransforms planes in WORLD SPACE
RectTransformUtility.ScreenPointToWorldPointInRectangle(tran1, zoomPivot, Camera.main, out pivot1));
RectTransformUtility.ScreenPointToWorldPointInRectangle(tran2, zoomPivot, Camera.main, out pivot2));
}
// Is this an already running zoom process and at least one of the touches was moved?
else if(touchZero.phase == TouchPhase.Moved || touchOne.phase == TouchPhase.Moved)
{
// Scale factor
var factor = (currentDistance / initialTouchDistance) * zoomSpeed;
factor = Mathf.Clamp(factor, 1, 5);
var scaleVector1 = Vector3.Scale(new Vector3(factor, factor, 1), initialScale1);
var scaleVector2 = Vector3.Scale(new Vector3(factor, factor, 1), initialScale2);
// apply scaled around pivot
ScaleAround(tran1, pivot1, scaleVector1);
ScaleAround(tran2, pivot2, scaleVector2);
}
}
}
Note: Typed on smartphone but I hope the idea gets clear

Making Field of view using Raycasts With for loops (Using Unity C#)

Code:
void IdleState ()
{
RaycastHit hit;
for (float i = -ViewWidth; i < ViewWidth; i++)
{
float Iterater = i/20;
if (Physics.Raycast(transform.position, transform.forward + new Vector3(Iterater,0,0), out hit, ViewRange))
{
Debug.DrawRay(transform.position,(transform.forward + new Vector3(Iterater,0,0)).normalized * ViewRange, Color.red);
if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Player"))
{
FoundPlayer(hit.transform);
}
}
}
}
Problem: In Unity I am trying to create a Field of view for an enemy AI by drawing multiple raycasts with for loops. For some reason the raycasts do this:
GIF
I've been trying to fix this for days Please help!
Also the FoundPlayer() function in case you need it:
void FoundPlayer (Transform DetectedObject)
{
float step = TurnSpeed * Time.deltaTime;
Vector3 Direc = DetectedObject.position - transform.position;
Vector3 RotMath = Vector3.RotateTowards(transform.forward, Direc,step,0.0f);
transform.rotation = Quaternion.LookRotation(RotMath);
Vector3 CurrentRot = transform.eulerAngles;
transform.rotation = Quaternion.Euler(0,CurrentRot.y,0);
}
The FoundPlayer() function just rotates the enemy towards the player when one of the raycasts hit it.
Regarding Raycast:
transform.Forward is in world-space coordinates, to which you add some fraction of world-space X-axis offset. What you wanted is to add some fraction of 'local-space' X-axis. transform.Right is the world-space conversion of local-space X-axis. Try:
var rayDirection = (transform.Forward + (Vector3.Scale(transform.Right, new Vector3(Iterater, 0, 0))).normalized;
Regarding Fields of View:
If all you want to do is check what objects are within a FOV, start finding everything within a sphere, then filter those objects to what's within appropriate angles from transform.forward:
float ViewRange = 10;
float hHalfFov = 45; // Horizontal Half-Field of View (Degrees)
float vHalfFov = 45; // Vertical Half-Field of View (Degrees)
void IdleState() {
// Find all colliders within ViewRange
var hits = Physics.OverlapSphere(transform.position, ViewRange);
foreach (var hit in hits) {
if ( hit.gameObject == this.gameObject ) continue; // don't hit self
// Check FOV
var direction = (transform.position - hit.transform.position);
var hDirn = Vector3.ProjectOnPlane(direction, transform.up).normalized; // Project onto transform-relative XY plane to check hFov
var vDirn = Vector3.ProjectOnPlane(direction, transform.right).normalized; // Project onto transform-relative YZ plane to check vFov
var hOffset = Vector3.Dot(hDirn, transform.forward) * Mathf.Rad2Deg; // Calculate horizontal angular offset in Degrees
var vOffset = Vector3.Dot(vDirn, transform.forward) * Mathf.Rad2Deg; // Calculate vertical angular offset in Degrees
if (hOffset > hHalfFov || vOffset > vHalfFov) continue; // Outside of FOV
Debug.DrawLine(transform.position, hit.transform.position, Color.red);
if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Player")) {
FoundPlayer(hit.transform);
}
}
}

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);

Categories