Monogame - Making the camera stop when my player collides - c#

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

Related

Move the camera only until a certain position

https://scontent.fmgf1-2.fna.fbcdn.net/v/t34.0-12/15870843_1543174992374352_1359602831_n.gif?oh=dc048c9e04617007c5e82379fd5a9c1a&oe=586CC623
Can you see the blue area in this gif? I want block the camera when the player go more to left, to not see the blue area. This is the code that I'm using to move the camera:
public class configuracoesDaCamera : MonoBehaviour {
Vector3 hit_position = Vector3.zero;
Vector3 current_position = Vector3.zero;
Vector3 camera_position = Vector3.zero;
float z = 0.0f;
public static bool bDedoNoHud;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
hit_position = Input.mousePosition;
camera_position = transform.position;
}
if (Input.GetMouseButton(0))
{
current_position = Input.mousePosition;
LeftMouseDrag();
}
Debug.Log("bDedoNoHud" + bDedoNoHud);
}
void LeftMouseDrag()
{
if (!bDedoNoHud)
{
// From the Unity3D docs: "The z position is in world units from the camera." In my case I'm using the y-axis as height
// with my camera facing back down the y-axis. You can ignore this when the camera is orthograhic.
current_position.z = hit_position.z = camera_position.y;
// Get direction of movement. (Note: Don't normalize, the magnitude of change is going to be Vector3.Distance(current_position-hit_position)
// anyways.
Vector3 direction = Camera.main.ScreenToWorldPoint(current_position) - Camera.main.ScreenToWorldPoint(hit_position);
// Invert direction to that terrain appears to move with the mouse.
direction = direction * -1;
Vector3 position = camera_position + direction;
transform.position = position;
}
}
}
You mean setting up boundaries?
This should help you
Put it in the update()

C# How to make a smooth jump in unity3d without moving the X, towards the nearest object

