层次模型动画—坦克2
XNA使用ModelBone类对象表示一个部件,如前所述,一个部件是一个刚性物体,部件中的所有顶点同时只能做一种运动,即部件中的顶点都具有相同的世界变换矩阵,部件具有相同的材质和纹理。一个部件必须有一个ModelMesh对象,ModelMesh对象用于在计算机显示器屏幕显示该部件的外观。ModelBone类的常用属性如下:
- Name:记录了部件在.x文件中的名称,例如,Tank.x文件表示一辆坦克,文件中根部件的名称是“Body”,则代表根部件的ModelBone类对象的Name属性为“Body”。
- Transform:部件本身运动所使用的世界变换矩阵,包括部件初始变换矩阵和部件运动变换矩阵,不包括父部件运动的世界变换矩阵。
- Children数组:记录了该部件的所有子部件,Children数组元素也是ModelBone类对象,根部件第一个子节点是Children[1],注意索引不是0,根部件第二个子节点是Children[2]等。
- Parent:表示部件的父部件。
- Index:一个3D模型的所有部件都保存在Model类属性Bones数组中,本属性是本部件在数组Bones中的索引号。
.x文件中定义的部件,必须在3D程序中转变为ModelBone类的对象,才能最终在显示器屏幕显示。使用ContentManager类方法Load来装载包含层次关系的.x文件:
Model tankModel=Content.Load<Model>(“tank”);
该方法将创建在.x文件中定义的每一个部件的ModelBone类对象,部件也称为骨骼(bone)。Model类对象记录一个3D模型的所有ModelBone类对象、ModelBone类对象所使用的ModelMesh类对象以及ModelBone类对象的阶层关系。Model类的一些常用属性和方法如下:
- Bones:记录3D模型的所有ModelBone类对象(即部件对象)数组。可以使用索引号得到部件对象,也可以使用部件对象的名称得到该部件,例如tankModel.Bones[“Gun”]。
- Meshes:ModelMesh类数组,保存3D模型所有部件使用的ModelMesh对象。
- Root:3D模型的根部件,是ModelBone类对象(即部件对象)。其属性Children数组记录了根部件的所有子部件,Children数组元素是ModelBone类对象,数组元素之间是兄弟关系。一个子部件可能也有自己的子部件(根部件的孙部件),孙部件在其父部件的属性Children数组中。用这种方法描述具有层次关系的3D模型。
- public void CopyAbsoluteBoneTransformsTo(Matrix[]BoneTransforms)方法:将Model类对象中所有部件的完整的世界变换矩阵,复制到外部矩阵数数组中,以便在渲染时使用。
用如下代码得到这个矩阵数组:
Matrix[] boneTransforms; boneTransforms=new Matrix[tankModel.Bones.Count]; tankModel.CopyAbsoluteTransformsTo[boneTransforms];
XNA代码
1. 在Game1类中添加变量:
Model tankModel; Matrix[] boneTransforms; //分别引用坦克Body、Turret和Gun部件 ModelBone BodyBone, TurretBone, GunBone; //坦克Body、Turret和Gun部件的变换矩阵 Matrix BodyTransform, TurretTransform, GunTransform; //分别为坦克Body部件的移动距离,炮塔旋转弧度,火炮上下转动弧度 float BodyMove = 0, GunUpDown = 0, TurreRound = 0; 2;
修改Game1类的LoadContent方法:
tankModel = Content.Load<Model>("Tank"); //得到坦克Body部件的ModelBone对象 BodyBone = tankModel.Bones["Body"]; TurretBone = tankModel.Bones["Turret"]; GunBone = tankModel.Bones["Gun"]; //得到部件的初始世界矩阵 BodyTransform = BodyBone.Transform; TurretTransform = TurretBone.Transform; GunTransform = GunBone.Transform; boneTransforms = new Matrix[tankModel.Bones.Count];
3. 修改Update方法:
CheckInput(); GunBone.Transform = Matrix.CreateRotationX(GunUpDown) * GunTransform; TurretBone.Transform = Matrix.CreateRotationY(TurreRound) * TurretTransform; BodyBone.Transform = Matrix.CreateTranslation(0, BodyMove,0) * BodyTransform; tankModel.CopyAbsoluteBoneTransformsTo(boneTransforms); base.Update(gameTime);
并添加CheckInput方法:
KeyboardState newState = Keyboard.GetState(); //键盘上下控制坦克的移动 if (newState.IsKeyDown(Keys.Down)) BodyMove += 0.5f; if (newState.IsKeyDown(Keys.Up)) BodyMove -= 0.5f; //键盘PageUp,PageDown控制炮管上下 if (newState.IsKeyDown(Keys.PageUp)) { GunUpDown -= 0.05f; if (GunUpDown < -0.6) GunUpDown = -0.6f; } if (newState.IsKeyDown(Keys.PageDown)) { GunUpDown += 0.05f; if (GunUpDown > 0) GunUpDown = 0.01f; } //键盘左右控制炮塔旋转 if (newState.IsKeyDown(Keys.Left)) { TurreRound -= 0.05f; if (TurreRound < -1f) TurreRound = -1f; } if (newState.IsKeyDown(Keys.Right)) { TurreRound += 0.05f; if (TurreRound > 1f) TurreRound = 1f; }
4. Draw方法中的代码如下:
GraphicsDevice.Clear(Color.CornflowerBlue); foreach (ModelMesh mesh in tankModel.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.EnableDefaultLighting(); effect.World = boneTransforms[mesh.ParentBone.Index]; effect.View = Matrix.CreateLookAt(new Vector3(0, 100.0f, 200.0f), Vector3.Zero ,Vector3 .Up ); effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height, 1.0f, 400.0f); } mesh.Draw(); } base.Draw(gameTime);
运行程序后,可使用键盘上下控制坦克前进后退,键盘PageUp,PageDown控制炮管上下,/键盘左右控制炮塔旋转,截图如下:
发布时间:2009/6/8 上午10:19:42 阅读次数:7631