博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

梦想成真 XNA (10) - 3D 模型的碰撞检测

Posted on 2011-08-25 10:14  webabcd  阅读(4109)  评论(4编辑  收藏

[索引页]
[源码下载] 


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