I would like to make a smooth jump towards the nearest cube. I already have a script to detect the closest cube. I want that the X-axis is locked, so only the Y-axis and the Z-axis change when jumping. I would like to use a Jump animation when jumping. I already tried to use Vector3MoveTowards, but that didn't really work well, maybe I didn't use it properly.
Detect nearest cube where the player should jump to (C#)
void Update()
{
FindClosestCube ();
GameObject closestCube = FindClosestCube ();
Debug.Log (closestCube);
}
GameObject FindClosestCube() {
GameObject[] gos;
gos = GameObject.FindGameObjectsWithTag("cube");
GameObject closest = null;
float distance = Mathf.Infinity;
float position = transform.position.z;
foreach (GameObject go in gos) {
float diff = go.transform.position.z - position;
float curDistance = diff;
if (curDistance < distance) {
closest = go;
distance = curDistance;
}
}
return closest;
}
The tricky part is that at some cubes you have to jump up (y+1), with some cubes you jump towards the same Y (y+0) and with some cubes you jump down (y-1).
How do I do this?
Image of how it looks like:
EDIT: I have this code right now:
----------------C#-----------------
Rigidbody rb;
public int clicks = 0;
Vector3 target;
public Animation jumpAnimation;
bool jump = false;
float cubeDiffY;
bool movePlayer;
public float smoothTime = 0.3f;
public float yVelocity = 0.0f;
void Start()
{
rb = GetComponent<Rigidbody> ();
}
void Update ()
{
FindClosestCube ();
GameObject closestCube = FindClosestCube ();
Debug.Log ("Closestcube = " + closestCube);
target = closestCube.transform.position + new Vector3 (0f, 0.7f, 0f);
cubeDiffY = target.y - transform.position.y;
movePlayer = true;
Debug.Log("Cube Difference Y-axis = " + Mathf.Round(cubeDiffY));
if (Input.GetMouseButtonDown (0))
{
clicks += 1;
jump = true;
jumpAnimation = gameObject.GetComponent<Animation>();
//jumpAnimation.Play ();
}
if (jump == true)
{
Jump ();
}
}
void Jump()
{
float newPosition = Mathf.SmoothDamp (transform.position.y, target.y, ref yVelocity, smoothTime);
transform.position = new Vector3 (0, newPosition, transform.position.z);
}
I calculated the difference in Y-axis between the cube where the player is standing on and the closestCube. But the Jump() doesn't work. How do I fix that?
Okay I set up a quick version of your game and got what you wanted to work, it is not exactly a quick solution, because what your doing doesn't have built in functionality for other than using animations.
Here is the character script that has all the code you need and commented thoroughly so it should explain itself.
using UnityEngine;
public class Character : MonoBehaviour
{
//the collider for the player
private new BoxCollider collider;
//the jump box collider on a empty game object that is a child to the player object
public BoxCollider JumpBox;
//the offset of the cube so it doesn't stop inside of it
public Vector3 cubeOffset;
//how high the jump will be
public float JumpHeight;
//how fast the jump will be
public float JumpSpeed;
//holds the change in position the jump will produce
private Vector3 jumpDelta;
//holds the destination cube the jump is attempting to hit
private Cube destinationCube;
//true if a jumping animation is currently playing
private bool jumping = false;
//used to swap the jump direction from up to down
private bool jumpDirection = true;
//used to hold the position of the jump so it knows when to stop
private float jumpPosition = 0;
// Use this for initialization
void Start()
{
collider = GetComponent<BoxCollider>();
}
// Update is called once per frame
void Update()
{
if(jumping)
{
//move straight towards the cube
transform.position = transform.position + (JumpSpeed * jumpDelta);
//move up and down to simulate a jump
//check the current move direction
if (jumpDirection)
{
//add to the jump position twice product of the JumpHeight the JumpSpeed so that it will
//rise and fall the same amount of time it takes to move to the destination
jumpPosition += JumpHeight * JumpSpeed * 2;
//if it has passed the jump height reverse the jump direction
if (jumpPosition >= JumpHeight)
jumpDirection = !jumpDirection;
transform.position += transform.up * JumpHeight * JumpSpeed * 2;
}
//the jump direction is going down
else
{
jumpPosition -= JumpHeight * JumpSpeed * 2;
transform.position -= transform.up * JumpHeight * JumpSpeed * 2;
}
//check if the character collider intersects witht he cubes collider
//if it has then stop jumping and set the final position as the destination position
if (collider.bounds.Intersects(destinationCube.BoxCollider.bounds))
{
jumping = false;
transform.position = destinationCube.transform.position + cubeOffset;
}
}
//detect a jump
if (Input.GetKeyDown(KeyCode.Space))
{
//detect all hits on the jump box
Collider[] hits = Physics.OverlapBox(JumpBox.center, JumpBox.size * 0.5f);
//get the closest collider with the right tag
Collider result = GetClosestColliderWithTag(hits, "Cube");
//if we have a result then begin the jumping animation
if(result != null)
{
//gets the destination cubes cube component(the custom class you have on your cubes)
destinationCube = result.gameObject.GetComponent<Cube>();
//calculate the jump delta
jumpDelta = (result.transform.position + cubeOffset) - transform.position;
//remove the left and right components so the jumping doesnt move to the left or right of the player
Vector3 component = Vector3.Project(jumpDelta, -transform.right);
jumpDelta -= component;
component = Vector3.Project(jumpDelta, transform.right);
jumpDelta -= component;
//setup the jump animation control fields to the initial values
jumpPosition = 0;
jumpDirection = true;
jumping = true;
}
}
}
private Collider GetClosestColliderWithTag(Collider[] colliders, string tag)
{
//just gets the closest collider
float distance = float.MaxValue;
int result = -1;
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].tag == tag)
{
float distanceTemp = Vector3.Distance(transform.position, colliders[i].transform.position);
if (distanceTemp < distance)
{
distance = distanceTemp;
result = i;
}
}
}
if (result != -1)
return colliders[result];
else return null;
}
}
And here is my cube script which has some things you will need to add
using UnityEngine;
public class Cube : MonoBehaviour {
//these arent important just fields I used to set up a quick version of your game
public GameObject StartPoint;
public GameObject EndPoint;
public float Speed;
private Vector3 directionVector;
private bool direction;
//YOU WILL NEED THIS!!
[HideInInspector]
public BoxCollider BoxCollider;
// Use this for initialization
void Start() {
//not important
directionVector = EndPoint.transform.position - StartPoint.transform.position;
directionVector.Normalize();
//DONT FORGET TO SET YOUR BOX COLLIDER
BoxCollider = GetComponent<BoxCollider>();
}
// Update is called once per frame
void Update()
{
float distance = 0;
if (direction)
{
distance = Vector3.Distance(EndPoint.transform.position, transform.position);
transform.position += directionVector * Speed;
if (distance < Vector3.Distance(EndPoint.transform.position, transform.position))
direction = !direction;
}
else
{
distance = Vector3.Distance(StartPoint.transform.position, transform.position);
transform.position -= directionVector * Speed;
if (distance < Vector3.Distance(StartPoint.transform.position, transform.position))
direction = !direction;
}
}
}
Previous Answer
I would say you need to calculate the perceived position of the object in the future.
Vector3 futurePos = cubePos + (cubeMoveDirection * cubeMoveSpeed);
Once you have the future position, even if it is not exact, you should aim your animation towards that position. To do this I would have the animation change a speed vector instead of an actual transforms position that way we can rotate this speed vector in any direction you want while keeping the orientation of the block. Otherwise you have to rotate the entire block to point towards the direction you want. If this is what you want then put your block under a empty gameobject, rotate the empty gameobject to point to where you want and do the speed calculations only.
Next your animation should have a net move vector which should be pre-calculated and scaled down or up to meet the distance to the future position. It will look something like this(note this is not tested)
//class fields
Vector3 AnimatedSpeed;
Vector3 AnimationDelta;
//basic calculation
//get the direction vector from the players current position to the future
block position
Vector3 dirVector = futurePos - transform.position;
//find the rotation from the current orientation to the direction vector
Quaternion rotation = Quaternion.FromToRotation(transform.forward, dirVector);
//calculate the distance from you to the cube and scale it with the magnitude of the AnimationDelta
float result = Vector3.Distance(transform.position, futurePos);
result = result / animationDelta.magnitude;
//finally rotate the forward vector by the rotation and multiply it by the
//animation speed and the result to get the step by step movement as
//the animation plays. NOTE: The animation should be based on forward direction
transform.position += (AnimationSpeed * rotation) * result * Time.deltaTime;
Hopefully this does it, like I said I haven't tested it at all so you may have to do some tweaking based on your particular case as this is essentially psuedo-code.
Good luck! I'm off to bed I'll check back when I wake up.

