这也是上的一个示例。
新建一个XNA(Windowsgame)工程,取名为Windows3DGame。
右击解决方案资源管理器中的Content节点,添加两个文件夹,分别命名为Audio和Models,然后向这两个文件夹里分别添加所需资源。
(h ttp://上可下载,也可从我的源码中获得,地址在下面。
)注意添加Model资源时,项目中只添加后缀名为.fbx的文件。
添加一个新类,命名为GameObject.cs,其代码如下:ing System;ing System.Collections.Generic;ing Microsoft.Xna.Framework;ing Microsoft.Xna.Framework.Audio;ing Microsoft.Xna.Framework.Content;ing Microsoft.Xna.Framework.GamerServices;ing Microsoft.Xna.Framework.Graphics;ing Microsoft.Xna.Framework.Input;ing ;10.u sing Microsoft.Xna.Framework.Storage;11.12.n amespace Windows3DGame13.{14. class GameObject15. {16. public Model model = null;17. public Vector3 position = Vector3.Zero; //位置参数18. public Vector3 rotation = Vector3.Zero; //旋转参数19. public float scale = 1.0f; //缩放参数20. }21.}一、绘制背景,载入地形。
1. 在Game.cs中添加几个变量1. GameObject terrain = new GameObject(); //地形2.3. Vector3 cameraPosition = new Vector3( //摄像机位置4. 0.0f, 60.0f, 160.0f);5. Vector3 cameraLookAt = new Vector3( //观察方向6. 0.0f, 50.0f, 0.0f);7.8. Matrix cameraProjectionMatrix; //Projection矩阵9. Matrix cameraViewMatrix; //View矩阵2. 在LoadContent()函数中添加如下代码:1. //设置View矩阵2. cameraViewMatrix = Matrix.CreateLookAt(3. cameraPosition,4. cameraLookAt,5. Vector3.Up);6.7. //设置Projection矩阵8. cameraProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(9. MathHelper.ToRadians(45.0f),10. graphics.GraphicsDevice.Viewport.AspectRatio,11. 1.0f,12. 10000.0f);13.14. //载入地形15. terrain.model = Content.Load<Model>(16. "Models\\terrain");3. 添加函数DrawGameObject(GameObject gameobject)用来绘制游戏中的对象1. //绘制对象2. private void DrawGameObject(GameObject gameobject)3. {4. foreach (ModelMesh mesh in gameobject.model.Meshes)5. {6. foreach (BasicEffect effect in mesh.Effects)7. {8. effect.EnableDefaultLighting(); //默认灯光9. //设置World矩阵10. effect.World =11. Matrix.CreateFromYawPitchRoll(12. gameobject.rotation.Y,13. gameobject.rotation.X,14. gameobject.rotation.Z) *15. Matrix.CreateScale(gameobject.scale) *16. Matrix.CreateTranslation(gameobject.position);17. //设置Projection矩阵18. effect.Projection = cameraProjectionMatrix;19. //设置View矩阵20. effect.View = cameraViewMatrix;21. }22. mesh.Draw();23. }24. }关于这些矩阵,参考/changjiangboy/archive/2008/08/05/2770935.aspx。
在Draw(GameTime gameTime)函数中添加如下代码:1. //调用函数绘制地形2. DrawGameObject(terrain);4. 运行,效果如下图二、绘制导弹发射装置1. 在Game.cs中实例化两个对象1. GameObject missileLauncherBase = new GameObject(); //底座2. GameObject missileLauncherHead = new GameObject(); //发射头2. 在LoadContent()函数中添加如下代码1. //载入发射装置2. missileLauncherBase.model = Content.Load<Model>(3. "Models\\launcher_base");4. missileLauncherBase.scale = 0.2f; //原大小的五分之一5.6. missileLauncherHead.model = Content.Load<Model>(7. "Models\\launcher_head");8. missileLauncherHead.scale = 0.2f; //原大小的五分之一9. missileLauncherHead.position =10. missileLauncherBase.position +11. new Vector3(0.0f, 20.0f, 0.0f); //head比base高203. 在Update(GameTime gameTime)函数中添加代码控制导弹发射方向1. KeyboardState keyboardState = Keyboard.GetState(); //获取键盘状态2. if (keyboardState.IsKeyDown(Keys.Left))3. {4. //绕Y轴旋转,从上往下看逆时针方向;可看作向左5. missileLauncherHead.rotation.Y += 0.05f;6. }7. if (keyboardState.IsKeyDown(Keys.Right))8. {9. //绕Y轴旋转,从上往下看顺时针方向;可看作向右10. missileLauncherHead.rotation.Y -= 0.05f;11. }12. if (keyboardState.IsKeyDown(Keys.Up))13. {14. //绕X轴旋转,左侧看顺时针方向;可看作向上15. missileLauncherHead.rotation.X += 0.05f;16. }17. if (keyboardState.IsKeyDown(Keys.Down))18. {19. //绕X轴旋转,左侧看逆时针方向;可看作向下20. missileLauncherHead.rotation.X -= 0.05f;21. }22.23. //左右旋转范围(-PI/3,PI/3)24. missileLauncherHead.rotation.Y = MathHelper.Clamp(25. missileLauncherHead.rotation.Y,26. -MathHelper.Pi/3.0f, MathHelper.Pi/3.0f);27.28. //左右旋转范围(0,PI/3)29. missileLauncherHead.rotation.X = MathHelper.Clamp(30. missileLauncherHead.rotation.X,31. 0, MathHelper.Pi/3.0f);MathHelper.Clamp(float value, float min, float max)函数的作用是限制value的值在min和max之间。
4. 在Draw(GameTime gameTime)函数中添加代码1. //调用函数绘制发射台2. DrawGameObject(missileLauncherBase);3. DrawGameObject(missileLauncherHead);5. 运行后效果如下三、发射导弹1. 在Game.cs中添加如下变量1. GameObject[] missiles; //导弹2. const int numMissiles = 20; //屏幕上显示最多导弹数目3. const float launcherHeadMuzzleOffset = 52.0f; //偏离炮膛参数,描述导弹初始位置,这样导弹直接从发射口4.//出现,看起来有“真实”些5. const float missilePower = 20.0f; //导弹动力,速度参数,大小决定导弹速度6. KeyboardState previousKeyboardState; //获取(上次)当前键盘状态,7. //将与(当前)下一次对比2. 在LoadContent()函数中添加代码1. //载入导弹2. missiles = new GameObject[numMissiles];3. for (int i = 0; i < numMissiles; i++)4. {5. missiles[i] = new GameObject();6. missiles[i].model =7. Content.Load<Model>("Models\\missile");8. missiles[i].scale = 3.0f; //这里为原大小的3倍9. }3. 在Game1类中添加以下几个函数1. // 发射导弹2. private void FireMissile()3. {4. foreach (GameObject missile in missiles)5. {6. if (!missile.alive)7. {8. missile.velocity = GetMissileMuzzleVelocity(); //初始速度9. missile.position = GetMissileMuzzlePosition(); //初始位置10. missile.rotation = missileLauncherHead.rotation; //旋转度与炮口一致11. missile.alive = true; //“激活”状态12. break;13. }14. }15. }16.17. // 获取导弹初始速度18. private Vector3 GetMissileMuzzleVelocity()19. {20. Matrix rotationMatrix =21. Matrix.CreateFromYawPitchRoll(22. missileLauncherHead.rotation.Y,23. missileLauncherHead.rotation.X,24. 0);25.26. return Vector3.Normalize(27. Vector3.Transform(Vector3.Forward,28. rotationMatrix)) * missilePower;29. }30.31. // 获取导弹初始位置32. private Vector3 GetMissileMuzzlePosition()33. {34. return missileLauncherHead.position +35. (Vector3.Normalize(36. GetMissileMuzzleVelocity()) *37. launcherHeadMuzzleOffset);38. }39.40. // 更新屏幕上的导弹41. private void UpdateMissiles()42. {43. foreach (GameObject missile in missiles)44. {45. if (missile.alive)46. {47. missile.position += missile.velocity;48. //如果导弹跑出3000的距离,“肉眼”看不见了,自动消失49. //如果导弹跑出窗口边缘,脱离视野,自动消失50. if (missile.position.Z < -3000.0f ||51. missile.position.X > graphics.GraphicsDevice.Viewport.Width / 2.0f ||52. missile.position.X < -graphics.GraphicsDevice.vViewport.Width / 2.0f ||53. missile.position.Y > graphics.GraphicsDevice.Viewport.Height / 2.0f)54. {55. missile.alive = false;56. }57. }58. }59. }4. 在Update(GameTime gameTime)函数中添加如下代码1. //按一次空格发射一枚导弹2. if (keyboardState.IsKeyDown(Keys.Space) &3. previousKeyboardState.IsKeyUp(Keys.Space))4. {5. FireMissile(); //发射导弹6. }7.8. //调用函数更新屏幕导弹9. UpdateMissiles();10.11. previousKeyboardState = keyboardState; //当前键盘状态为下一初始状态5. 在Draw(GameTime gameTime)函数中添加代码绘制导弹1. //绘制各个导弹2. foreach (GameObject missile in missiles)3. {4. if (missile.alive)5. {6. DrawGameObject(missile);7. }8. }6. 运行,效果如下四、添加飞船1. 在Game.cs中添加如下常变量1. GameObject[] enemyShips; //飞船2. Random r = new Random(); //随机数生成器3. const int numEnemyShips = 3; //屏幕飞船数目4. Vector3 shipMinPosition = new Vector3(-2000.0f, 300.0f, -6000.0f); //最远位置5. Vector3 shipMaxPosition = new Vector3(2000.0f, 800.0f, -4000.0f); //最近位置6. const float shipMinVelocity = 5.0f; //最小速度7. const float shipMaxVelocity = 10.0f; //最大速度2. 在LoadContent()函数中加以下代码1. //载入飞船2. enemyShips = new GameObject[numEnemyShips];3. for (int i = 0; i < numEnemyShips; i++)4. {5. enemyShips[i] = new GameObject();6. enemyShips[i].model = Content.Load<Model>(7. "Models\\enemy");8. enemyShips[i].scale = 0.1f;9. enemyShips[i].rotation = new Vector3(10. 0.0f, MathHelper.Pi, 0.0f);11. }3. 添加函数用来更新屏幕上的飞船1. // 更新屏幕上的飞船2. private void UpdateEnemyShips()3. {4. foreach (GameObject ship in enemyShips)5. {6. if (ship.alive)7. {8. ship.position += ship.velocity;9. //如果飞船飞到炮口后(炮位置Z坐标是0),自动消失10. //如果飞船飞出视野(左、右、上),自动消失11. if (ship.position.Z > 10.0f ||12. ship.position.X > (-ship.position.Z + 160f) * Math.Tan(22.5) ||13. ship.position.X < -(-ship.position.Z + 160f) * Math.Tan(22.5) ||14. ship.position.Y > (-ship.position.Z + 160f) * Math.Tan(22.5) /15. graphics.GraphicsDevice.Viewport.AspectRatio)16. {17. ship.alive = false;18. }19. }20. else //如果有没“激活”的飞船,激活它,并确定其位置速度21. {22. ship.alive = true;23. ship.position = new Vector3(24. MathHelper.Lerp(25. shipMinPosition.X,26. shipMaxPosition.X,27. (float)r.NextDouble()),28.29. MathHelper.Lerp(30. shipMinPosition.Y,31. shipMaxPosition.Y,32. (float)r.NextDouble()),33.34. MathHelper.Lerp(35. shipMinPosition.Z,36. shipMaxPosition.Z,37. (float)r.NextDouble()));38.39. ship.velocity = new Vector3(40. 0.0f,41. 0.0f,42. MathHelper.Lerp(shipMinVelocity,43. shipMaxVelocity, (float)r.NextDouble()));44. }45. }46. }MathHelper.Lerp(float value1,float value2,float amount)函数线性插入一个限定在两个值之间的值。