梦想成真 XNA (10) - 3D 模型的碰撞检测
梦想成真 XNA (10) - 3D 模型的碰撞检测
作者:webabcd
介绍
XNA: 3D 模型的碰撞检测。通过包围球(Bounding Sphere)算法实现碰撞检测
示例
演示 3D 模型的碰撞检测,“上下左右”键控制 3D 模型的旋转,“LeftShift”和“LeftControl”键控制 3D 模型的前后移动(按键盘 S 键加载此 Demo)
3D/Collision/RotationDirection.cs
namespace XNA.Component._3D.Collision
{
/// <summary>
/// 旋转方向
/// </summary>
public enum RotationDirection
{
Up,
Down,
Left,
Right
}
}
3D/Collision/TranslationDirection.cs
namespace XNA.Component._3D.Collision
{
/// <summary>
/// 位移方向
/// </summary>
public enum TranslationDirection
{
Forward,
Backward
}
}
3D/Collision/MyCamera.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace XNA.Component._3D.Collision
{
/// <summary>
/// 自定义摄像机类
/// </summary>
public class MyCamera : Microsoft.Xna.Framework.GameComponent
{
// View 矩阵,用于设置摄像机的位置和方向
public Matrix View { get; protected set; }
// Projection 矩阵,用于将 3D 物体投影到 2D 屏幕上
public Matrix Projection { get; protected set; }
public MyCamera(Game game, Vector3 pos, Vector3 target, Vector3 up)
: base(game)
{
/*
* Matrix CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector) - 实例化视图矩阵
* Vector3 cameraPosition - 摄像机的位置坐标
* Vector3 cameraTarget - 摄像机镜头的朝向向量
* Vector3 cameraUpVector - 摄像机机身的顶部的上方的向量
*/
View = Matrix.CreateLookAt(pos, target, up);
/*
* CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance) - 实例化投影矩阵
* float fieldOfView - Y 轴方向上的视角弧度,一般是四分之一个 PI
* float aspectRatio - 可视区的长宽比,一般就是游戏窗口的宽除以游戏窗口的高
* float nearPlaneDistance - 当物体离摄像机多近时无法看清
* float farPlaneDistance - 当物体离摄像机多远时无法看清
*/
Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4, // 四分之一个 PI(MathHelper 里有很多实用的功能)
(float)Game.Window.ClientBounds.Width /
(float)Game.Window.ClientBounds.Height,
1,
3000
);
}
public override void Initialize()
{
base.Initialize();
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
}
}
3D/Collision/MyModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace XNA.Component._3D.Collision
{
/// <summary>
/// 自定义 3D 模型类
/// </summary>
public class MyModel
{
// Model - 代表 3D 模型。代表 2D 对象的是 Texture2D
private Model _model;
// 旋转矩阵
private Matrix _rotation = Matrix.Identity;
// 位移矩阵
private Matrix _translation = Matrix.Identity;
// 初始矩阵
private Matrix _world = Matrix.Identity;
public MyModel(Model model, Matrix world)
{
_model = model;
_world = world;
}
/// <summary>
/// 绘制 3D 模型
/// </summary>
public void Draw(MyCamera camera)
{
/*
* 一个 Model 对象里有多个 ModelMesh 对象,每个 ModelMesh 对象包含了此网格的纹理、颜色等信息
*/
foreach (ModelMesh mesh in _model.Meshes)
{
/*
* 一个 ModelMesh 对象里有多个用于绘制 ModelMesh 的 Effect
*/
foreach (BasicEffect effect in mesh.Effects)
{
// 使用默认光源效果
effect.EnableDefaultLighting();
effect.Projection = camera.Projection;
effect.View = camera.View;
effect.World = GetWorld();
}
// 绘制 ModelMesh
mesh.Draw();
}
}
/// <summary>
/// 根据方向(上下左右)计算旋转矩阵
/// </summary>
public void Rotate(RotationDirection direction)
{
switch (direction)
{
case RotationDirection.Up:
_rotation *= Matrix.CreateRotationX(-MathHelper.Pi / 180); // 绕 X 轴旋转的弧度
break;
case RotationDirection.Down:
_rotation *= Matrix.CreateRotationX(MathHelper.Pi / 180);
break;
case RotationDirection.Left:
_rotation *= Matrix.CreateRotationY(-MathHelper.Pi / 180); // 绕 Y 轴旋转的弧度
break;
case RotationDirection.Right:
_rotation *= Matrix.CreateRotationY(MathHelper.Pi / 180);
break;
}
}
/// <summary>
/// 计算位移矩阵(前进或后退)
/// </summary>
public void Move(TranslationDirection direction)
{
switch (direction)
{
case TranslationDirection.Forward:
_translation *= Matrix.CreateTranslation(_rotation.Backward);
break;
case TranslationDirection.Backward:
_translation *= Matrix.CreateTranslation(_rotation.Forward);
break;
}
}
/// <summary>
/// 获取世界矩阵,即初始矩阵乘以旋转矩阵
/// </summary>
public Matrix GetWorld()
{
return _world * _rotation * _translation;
}
/// <summary>
/// 返回自定义 3D 模型的 Model 对象
/// </summary>
public Model GetModel()
{
return _model;
}
/// <summary>
/// 检测另一个 Model 是否与本 Model 发生了碰撞
/// </summary>
/// <param name="model2">另一个 3D 模型的 Model</param>
/// <param name="world2">另一个 3D 模型的世界矩阵</param>
/// <returns></returns>
public bool CheckCollision(Model model2, Matrix world2)
{
foreach (ModelMesh mesh in _model.Meshes)
{
foreach (ModelMesh mesh2 in model2.Meshes)
{
// 每个 ModelMesh 对象都有一个 BoundingSphere 属性
if (mesh.BoundingSphere.Transform(GetWorld()).Intersects(mesh2.BoundingSphere.Transform(world2)))
return true;
}
}
return false;
}
}
}
3D/Collision/Demo.cs
/*
* 本例演示:3D 模型的碰撞检测,通过包围球(Bounding Sphere)算法(XNA 已经内置了这种算法)
* “上下左右”键控制 3D 模型的旋转,“LeftShift”和“LeftControl”键控制 3D 模型的前后移动
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace XNA.Component._3D.Collision
{
public class Demo : Microsoft.Xna.Framework.DrawableGameComponent
{
private MyCamera _camera;
// 计算机控制的模型
private MyModel _modelSystem;
// 用户控制的模型
private MyModel _modelUser;
// 是否发生了碰撞
bool _collided = false;
public Demo(Game game)
: base(game)
{
}
public override void Initialize()
{
// 添加摄像机组件
_camera = new MyCamera(Game, new Vector3(0, 0, 50), Vector3.Zero, Vector3.Up);
Game.Components.Add(_camera);
base.Initialize();
}
protected override void LoadContent()
{
_modelSystem = new MyModel(Game.Content.Load<Model>(@"3DModel\Spaceship"), Matrix.Identity * Matrix.CreateTranslation(0, 0, -200));
_modelUser = new MyModel(Game.Content.Load<Model>(@"3DModel\Spaceship"), Matrix.Identity * Matrix.CreateTranslation(0, -10, 0));
base.LoadContent();
}
public override void Update(GameTime gameTime)
{
// 判断两个 3D 模型是否发生了碰撞
_collided = _modelUser.CheckCollision(_modelSystem.GetModel(), _modelSystem.GetWorld());
if (_collided)
return;
KeyboardState keyboardState = Keyboard.GetState();
// 根据用户的按键(上下左右),旋转 3D 模型
if (keyboardState.IsKeyDown(Keys.Up))
_modelUser.Rotate(RotationDirection.Up);
else if (keyboardState.IsKeyDown(Keys.Down))
_modelUser.Rotate(RotationDirection.Down);
else if (keyboardState.IsKeyDown(Keys.Left))
_modelUser.Rotate(RotationDirection.Left);
else if (keyboardState.IsKeyDown(Keys.Right))
_modelUser.Rotate(RotationDirection.Right);
// 根据用户的按键移动 3D 模型:LeftShift - 向前移动;LeftControl - 向后移动
if (keyboardState.IsKeyDown(Keys.LeftShift))
_modelUser.Move(TranslationDirection.Forward);
else if (keyboardState.IsKeyDown(Keys.LeftControl))
_modelUser.Move(TranslationDirection.Backward);
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
if (_collided)
Game.GraphicsDevice.Clear(Color.IndianRed);
else
Game.GraphicsDevice.Clear(Color.CornflowerBlue);
// 绘制 3D 模型
_modelSystem.Draw(_camera);
_modelUser.Draw(_camera);
base.Update(gameTime);
}
}
}
OK
[源码下载]
浙公网安备 33010602011771号