Unity C# use Transform.RotateAround with touch to move camera around target

So am looking at changing my Transform.RotateAround script for touch input. If I get rid of the "Input.GetAxis", the camera rotates on its own around the target. I cant seem to figure out how to format the code to get the position of the touch to a string to use with this. If anyone has any tips that would be awesome!
Here is the code:
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera-Control/Mouse Orbit with zoom2")]
public class MouseOrbitImproved2 : MonoBehaviour {
public float speed;
public Transform target;
public float rotateSpeed;
public Transform camera = Camera.main.transform;
public Vector3 camPosition;
public float camSpeed;
public float minDistance;
public float maxDistance;
//private Vector3 moveDirection = Vector3.zero;
//private Vector3 moveDirection = target.position;
public float perspectiveZoomSpeed = 0.5f; // The rate of change of the field of view in perspective mode.
public float orthoZoomSpeed = 0.5f; // The rate of change of the orthographic size in orthographic mode.
void start() {
//camPosition = camera.transform.position;
}
public void Update() {
if (Input.touchCount == 1) {
transform.LookAt(target);
Touch touchSwipe = Input.GetTouch(0);
string position = touchSwipe.deltaPosition;
transform.RotateAround(target.position, Vector3.up, Input.GetAxis(position)* speed);
transform.RotateAround(target.position, Vector3.forward, Input.GetAxis(position)* speed);
}
transform.LookAt(target);
/*
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0)
{
// calculate new position first...
camPosition += transform.forward * scroll * camSpeed;
// then compare to the limits:
float distanceToTarget = Vector3.Distance(target.position, camPosition);
// you can clamp the movement to min and max distances:
if (distanceToTarget > maxDistance){ // clamp at maxDistance...
camPosition = target.position - maxDistance * transform.forward;
}
if (distanceToTarget < minDistance){ // or at minDistance
camPosition = target.position - minDistance * transform.forward;
}
// finally, update the actual camera position:
transform.position = camPosition;
// set camera position
}
*/
// 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;
// If the camera is orthographic...
if (Camera.main.orthographic)
{
// ... change the orthographic size based on the change in distance between the touches.
Camera.main.orthographicSize += deltaMagnitudeDiff * orthoZoomSpeed;
// Make sure the orthographic size never drops below zero.
Camera.main.orthographicSize = Mathf.Max(Camera.main.orthographicSize, 0.1f);
}
else
{
// Otherwise change the field of view based on the change in distance between the touches.
Camera.main.fieldOfView += deltaMagnitudeDiff * perspectiveZoomSpeed;
// Clamp the field of view to make sure it's between 0 and 180.
Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView, 0.1f, 179.9f);
}
}
}
public void ChangeToAxial (){
Camera.main.transform.position = new Vector3 (45.3f,234.5f,66.9f);
}
public void ChangeToSagright (){
Camera.main.transform.position = new Vector3 (28.13f,76.41f,222.68f);
}
public void ChangeToSagleft (){
Camera.main.transform.position = new Vector3 (49.36f,66.85f,-93.78f);
}
public void ChangeToCoronal () {
Camera.main.transform.position = new Vector3 (-71.6f,69.66f,66.29f);
}
}
I think it may be best if you look at this question and answer from the unity forum Unit 5 forum rotating camera with touch. It should work with the Input.GetTouch instead of the Input.GetAxis. Hope that helps. Be aware that script is in Javascript, but you shouldn't need the whole script.

