梦想成真 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
[源码下载]