我有几个报价给你。这是一个 youtube 视频,展示了基本的相机控制。
https://www.youtube.com/watch?v=OO_yNzAuDe4
这是我的游戏中正在使用的一些当前代码:
using SharpDX;
using System;
namespace VoidwalkerEngine.Framework.DirectX
{
public enum CameraMode
{
FreeLook,
Orbit
}
public class Camera
{
/// <summary>
/// The name of this camera
/// </summary>
public string Name { get; set; }
/// <summary>
/// The eye location of this camera
/// </summary>
public Vector3 Location { get; set; }
/// <summary>
/// The Pitch of this Camera, as Radians
/// </summary>
public float Pitch { get; set; }
/// <summary>
/// The Yaw of this Camera, as Radians
/// </summary>
public float Yaw { get; set; }
/// <summary>
/// The Roll of this Camera, as Radians
/// </summary>
public float Roll { get; set; }
/// <summary>
/// The NearZ of this Camera
/// </summary>
public float NearZ { get; set; }
/// <summary>
/// The FarZ of this Camera
/// </summary>
public float FarZ { get; set; }
/// <summary>
/// The Field of View of this Camera, value should be
/// between 0.70 and 1.20
/// </summary>
public float FieldOfView { get; set; }
public float AspectRatio { get; set; }
public float LookSpeed { get; set; }
public float MoveSpeed { get; set; }
/// <summary>
/// Determines if this Camera is currently accelerating.
/// </summary>
public bool IsAccelerating { get; set; }
/// <summary>
/// The acceleration speed multiplier of this Camera.
/// </summary>
public float AccelerationMultiplier { get; set; }
public CameraMode Mode { get; set; }
public float ViewportWidth;
public float ViewportHeight;
/// <summary>
/// The BoundingSphere of this Camera
/// </summary>
public BoundingSphere Bounds
{
get
{
return new BoundingSphere()
{
Center = this.Location,
Radius = 2.0f
};
}
}
/// <summary>
/// The Target Vector of this Camera
/// </summary>
public Vector3 Target
{
get
{
return new Vector3(
(float)Math.Sin(this.Yaw),
(float)Math.Tan(this.Pitch),
(float)Math.Cos(this.Yaw));
}
}
/// <summary>
/// The Frustum of this Camera
/// </summary>
public BoundingFrustum Frustum
{
get
{
return new BoundingFrustum(this.ModelViewProjectionMatrix);
}
}
public Matrix ModelViewMatrix
{
get
{
return Matrix.LookAtLH(this.Location, Location + Target, Up);
}
}
public Matrix ProjectionMatrix
{
get
{
return Matrix.PerspectiveFovLH(FieldOfView, AspectRatio, NearZ, FarZ);
}
}
public Matrix ModelViewProjectionMatrix
{
get
{
return ModelViewMatrix * ProjectionMatrix;
}
}
//public CardinalDirectionType Direction
//{
// get
// {
// return VoidwalkerMath.GetCardinalDirection(VoidwalkerMath.ToDegrees(Yaw));
// }
//}
public Vector3 Forward
{
get
{
return new Vector3((float)Math.Cos(Pitch), 0, (float)Math.Sin(Pitch));
}
}
public Vector3 Right
{
get
{
return new Vector3(Forward.X, 0, -Forward.X);
}
}
public Vector3 Up
{
get
{
return new Vector3(-(float)Math.Sin(Roll), (float)Math.Cos(Roll), 0);
}
}
public Camera()
{
}
public Camera(string name)
: this()
{
this.Name = name;
this.Location = new Vector3();
}
public void ToOrigin()
{
Transform(Vector3.Zero, 0, 0, 0);
}
public void Transform(Vector3 location, float pitch, float yaw, float roll)
{
this.Location = location;
this.Pitch = pitch;
this.Yaw = yaw;
this.Roll = roll;
}
public float GetCurrentMoveSpeed()
{
if (IsAccelerating)
{
return this.MoveSpeed * this.AccelerationMultiplier;
}
return this.MoveSpeed;
}
public void TranslateLeft(float deltaTime)
{
float moveSpeed = GetCurrentMoveSpeed();
this.Location = new Vector3(
Location.X - (float)Math.Sin(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime,
Location.Y,
Location.Z - (float)Math.Cos(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime);
}
public void TranslateRight(float deltaTime)
{
float moveSpeed = GetCurrentMoveSpeed();
this.Location = new Vector3(
Location.X + (float)Math.Sin(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime,
Location.Y,
Location.Z + (float)Math.Cos(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime);
}
public void TranslateForward(float deltaTime)
{
float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
float moveSpeed = GetCurrentMoveSpeed();
this.Location = new Vector3(
this.Location.X + (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
this.Location.Y + (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
this.Location.Z + (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
}
public void TranslateBackward(float deltaTime)
{
float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
float moveSpeed = GetCurrentMoveSpeed();
this.Location = new Vector3(
this.Location.X - (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
this.Location.Y - (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
this.Location.Z - (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
}
public void TransformYawPitch(float dx, float dy)
{
Yaw += dx * LookSpeed;
Pitch -= dy * LookSpeed;
const float pitchClamp = 1.56f;
if (Pitch <= -pitchClamp)
{
Pitch = -pitchClamp;
}
if (Pitch >= pitchClamp)
{
Pitch = pitchClamp;
}
}
public void TranslateUp(float deltaTime)
{
this.Location = new Vector3(
this.Location.X,
this.Location.Y + GetCurrentMoveSpeed() * deltaTime,
this.Location.Z); // TODO implement up/down based upon roll orientation.
}
public void TranslateDown(float deltaTime)
{
this.Location = new Vector3(
this.Location.X,
this.Location.Y - GetCurrentMoveSpeed() * deltaTime,
this.Location.Z);
}
public void LookAt(Vector3 location, float pitch, float yaw, float roll)
{
this.Location = location;
this.Pitch = pitch;
this.Yaw = yaw;
this.Roll = roll;
}
public void SetAspectRatio(int width, int height)
{
this.ViewportWidth = width;
this.ViewportHeight = height;
this.AspectRatio = width / (float)height;
}
}
}
该代码本身只会帮助您查看实现细节。我现在无法提供显示初始化和鼠标输入的最小相机示例。我建议观看 youtube 视频,因为这是我多年前开始使用的内容,并且随着时间的推移进行了修改。该教程也使用 LWJGL,正如您所要求的那样。 (尽管 API 确实没有实际意义,考虑到框架通常是可以互换的)。
计算向前和向后移动的代码是(这可能最终是左右,这取决于你的场景是如何设置的;记住,方向是完全主观的):
public void TranslateForward(float deltaTime)
{
float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
float moveSpeed = GetCurrentMoveSpeed();
this.Location = new Vector3(
this.Location.X + (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
this.Location.Y + (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
this.Location.Z + (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
}
public void TranslateBackward(float deltaTime)
{
float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
float moveSpeed = GetCurrentMoveSpeed();
this.Location = new Vector3(
this.Location.X - (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
this.Location.Y - (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
this.Location.Z - (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
}
其实这是我的鼠标输入代码。您不会在网上找到与此类似的任何内容,因为我实际上是在查询原始鼠标输入。大多数人不这样做,这就是为什么大多数示例会在鼠标按下时导致非常糟糕的“相机捕捉”。
private void OnMouseMove(object sender, MouseEventArgs args)
{
this.CurrentMouseLocation = new Point(args.X, args.Y);
if (IsMouseLookEnabled)
{
MouseUpdate[] updates = Mouse.GetBufferedData();
if (updates != null && updates.Length > 0)
{
int xAccumulation = 0;
int yAccumulation = 0;
if (updates != null && updates.Length > 0)
{
for (int i = 0; i < updates.Length; i++)
{
if (updates[i].IsButton)
{
// Reject Buffered Data
return;
}
if (updates[i].Offset == MouseOffset.X)
{
xAccumulation += updates[i].Value;
}
if (updates[i].Offset == MouseOffset.Y)
{
yAccumulation += updates[i].Value;
}
}
float dx = MathUtil.DegreesToRadians(xAccumulation) * 0.25F;
float dy = MathUtil.DegreesToRadians(yAccumulation) * 0.25F;
Camera.TransformYawPitch(dx, dy);
}
}
Cursor.Position = new System.Drawing.Point(_mouseLockLocation.X, _mouseLockLocation.Y);
this.CurrentMouseLocation = _mouseLockLocation;
}
}