Unity 2D - zoom in =ok, zoom out = borked

I hope someone can help. Im trying to create a little script that zooms in to my player and back out - toggling.
The zoom in works fine, but when I try to zoom back out it doesn't work, it gets stuck. I've created a bool to ensure it only runs the code when it needs to and I'm wondering if that is what's causing the error.
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public float zoom = 10f;
public float normal = 3.471398f;
public float smooth = 5f;
private bool isZoomed = false;
public Camera cam;
public GameObject player;
// lock the camera settings
public float LockedX = 0f;
public float LockedY = 0f;
public float LockedZ = 0f;
private bool hasBeenZoomed = false;
Vector3 targetPos;
private Transform playerTransform;
// Use this for initialization
void Start()
{
targetPos = transform.position;
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("z")) { isZoomed = !isZoomed; }
if (isZoomed == true)
{
ZoomInToPlayer();
hasBeenZoomed = true;
}
else
{
if (hasBeenZoomed)
{
ZoomOutFromPlayer();
hasBeenZoomed = false;
}
}
}
void ZoomInToPlayer()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
float targetX = transform.position.x;
float targetY = transform.position.y;
// ... the target x coordinate should be a Lerp between the camera's current x position and the player's current x position.
targetX = Mathf.Lerp(transform.position.x, playerTransform.position.x, smooth * Time.deltaTime);
//Debug.Log("player x is " + playerTransform.position.x + " and TargetX is " + targetX);
// ... the target y coordinate should be a Lerp between the camera's current y position and the player's current y position.
targetY = Mathf.Lerp(transform.position.y, playerTransform.position.y, smooth * Time.deltaTime);
//Debug.Log("player y is " + playerTransform.position.y+ " and TargetY is " + targetY);
// Set the camera's position to the target position with the same z component.
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
// Change the size of the camera viewport
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, zoom, Time.deltaTime * smooth);
}
void ZoomOutFromPlayer()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
float targetX;
float targetY;
// Change the size of the camera viewport
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, normal, Time.deltaTime * smooth);
// ... the target x coordinate should be a Lerp between the camera's current x position and the original x position.
targetX = Mathf.Lerp(transform.position.x, LockedX, smooth * Time.deltaTime);
// ... the target y coordinate should be a Lerp between the camera's current y position and the original y position.
targetY = Mathf.Lerp(transform.position.y, LockedY, smooth * Time.deltaTime);
// Set the camera's position to the target position with the same z component.
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
}
}
Your methods ZoomInToPlayer and ZoomOutFromPlayer are written in a way that suggests that they should be called once per frame for the duration of the zoom in/out animation. However, ZoomOutFromPlayer will only be called once, because Update, when ZoomOutFromPlayer is called, the hasBeenZoomed is immediately set to false.
What you're trying to do here, essentially, is a simple Finite State Machine. I suggest researching this design pattern a little more — it will help you noticing the sources of such problems and structuring your code in a better way.
In this particular case, a good way to prevent this problem when designing your code would be to write something akin to "API documentation" for yourself, when writing your methods. For ZoomOutFromPlayer, it would read something like this:
Call every frame when you want to perform zoom-out animation, until the animation is complete.
After you written (and read) such a description, you should immediately notice a red flag — "until the animation is complete"? So, the code that calls this method should somehow take track of whether the animation is complete or not, during a separate mechanism? Wouldn't it make it really easy to use this method incorrectly? Well, that's exactly what happened here.
Instead, what you could've done, is to create two different methods, ZoomInUpdate and ZoomOutUpdate, with descriptions that would read something like this:
Call every frame when the camera should be zoomed out/zoomed in.
This way, using this methods is a lot easier, and you can safely throw out additional logic with hasBeenZoomed out. Just call these methods every frame, and ensure (inside these methods) that they change the camera settings with a certain speed, if these settings need to be changed, or otherwise do nothing.
Try this
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public float zoom = 10f;
public float normal = 3.471398f;
public float smooth = 5f;
private bool isZoomed = false;
private bool isZoomFinished = true; // the animation zoom is over ?
public Camera cam;
public GameObject player;
public float LockedX = 0f;
public float LockedY = 0f;
public float LockedZ = 0f;
private bool hasBeenZoomed = false;
Vector3 targetPos;
private Transform playerTransform;
void Start()
{
targetPos = transform.position;
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
if (Input.GetKeyDown("z") && isZoomFinished) {
isZoomed = !isZoomed;
isZoomFinished = false;
}
if (isZoomed && !isZoomFinished)
{
ZoomInToPlayer();
}
else if (!isZoomed && !isZoomFinished)
{
ZoomOutFromPlayer()
}
}
float delta = 0;
void ZoomInToPlayer()
{
delta += smooth * Time.deltaTime;
//Cam size
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, zoom, delta);
//Cam pos
float targetX = transform.position.x;
float targetY = transform.position.y;
targetX = Mathf.Lerp(transform.position.x, playerTransform.position.x, delta);
targetY = Mathf.Lerp(transform.position.y, playerTransform.position.y, delta);
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
// is animation over ?
if(delta >= 1) {
isZoomFinished = true;
delta = 0;
}
}
void ZoomOutFromPlayer()
{
delta += smooth * Time.deltaTime;
//Cam size
cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, normal, delta);
//Cam pos
float targetX;
float targetY;
targetX = Mathf.Lerp(transform.position.x, LockedX, delta);
targetY = Mathf.Lerp(transform.position.y, LockedY, delta);
cam.transform.position = new Vector3(targetX, targetY, transform.position.z);
// is animation over ?
if(delta >= 1) {
isZoomFinished = true;
delta = 0;
}
}
}

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