![Smile :)](./images/smilies/icon_smile.gif)
1: Create a folder in the BepuPhsyicsDemos solution called "CameraComponents"
2: In that folder, create the class "CameraVariables.cs" with the following code:
Code: Select all
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BEPUphysicsDemos.CameraComponents
{
/// <summary>
/// Different modes of camera movement
/// </summary>
public enum CameraMode
{
/// <summary>
/// Non-sprung chase camera, can move the character.
/// </summary>
ChaseStiff,
/// <summary>
/// First person viewpoint, can move the character.
/// </summary>
FirstPersonMovement,
/// <summary>
/// A free moving, physics independent camera
/// </summary>
Free,
/// <summary>
/// Static distance, offset, independent camera, can move the character, is obstruction aware.
/// </summary>
Offset,
}
}
4: Here is the biggest change. I've rearranged the entire camera class to support multiple camera classes by changing the "CameraMode" - a user can just define their own mode and create the function. Notice that code has been rearranged a bit, and the camera movement code from the "CharacterControllerInput" class has been moved into here. Also, instead of just turning on/off chase camera, we use a "ChangeCameraMode" function. To get this to work, just copy and paste into your "Camera.cs"
Code: Select all
using System;
using BEPUphysics;
using BEPUphysics.BroadPhaseEntries;
using BEPUphysics.CollisionRuleManagement;
using BEPUphysics.Entities;
using BEPUutilities;
using Microsoft.Xna.Framework.Input;
using BEPUphysicsDemos.AlternateMovement.Character;
namespace BEPUphysicsDemos.CameraComponents
{
/// <summary>
/// Simple camera class for moving around the demos area.
/// </summary>
public class Camera
{
#region Fields
#region Chase Camera Mode
/// <summary>
/// Chase camera: how high to offset the camera along the entity's up vector.
/// </summary>
private float cameraVerticalOffset = 5.0f;
//The following are used for the chase camera only.
/// <summary>
/// Entity to follow around and point at.
/// </summary>
private Entity entityToFollow;
/// <summary>
/// Offset vector from the center of the target chase entity to look at.
/// </summary>
private Vector3 offsetFromChaseTarget;
/// <summary>
/// Whether or not to transform the offset vector with the rotation of the entity.
/// </summary>
private bool transformOffset;
/// <summary>
/// Distance away from the target entity to try to maintain. The distance will be shorter at times if the ray hits an object.
/// </summary>
private float distanceToTarget;
/// <summary>
/// Offset distance away from hit point when chasing and the ray intersects.
/// </summary>
private const float chaseCameraMargin = 1f;
#endregion
#region Private Variables to Reduce Declarations
/// <summary>
/// Hold variable for what we are looking at with cameras.
/// </summary>
private Vector3 lookAt;
/// <summary>
/// Hold variable for our world matrix backwards.
/// </summary>
private Vector3 backwards;
/// <summary>
/// Hold variable for our camera offset.
/// </summary>
private Vector3 offset;
/// <summary>
/// Hold variable to find the earliest ray hit that isn't the chase target to position the camera appropriately.
/// </summary>
private RayCastResult result;
/// <summary>
/// FirstPersonMovement: Whether or not to smooth out character steps and other discontinuous motion.
/// </summary>
public bool UseCameraSmoothing = true;
#endregion
/// <summary>
/// Gets the game associated with the camera.
/// </summary>
public DemosGame Game
{
get;
private set;
}
/// <summary>
/// Gets or sets the position of the camera.
/// </summary>
public Vector3 Position { get; set; }
/// <summary>
/// Gets or sets the speed at which the camera moves.
/// </summary>
public float Speed { get; set; }
/// <summary>
/// Gets or sets the projection matrix of the camera.
/// </summary>
public Matrix ProjectionMatrix { get; set; }
/// <summary>
/// Gets the view matrix of the camera.
/// </summary>
public Matrix ViewMatrix
{
get { return Matrix.CreateViewRH(Position, viewDirection, lockedUp); }
}
/// <summary>
/// Gets the world transformation of the camera.
/// </summary>
public Matrix WorldMatrix
{
get { return Matrix.CreateWorldRH(Position, viewDirection, lockedUp); }
}
private Vector3 viewDirection;
/// <summary>
/// Gets or sets the view direction of the camera.
/// </summary>
public Vector3 ViewDirection
{
get { return viewDirection; }
set
{
Vector3.Normalize(ref value, out value);
//Validate the input. A temporary violation of the maximum pitch is permitted as it will be fixed as the user looks around.
//However, we cannot allow a view direction parallel to the locked up direction.
float dot;
Vector3.Dot(ref value, ref lockedUp, out dot);
if (Math.Abs(dot) > 1 - Toolbox.BigEpsilon)
throw new ArgumentException("The view direction must not be aligned with the locked up direction.");
viewDirection = value;
}
}
private float maximumPitch = MathHelper.PiOver2 * 0.99f;
/// <summary>
/// Gets or sets how far the camera can look up or down in radians.
/// </summary>
public float MaximumPitch
{
get { return maximumPitch; }
set
{
if (value < 0)
throw new ArgumentException("Maximum pitch corresponds to pitch magnitude; must be positive.");
if (value >= MathHelper.PiOver2)
throw new ArgumentException("Maximum pitch must be less than Pi/2.");
maximumPitch = value;
}
}
private Vector3 lockedUp;
/// <summary>
/// Gets or sets the current locked up vector of the camera.
/// </summary>
public Vector3 LockedUp
{
get { return lockedUp; }
set
{
var oldUp = lockedUp;
Vector3.Normalize(ref value, out lockedUp);
//Move the view direction with the transform. This helps guarantee that the view direction won't end up aligned with the up vector.
Quaternion rotation;
Quaternion.GetQuaternionBetweenNormalizedVectors(ref oldUp, ref lockedUp, out rotation);
Quaternion.Transform(ref viewDirection, ref rotation, out viewDirection);
}
}
/// <summary>
/// Whether or not to use the default free-flight camera controls.
/// Set to false when using vehicles or character controllers.
/// </summary>
public CameraMode cameraMode = CameraMode.Free;
/// <summary>
/// FirstPersonMovement: controller to use
/// </summary>
public CharacterController characterController;
/// <summary>
/// FirstPersonMovement: Offset from the position of the character to the 'eyes' while the character is standing.
/// </summary>
public float StandingCameraOffset = .7f;
/// <summary>
/// FirstPersonMovement: Offset from the position of the character to the 'eyes' while the character is crouching.
/// </summary>
public float CrouchingCameraOffset = .4f;
#endregion
#region Initialization
/// <summary>
/// Creates a camera.
/// </summary>
/// <param name="game">Game used with the camera.</param>
/// <param name="position">Initial position of the camera.</param>
/// <param name="speed">Speed of the camera per second.</param>
/// <param name="pitch">Initial pitch angle of the camera.</param>
/// <param name="yaw">Initial yaw value of the camera.</param>
/// <param name="projectionMatrix">Projection matrix used.</param>
public Camera(DemosGame game, Vector3 position, float speed, float pitch, float yaw, Matrix projectionMatrix)
{
this.Game = game;
Position = position;
Speed = speed;
Yaw(yaw);
Pitch(pitch);
ProjectionMatrix = projectionMatrix;
rayCastFilter = RayCastFilter;
}
#endregion
#region Support
/// <summary>
/// Sets up all the information required by the chase camera.
/// </summary>
/// <param name="target">Target to follow.</param>
/// <param name="offset">Offset from the center of the entity target to point at.</param>
/// <param name="transform">Whether or not to transform the offset with the target entity's rotation.</param>
/// <param name="distance">Distance from the target position to try to maintain.</param>
public void ChangeCameraMode(CameraMode mode, Entity target, Vector3 offset, bool transform, float distance)
{
cameraMode = mode;
entityToFollow = target;
offsetFromChaseTarget = offset;
transformOffset = transform;
distanceToTarget = distance;
switch (mode)
{
case CameraMode.ChaseStiff:
// This changes our camera's up vector to allow full screen rotation. Comment out to keep in original up.
LockedUp = target.BufferedStates.InterpolatedStates.OrientationMatrix.Up;
break;
case CameraMode.FirstPersonMovement:
lockedUp = Vector3.Up;
LockedUp = Vector3.Up;
break;
case CameraMode.Free:
lockedUp = Vector3.Up;
LockedUp = Vector3.Up;
break;
case CameraMode.Offset:
lockedUp = Vector3.Up;
LockedUp = Vector3.Up;
break;
}
}
//The raycast filter limits the results retrieved from the Space.RayCast while in chase camera mode.
Func<BroadPhaseEntry, bool> rayCastFilter;
bool RayCastFilter(BroadPhaseEntry entry)
{
return entry != entityToFollow.CollisionInformation && (entry.CollisionRules.Personal <= CollisionRule.Normal);
}
/// <summary>
/// Moves the camera forward.
/// </summary>
/// <param name="distance">Distance to move.</param>
public void MoveForward(float distance)
{
Position += WorldMatrix.Forward * distance;
}
/// <summary>
/// Moves the camera to the right.
/// </summary>
/// <param name="distance">Distance to move.</param>
public void MoveRight(float distance)
{
Position += WorldMatrix.Right * distance;
}
/// <summary>
/// Moves the camera up.
/// </summary>
/// <param name="distance">Distanec to move.</param>
public void MoveUp(float distance)
{
Position += new Vector3(0, distance, 0);
}
/// <summary>
/// Rotates the camera around its locked up vector.
/// </summary>
/// <param name="radians">Amount to rotate.</param>
public void Yaw(float radians)
{
//Rotate around the up vector.
Matrix3x3 rotation;
Matrix3x3.CreateFromAxisAngle(ref lockedUp, radians, out rotation);
Matrix3x3.Transform(ref viewDirection, ref rotation, out viewDirection);
//Avoid drift by renormalizing.
viewDirection.Normalize();
}
/// <summary>
/// Rotates the view direction up or down relative to the locked up vector.
/// </summary>
/// <param name="radians">Amount to rotate.</param>
public void Pitch(float radians)
{
//Do not allow the new view direction to violate the maximum pitch.
float dot;
Vector3.Dot(ref viewDirection, ref lockedUp, out dot);
//While this could be rephrased in terms of dot products alone, converting to actual angles can be more intuitive.
//Consider +Pi/2 to be up, and -Pi/2 to be down.
float currentPitch = (float)Math.Acos(MathHelper.Clamp(-dot, -1, 1)) - MathHelper.PiOver2;
//Compute our new pitch by clamping the current + change.
float newPitch = MathHelper.Clamp(currentPitch + radians, -maximumPitch, maximumPitch);
float allowedChange = newPitch - currentPitch;
//Compute and apply the rotation.
Vector3 pitchAxis;
Vector3.Cross(ref viewDirection, ref lockedUp, out pitchAxis);
//This is guaranteed safe by all interaction points stopping viewDirection from being aligned with lockedUp.
pitchAxis.Normalize();
Matrix3x3 rotation;
Matrix3x3.CreateFromAxisAngle(ref pitchAxis, allowedChange, out rotation);
Matrix3x3.Transform(ref viewDirection, ref rotation, out viewDirection);
//Avoid drift by renormalizing.
viewDirection.Normalize();
}
#endregion
#region Update
/// <summary>
/// Updates the state of the camera.
/// </summary>
/// <param name="dt">Time since the last frame in seconds.</param>
/// <param name="keyboardInput">Input for this frame from the keyboard.</param>
/// <param name="mouseInput">Input for this frame from the mouse.</param>
/// <param name="gamePadInput">Input for this frame from the game pad.</param>
#if !WINDOWS
public void Update(float dt, KeyboardState keyboardInput, GamePadState gamePadInput)
{
if (cameraMode != CameraMode.ChaseStiff)
{
Yaw += gamePadInput.ThumbSticks.Right.X * -1.5f * dt;
Pitch += gamePadInput.ThumbSticks.Right.Y * 1.5f * dt;
}
#else
public void Update(float dt, KeyboardState keyboardInput, MouseState mouseInput, GamePadState gamePadInput)
{
//Only turn if the mouse is controlled by the game.
if (!Game.IsMouseVisible &&
cameraMode != CameraMode.ChaseStiff)
{
Yaw((200 - mouseInput.X) * dt * .12f);
Pitch((200 - mouseInput.Y) * dt * .12f);
}
#endif
switch (cameraMode)
{
case CameraMode.ChaseStiff:
UpdateCameraChaseStiff();
break;
case CameraMode.FirstPersonMovement:
UpdateCameraFirstPersonMovement(dt);
break;
case CameraMode.Free:
#if !WINDOWS
UpdateCameraFree(dt, keyboardInput, gamepadInput);
#else
UpdateCameraFree(dt, keyboardInput, mouseInput, gamePadInput);
#endif
break;
case CameraMode.Offset:
UpdateCameraOffset();
break;
}
}
#if !WINDOWS
/// <summary>
/// Freely moving camera.
/// </summary>
public void UpdateCameraFree(float dt, KeyboardState keyboardInput, GamePadState gamePadInput)
{
#else
/// <summary>
/// Freely moving camera.
/// </summary>
/// <param name="dt"></param>
/// <param name="keyboardInput"></param>
/// <param name="mouseInput"></param>
/// <param name="gamePadInput"></param>
public void UpdateCameraFree(float dt, KeyboardState keyboardInput, MouseState mouseInput, GamePadState gamePadInput)
{
#endif
//Only move around if the camera has control over its own position.
float distance = Speed * dt;
#if !WINDOWS
MoveForward(gamePadInput.ThumbSticks.Left.Y * distance);
MoveRight(gamePadInput.ThumbSticks.Left.X * distance);
if (gamePadInput.IsButtonDown(Buttons.LeftStick))
MoveUp(distance);
if (gamePadInput.IsButtonDown(Buttons.RightStick))
MoveUp(-distance);
#endif
if (keyboardInput.IsKeyDown(Keys.E))
MoveForward(distance);
if (keyboardInput.IsKeyDown(Keys.D))
MoveForward(-distance);
if (keyboardInput.IsKeyDown(Keys.S))
MoveRight(-distance);
if (keyboardInput.IsKeyDown(Keys.F))
MoveRight(distance);
if (keyboardInput.IsKeyDown(Keys.A))
MoveUp(distance);
if (keyboardInput.IsKeyDown(Keys.Z))
MoveUp(-distance);
}
/// <summary>
/// Taken from the character controller input class
/// </summary>
private void UpdateCameraFirstPersonMovement(float dt)
{
//Note that the character controller's update method is not called here; this is because it is handled within its owning space.
//This method's job is simply to tell the character to move around based on the Camera and input.
////Rotate the camera of the character based on the support velocity, if a support with velocity exists.
////This can be very disorienting in some cases; that's why it is off by default!
//if (CharacterController.SupportFinder.HasSupport)
//{
// SupportData? data;
// if (CharacterController.SupportFinder.HasTraction)
// data = CharacterController.SupportFinder.TractionData;
// else
// data = CharacterController.SupportFinder.SupportData;
// EntityCollidable support = data.Value.SupportObject as EntityCollidable;
// if (support != null && !support.Entity.IsDynamic) //Having the view turned by dynamic entities is extremely confusing for the most part.
// {
// float dot = Vector3.Dot(support.Entity.AngularVelocity, CharacterController.Body.OrientationMatrix.Up);
// Camera.Yaw += dot * dt;
// }
//}
if (UseCameraSmoothing)
{
//First, find where the camera is expected to be based on the last position and the current velocity.
//Note: if the character were a free-floating 6DOF character, this would need to include an angular velocity contribution.
//And of course, the camera orientation would be based on the character's orientation.
Position = Position + characterController.Body.LinearVelocity * dt;
//Now compute where it should be according the physical body of the character.
Vector3 up = characterController.Body.OrientationMatrix.Up;
Vector3 bodyPosition = characterController.Body.BufferedStates.InterpolatedStates.Position;
Vector3 goalPosition = bodyPosition + up * (characterController.StanceManager.CurrentStance == Stance.Standing ? StandingCameraOffset : CrouchingCameraOffset);
//Usually, the camera position and the goal will be very close, if not matching completely.
//However, if the character steps or has its position otherwise modified, then they will not match.
//In this case, we need to correct the camera position.
//To do this, first note that we can't correct infinite errors. We need to define a bounding region that is relative to the character
//in which the camera can interpolate around. The most common discontinuous motions are those of upstepping and downstepping.
//In downstepping, the character can teleport up to the character's MaximumStepHeight downwards.
//In upstepping, the character can teleport up to the character's MaximumStepHeight upwards, and the body's CollisionMargin horizontally.
//Picking those as bounds creates a constraining cylinder.
Vector3 error = goalPosition - Position;
float verticalError = Vector3.Dot(error, up);
Vector3 horizontalError = error - verticalError * up;
//Clamp the vertical component of the camera position within the bounding cylinder.
if (verticalError > characterController.StepManager.MaximumStepHeight)
{
Position -= up * (characterController.StepManager.MaximumStepHeight - verticalError);
verticalError = characterController.StepManager.MaximumStepHeight;
}
else if (verticalError < -characterController.StepManager.MaximumStepHeight)
{
Position -= up * (-characterController.StepManager.MaximumStepHeight - verticalError);
verticalError = -characterController.StepManager.MaximumStepHeight;
}
//Clamp the horizontal distance too.
float horizontalErrorLength = horizontalError.LengthSquared();
float margin = characterController.Body.CollisionInformation.Shape.CollisionMargin;
if (horizontalErrorLength > margin * margin)
{
Vector3 previousHorizontalError = horizontalError;
Vector3.Multiply(ref horizontalError, margin / (float)Math.Sqrt(horizontalErrorLength), out horizontalError);
Position -= horizontalError - previousHorizontalError;
}
//Now that the error/camera position is known to lie within the constraining cylinder, we can perform a smooth correction.
//This removes a portion of the error each frame.
//Note that this is not framerate independent. If fixed time step is not enabled,
//a different smoothing method should be applied to the final error values.
//float errorCorrectionFactor = .3f;
//This version is framerate independent, although it is more expensive.
float errorCorrectionFactor = (float)(1 - Math.Pow(.000000001, dt));
Position += up * (verticalError * errorCorrectionFactor);
Position += horizontalError * errorCorrectionFactor;
}
else
{
Position = characterController.Body.Position + (characterController.StanceManager.CurrentStance == Stance.Standing ? StandingCameraOffset : CrouchingCameraOffset) * characterController.Body.OrientationMatrix.Up;
}
}
/// <summary>
/// Chase camera locked to a set distance from the chase object.
/// </summary>
private void UpdateCameraChaseStiff()
{
// CHASE STIFF
if (transformOffset)
offset = Matrix3x3.Transform(offsetFromChaseTarget, entityToFollow.BufferedStates.InterpolatedStates.OrientationMatrix);
else
offset = offsetFromChaseTarget;
lookAt = entityToFollow.BufferedStates.InterpolatedStates.Position + offset;
// Set camera behind the look at
Position = lookAt + (entityToFollow.BufferedStates.InterpolatedStates.OrientationMatrix.Backward * 20);
// This changes our camera's up vector to allow full screen rotation. Comment out to keep in original up.
LockedUp = entityToFollow.BufferedStates.InterpolatedStates.OrientationMatrix.Up;
// elevate camera by up vector
Position += (entityToFollow.BufferedStates.InterpolatedStates.OrientationMatrix.Up * cameraVerticalOffset);
// TEMP test offset when intersecting object
Vector3 dir = Vector3.Normalize(Position - lookAt);
float dist = Vector3.Distance(Position, lookAt);
if (entityToFollow.Space.RayCast(new Ray(lookAt, dir), dist, rayCastFilter, out result))
{
Position = result.HitData.Location; //lookAt + (Math.Max(result.HitData.T - chaseCameraMargin, 0)) * backwards; //Put the camera just before any hit spot.
}
// set our view direction
viewDirection = Vector3.Normalize(lookAt - Position);
}
/// <summary>
/// Offset, freely rotatable camera at a fixed distance.
/// </summary>
private void UpdateCameraOffset()
{
// OFFSET MODE - TODO reset up to 0,1,0
if (transformOffset)
offset = Matrix3x3.Transform(offsetFromChaseTarget, entityToFollow.BufferedStates.InterpolatedStates.OrientationMatrix);
else
offset = offsetFromChaseTarget;
lookAt = entityToFollow.BufferedStates.InterpolatedStates.Position + offset;
backwards = -viewDirection;
//Find the earliest ray hit that isn't the chase target to position the camera appropriately.
RayCastResult result;
if (entityToFollow.Space.RayCast(new Ray(lookAt, backwards), distanceToTarget, rayCastFilter, out result))
{
Position = lookAt + (Math.Max(result.HitData.T - chaseCameraMargin, 0)) * backwards; //Put the camera just before any hit spot.
}
else
Position = lookAt + (Math.Max(distanceToTarget - chaseCameraMargin, 0)) * backwards;
}
#endregion
}
}
Code: Select all
using BEPUphysics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Diagnostics;
using BEPUphysics.BroadPhaseEntries.MobileCollidables;
using ConversionHelper;
using BEPUutilities;
using BEPUphysicsDemos.CameraComponents;
namespace BEPUphysicsDemos.AlternateMovement.Character
{
/// <summary>
/// Handles input and movement of a character in the game.
/// Acts as a simple 'front end' for the bookkeeping and math of the character controller.
/// </summary>
public class CharacterControllerInput
{
/// <summary>
/// Camera to use for input.
/// </summary>
public Camera Camera;
/// <summary>
/// Physics representation of the character.
/// </summary>
public CharacterController CharacterController;
/// <summary>
/// Whether or not to use the character controller's input.
/// </summary>
public bool IsActive = true;
/// <summary>
/// Owning space of the character.
/// </summary>
public Space Space { get; private set; }
/// <summary>
/// Constructs the character and internal physics character controller.
/// </summary>
/// <param name="owningSpace">Space to add the character to.</param>
/// <param name="cameraToUse">Camera to attach to the character.</param>
public CharacterControllerInput(Space owningSpace, Camera cameraToUse)
{
// Controller to move around with this input
CharacterController = new CharacterController();
// Assign to camera to use in movement
cameraToUse.characterController = CharacterController;
// Track our space
Space = owningSpace;
Space.Add(CharacterController);
Camera = cameraToUse;
Deactivate();
}
/// <summary>
/// Gives the character control over the Camera and movement input.
/// </summary>
public void Activate()
{
if (!IsActive)
{
IsActive = true;
Space.Add(CharacterController);
Camera.ChangeCameraMode(CameraMode.FirstPersonMovement, null, Vector3.Zero, false, 0.0f);
CharacterController.Body.Position = Camera.Position - new Vector3(0, Camera.StandingCameraOffset, 0);
}
}
/// <summary>
/// Returns input control to the Camera.
/// </summary>
public void Deactivate()
{
if (IsActive)
{
IsActive = false;
Camera.cameraMode = CameraMode.Free;
Space.Remove(CharacterController);
}
}
/// <summary>
/// Handles the input and movement of the character.
/// </summary>
/// <param name="dt">Time since last frame in simulation seconds.</param>
/// <param name="previousKeyboardInput">The last frame's keyboard state.</param>
/// <param name="keyboardInput">The current frame's keyboard state.</param>
/// <param name="previousGamePadInput">The last frame's gamepad state.</param>
/// <param name="gamePadInput">The current frame's keyboard state.</param>
public void Update(float dt, KeyboardState previousKeyboardInput, KeyboardState keyboardInput, GamePadState previousGamePadInput, GamePadState gamePadInput)
{
if (IsActive)
{
Vector2 totalMovement = Vector2.Zero;
#if XBOX360
totalMovement += new Vector2(gamePadInput.ThumbSticks.Left.X, gamePadInput.ThumbSticks.Left.Y);
CharacterController.HorizontalMotionConstraint.SpeedScale = Math.Min(totalMovement.Length(), 1); //Don't trust the game pad to output perfectly normalized values.
CharacterController.HorizontalMotionConstraint.MovementDirection = totalMovement;
CharacterController.StanceManager.DesiredStance = gamePadInput.IsButtonDown(Buttons.RightStick) ? Stance.Crouching : Stance.Standing;
//Jumping
if (previousGamePadInput.IsButtonUp(Buttons.LeftStick) && gamePadInput.IsButtonDown(Buttons.LeftStick))
{
CharacterController.Jump();
}
#else
//Collect the movement impulses.
if (keyboardInput.IsKeyDown(Keys.E))
{
totalMovement += new Vector2(0, 1);
}
if (keyboardInput.IsKeyDown(Keys.D))
{
totalMovement += new Vector2(0, -1);
}
if (keyboardInput.IsKeyDown(Keys.S))
{
totalMovement += new Vector2(-1, 0);
}
if (keyboardInput.IsKeyDown(Keys.F))
{
totalMovement += new Vector2(1, 0);
}
if (totalMovement == Vector2.Zero)
CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Zero;
else
CharacterController.HorizontalMotionConstraint.MovementDirection = Vector2.Normalize(totalMovement);
CharacterController.StanceManager.DesiredStance = keyboardInput.IsKeyDown(Keys.Z) ? Stance.Crouching : Stance.Standing;
//Jumping
if (previousKeyboardInput.IsKeyUp(Keys.A) && keyboardInput.IsKeyDown(Keys.A))
{
CharacterController.Jump();
}
#endif
CharacterController.ViewDirection = Camera.WorldMatrix.Forward;
}
}
}
}
Code: Select all
/// <summary>
/// Gives the vehicle control over the camera and movement input.
/// </summary>
public void Activate()
{
if (!IsActive)
{
IsActive = true;
//Put the vehicle where the camera is.
Vehicle.Body.Position = Camera.Position - CameraOffset;
Vehicle.Body.LinearVelocity = Vector3.Zero;
Vehicle.Body.AngularVelocity = Vector3.Zero;
Vehicle.Body.Orientation = Quaternion.Identity;
Camera.ChangeCameraMode(CameraMode.ChaseStiff, Vehicle.Body, new Vector3(0, .6f, 0), true, 10);
}
}
/// <summary>
/// Returns input control to the camera.
/// </summary>
public void Deactivate()
{
if (IsActive)
{
IsActive = false;
Camera.cameraMode = CameraMode.Free;
}
}
8: You should now be able to create your own camera update modes and functions.
Does this interest you or work for you? I ask because it's time consuming to change the camera modes every time I get a new branch
![Smile :)](./images/smilies/icon_smile.gif)
You may want to revisit how I use the "LockedUp" function/variable.
Thanks! Great work again!