首页 > 代码库 > Axiom3D写游戏:第一个窗口

Axiom3D写游戏:第一个窗口

  Axiom主要的代码大致翻看了下,就想到了自己来模拟一下游戏开发.

  这章主要包括创建窗口及3D渲染的一些基本元素,并添加一个第三人称的骨骼动画作主角,加上前文中修改过后的地形组件,能用鼠标和键盘进行漫游.如下图所示.  

  在Axiom上给出的例子中,可以选择是用OpenGL或是DirectX渲染,现在有问题的地方是如果用OpenGL渲染,而Axiom鼠标与键盘输入采用的是SharpInputSystem,这个在DirectX渲染下是没有问题的,但是在OpenGL渲染的窗口下得不到正确的鼠标位置.而DixectX渲染下,运行一会后,骨骼动画在进行骨骼变化时,调用SoftwareVertexBlend会引起程序挂起,这个问题没找到解决方法,以后在来调.

  在上面的情况下,在加上前面我主要学的是OpenGL,我决定在我接下来采用OpenGL渲染以及OpenTK里提供的鼠标与键盘输入接口.

  接下来,通过Axiom里提供的例子,结合Samples.Brower.Win32与Samples.Common来看Axiom的渲染流程,结合上面的流程自己写了一半,才发现有现存的,在Axiom.Framework里的Game抽象类里,提供对应流程,因为原始采用SharpInputSystem来处理键盘与鼠标反馈,经过修改,下面是代码.

  1     public abstract class Game : IDisposable, IWindowEventListener  2     {  3         protected Root Engine;   4         protected IConfigurationManager ConfigurationManager;  5         protected ResourceGroupManager Content;  6         protected SceneManager SceneManager;  7         protected Camera Camera;  8         protected Viewport Viewport;  9         protected RenderWindow Window; 10         protected Axiom.Graphics.RenderSystem RenderSystem; 11  12         public virtual void Run() 13         { 14             PreInitialize(); 15             LoadConfiguration(); 16             Initialize(); 17             CreateRenderSystem(); 18             CreateRenderWindow(); 19             LoadResource(); 20             CreateSceneManager(); 21             CreateCamera(); 22             CreateViewports(); 23             CreateInput(); 24             CreateScene(); 25             this.Engine.StartRendering(); 26         } 27  28         private void PreInitialize() 29         { 30             this.ConfigurationManager = new DefaultConfigurationManager(); 31  32             // instantiate the Root singleton 33             this.Engine = new Root(this.ConfigurationManager.LogFilename); 34  35             // add event handlers for frame events 36             this.Engine.FrameStarted += Engine_FrameRenderingQueued; 37         } 38  39         public virtual void LoadConfiguration() 40         { 41             this.ConfigurationManager.RestoreConfiguration(this.Engine); 42         } 43  44  45         private void Engine_FrameRenderingQueued(object source, FrameEventArgs e) 46         { 47             Update(e.TimeSinceLastFrame); 48         } 49  50         public virtual void Initialize() 51         { 52         } 53  54         public virtual void CreateRenderSystem() 55         { 56             if (this.Engine.RenderSystem == null) 57             { 58                 this.RenderSystem = this.Engine.RenderSystem = this.Engine.RenderSystems.First().Value; 59             } 60             else 61             { 62                 this.RenderSystem = this.Engine.RenderSystem; 63             } 64         } 65  66         public virtual void CreateRenderWindow() 67         { 68             this.Window = Root.Instance.Initialize(true, "Xin Game"); 69  70             WindowEventMonitor.Instance.RegisterListener(this.Window, this); 71         } 72  73         public virtual void LoadResource() 74         { 75             ResourceGroupManager.Instance.InitializeAllResourceGroups(); 76         } 77  78         public virtual void CreateSceneManager() 79         { 80             // Get the SceneManager, a generic one by default 81             this.SceneManager = this.Engine.CreateSceneManager("DefaultSceneManager", "GameSMInstance"); 82             this.SceneManager.ClearScene(); 83         } 84  85         public virtual void CreateCamera() 86         { 87             // create a camera and initialize its position 88             this.Camera = this.SceneManager.CreateCamera("MainCamera"); 89             this.Camera.Position = new Vector3(0, 0, 0); 90             this.Camera.LookAt(new Vector3(0, 0, -300)); 91  92         } 93  94         public virtual void CreateViewports() 95         { 96             // create a new viewport and set it‘s background color 97             this.Viewport = this.Window.AddViewport(this.Camera, 0, 0, 1.0f, 1.0f, 100); 98             this.Viewport.BackgroundColor = ColorEx.SteelBlue; 99         }100 101         public virtual void CreateInput()102         {103             //var window = this.Window["nativewindow"];104         }105 106         public abstract void CreateScene();107 108         public virtual void Update(float timeSinceLastFrame)109         {110         }111 112         #region IDisposable Implementation113 114         #region IsDisposed Property115 116         /// <summary>117         /// Determines if this instance has been disposed of already.118         /// </summary>119         public bool IsDisposed { get; set; }120 121         #endregion IsDisposed Property122 123         /// <summary>124         /// Class level dispose method125         /// </summary>126         /// <remarks>127         /// When implementing this method in an inherited class the following template should be used;128         /// protected override void dispose( bool disposeManagedResources )129         /// {130         ///     if ( !IsDisposed )131         ///     {132         ///         if ( disposeManagedResources )133         ///         {134         ///             // Dispose managed resources.135         ///         }136         /// 137         ///         // If there are unmanaged resources to release, 138         ///         // they need to be released here.139         ///     }140         ///141         ///     // If it is available, make the call to the142         ///     // base class‘s Dispose(Boolean) method143         ///     base.dispose( disposeManagedResources );144         /// }145         /// </remarks>146         /// <param name="disposeManagedResources">True if Unmanaged resources should be released.</param>147         protected virtual void dispose(bool disposeManagedResources)148         {149             if (!IsDisposed)150             {151                 if (disposeManagedResources)152                 {153                     if (this.Engine != null)154                     {155                         // remove event handlers156                         this.Engine.FrameStarted -= Engine_FrameRenderingQueued;157                     }158                     if (this.SceneManager != null)159                     {160                         this.SceneManager.RemoveAllCameras();161                     }162                     this.Camera = null;163                     if (Root.Instance != null)164                     {165                         Root.Instance.RenderSystem.DetachRenderTarget(this.Window);166                     }167                     if (this.Window != null)168                     {169                         WindowEventMonitor.Instance.UnregisterWindow(this.Window);170                         this.Window.Dispose();171                     }172                     if (this.Engine != null)173                     {174                         this.Engine.Dispose();175                     }176                 }177 178                 // There are no unmanaged resources to release, but179                 // if we add them, they need to be released here.180             }181             IsDisposed = true;182         }183 184         /// <summary>185         /// Call to when class is no longer needed 186         /// </summary>187         public void Dispose()188         {189             dispose(true);190             GC.SuppressFinalize(this);191         }192 193         ~Game()194         {195             dispose(false);196         }197 198         #endregion IDisposable Implementation199 200         #region IWindowEventListener Implementation201 202         /// <summary>203         /// Window has moved position204         /// </summary>205         /// <param name="rw">The RenderWindow which created this event</param>206         public void WindowMoved(RenderWindow rw)207         {208         }209 210         /// <summary>211         /// Window has resized212         /// </summary>213         /// <param name="rw">The RenderWindow which created this event</param>214         public void WindowResized(RenderWindow rw)215         {216         }217 218         /// <summary>219         /// Window has closed220         /// </summary>221         /// <param name="rw">The RenderWindow which created this event</param>222         public void WindowClosed(RenderWindow rw)223         {224             // Only do this for the Main Window225             if (rw == this.Window)226             {227                 Root.Instance.QueueEndRendering();228             }229         }230 231         /// <summary>232         /// Window lost/regained the focus233         /// </summary>234         /// <param name="rw">The RenderWindow which created this event</param>235         public void WindowFocusChange(RenderWindow rw)236         {237         }238 239         #endregion240     }
Game

  在原来的基础上去掉相关SharpInputSystem的代码,相关初始化过程都在Run这个方法内,主要过程生成Root对象,此过程会预先调用大部分Manager类,以达到相关静态构造函数能按顺序进行,不然可能会引发一些问题,插件的加载也在这个方法内。在加载插件时,会查找是否包含OpenGL与DirectX渲染插件,在这里我们只选择添加了RenderSystems.OpenGL的插件,那么在接下创建RenderSystem时,选择这个渲染插件作为Root对象的渲染系统。根据对应的RenderSystem我们创建对应的窗口,跟踪相关代码,我们知道我们创建的是RenderWindow的子类OpenTKWindow.在创建窗口之后。我们会选择加载资源进来。然后是创建场景管理SceneManager,场景管理具体可以查看Ogre中SceneManager分析,Ogre本身给我们提供了二个场景管理插件,分别是BSP与Octree,这二个方案主要区别是针对物体怎么确定是否需要渲染的逻辑有所不同,最新Axiom因为直接用的是Ogre1.7之后的地形插件,所以不需要固定使用Octree的场景管理方案。在已经创建管理基础上,我们创建摄像机,已经对应窗口的Viewport对象。方法Update就是Root对象的消息循环,我们每桢的逻辑都可以放入这个方法内,当我们调用Root的StartRendering方法后,正常情况下就不断更新Update方法。

  在原来的Game里我们放弃了原来的SharpInputSystem,我们需要一个能正常接收对应OpenTKWindow里键盘与鼠标的输入,并且还有前面所说的一个骨骼动画加上地形组件所产生的地形,以及一些基本的UI组件显示。

  1     public class OpenTKGame : Game  2     {  3         public OpenTKGame()  4         {  5   6         }  7   8   9         #region property 10         public KeyboardState KeyboardState 11         { 12             get 13             { 14                 return Keyboard.GetState(); 15             } 16         } 17  18         public MouseState MouseState 19         { 20             get 21             { 22                 return Mouse.GetState(); 23             } 24         } 25  26         #endregion 27  28         protected SinbadCharacterController chara; 29         protected CameraManager cameraManager; 30         protected UIManager uiManager; 31         protected TerrainManager terrainManager; 32  33         private bool bLoadTerrain = true; 34         private bool bDown = false; 35         public override void CreateRenderWindow() 36         { 37             base.CreateRenderWindow(); 38         } 39  40         public override void LoadResource() 41         { 42             //ResourceGroupManager.Instance.InitializeResourceGroup("Popular"); 43             ResourceGroupManager.Instance.InitializeResourceGroup("Essential"); 44             uiManager = new UIManager(this.Window); 45         } 46  47         public override void CreateCamera() 48         { 49             //uiManager.ShowBackdrop("SdkTrays/Bands"); 50             base.CreateCamera(); 51             this.cameraManager = new CameraManager(this.Camera); 52             this.terrainManager = new TerrainManager(this.SceneManager); 53         } 54  55         public override void CreateInput() 56         { 57             base.CreateInput(); 58             var window = this.Window["nativewindow"] as OpenTK.NativeWindow; 59             if (window == null) 60             { 61                 throw new NotImplementedException(); 62             } 63             var mouse = window.InputDriver.Mouse[0]; 64             var keyboard = window.InputDriver.Keyboard[0]; 65             mouse.ButtonDown += mouse_ButtonDown; 66             mouse.ButtonUp += mouse_ButtonUp; 67             mouse.Move += mouse_Move; 68  69             keyboard.KeyDown += keyboard_KeyDown; 70             keyboard.KeyUp += keyboard_KeyUp; 71         } 72  73         public override void CreateScene() 74         { 75             // set background and some fog 76             Viewport.BackgroundColor = new ColorEx(1.0f, 1.0f, 0.8f); 77             SceneManager.SetFog(FogMode.Linear, new ColorEx(1.0f, 1.0f, 1.0f), 0, 15, 100); 78             // set shadow properties 79             //SceneManager.ShadowTechnique = ShadowTechnique.TextureModulative; 80             SceneManager.ShadowColor = new ColorEx(0.5f, 0.5f, 0.5f); 81             SceneManager.ShadowTextureSize = 1024; 82             SceneManager.ShadowTextureCount = 1; 83             // disable default camera control so the character can do its own 84             cameraManager.setStyle(CameraStyle.Manual); 85             // use a small amount of ambient lighting 86             SceneManager.AmbientLight = new ColorEx(0.3f, 0.3f, 0.3f); 87  88             // add a bright light above the scene 89             Light light = SceneManager.CreateLight("CharacterLight"); 90             light.Type = LightType.Point; 91             light.Position = new Vector3(-10, 40, 20); 92             light.Specular = ColorEx.White; 93  94             this.uiManager.ShowBackdrop("SdkTrays/Bands"); 95             this.uiManager.ShowLoadingBar(6, 1, 0.7f); 96             ResourceGroupManager.Instance.InitializeResourceGroup("Popular"); 97             if (this.bLoadTerrain) 98             { 99                 terrainManager.SetupContent();100             }101             this.uiManager.HideLoadingBar();102             this.uiManager.HideBackdrop();103 104             ShowCursor(0);105             uiManager.ShowCursor();106 107             MeshManager.Instance.CreatePlane("floor", ResourceGroupManager.DefaultResourceGroupName,108                                               new Plane(Vector3.UnitY, 0), 100, 100, 10, 10, true, 1, 10, 10, Vector3.UnitZ);109 110             // create a floor entity, give it a material, and place it at the origin111             Entity floor = SceneManager.CreateEntity("Floor", "floor");112             floor.MaterialName = "Examples/Rockwall";113             floor.CastShadows = false;114             SceneManager.RootSceneNode.AttachObject(floor);115 116             // create our character controller117             this.chara = new SinbadCharacterController(Camera);118             this.chara.bodyNode.Position = new Vector3(1900, 5, 2000);119         }120 121         public override void Update(float timeSinceLastFrame)122         {123             base.Update(timeSinceLastFrame);124             this.cameraManager.frameRenderingQueued(timeSinceLastFrame);125             this.chara.AddTime(timeSinceLastFrame);126             if (bLoadTerrain)127             {128                 var position = this.chara.bodyNode.Position;129                 terrainManager.GetTerrainReyHighter(ref position, timeSinceLastFrame);130                 this.chara.bodyNode.Position = position;131             }132         }133 134         [DllImport("user32.dll", EntryPoint = "ShowCursor", CharSet = CharSet.Auto)]135         public extern static void ShowCursor(int status);136 137         #region event138         void keyboard_KeyUp(object sender, KeyboardKeyEventArgs e)139         {140             this.cameraManager.injectKeyUp(e);141             this.chara.InjectKeyUp(e);142         }143 144         void keyboard_KeyDown(object sender, KeyboardKeyEventArgs e)145         {146             switch (e.Key)147             {148                 case Key.R:149                     switch (this.Camera.PolygonMode)150                     {151                         case PolygonMode.Points:152                             this.Viewport.Camera.PolygonMode = PolygonMode.Solid;153                             break;154                         case PolygonMode.Solid:155                             this.Viewport.Camera.PolygonMode = PolygonMode.Wireframe;156                             break;157                         case PolygonMode.Wireframe:158                             this.Viewport.Camera.PolygonMode = PolygonMode.Points;159                             break;160                     }161                     break;162                 default:163                     break;164             }165 166             this.cameraManager.injectKeyDown(e);167             this.chara.InjectKeyDown(e);168         }169 170         void mouse_Move(object sender, MouseMoveEventArgs e)171         {172             this.cameraManager.injectMouseMove(e);173             if (bDown)174             {175                 this.chara.InjectMouseMove(e);176             }177             uiManager.RefreshCursor(e.X, e.Y);178         }179 180         void mouse_ButtonUp(object sender, MouseButtonEventArgs e)181         {182             this.cameraManager.injectMouseUp(e);183             bDown = false;184         }185 186         void mouse_ButtonDown(object sender, MouseButtonEventArgs e)187         {188             this.cameraManager.injectMouseDown(e);189             this.chara.InjectMouseDown(e);190             bDown = true;191         }192         #endregion193     }
OpenTKGame

  OpenTKGame继续Game,有一些方法做了相关改动,在LoadResource里,并没有全部加载所有资源,只是加载了UI所需要的资源。在CreateInput里,我们获取OpenTKWindow里的nativewindow,以此对象来检测鼠标与键盘的输入。然后在CreateScene就是我们相关元素的初始化的一些过程,场景管理的一些基本定义,一个点光源,界面上的一些UI元素,这里有二个位置,一个是加载游戏资源对应的进度条,一个是光标。然后是地形加载,最后就是主角,骨骼动画的加载。

  关于地形,我在上文Axiom3D:资源引用与加载基本流程大致分析了下,原Axiom里的地形组件有些问题还没修正,需要自己修改相关位置的BUG才能得到正确的地形,这里就不仔细说了,需要注意的一点就是,我们的角色要正确的与地面交互.下面是主要源代码。

  1     public class TerrainManager  2     {  3         private const string TerrainFilePrefix = "TestTerrain";  4         private const string TerrainFileSuffix = "dat";  5         private const float TerrainWorldSize = 5120;  6         private const int TerrainSize = 513;  7   8         private const int TerrainPageMinX = 0;  9         private const int TerrainPageMinY = 0; 10         private const int TerrainPageMaxX = 0; 11         private const int TerrainPageMaxY = 0; 12  13         protected bool terrainsImported = false; 14  15         protected Real fallVelocity; 16         protected Real heightUpdateCountDown; 17         protected Real heightUpdateRate; 18         protected Vector3 terrainPos = new Vector3(0, 0, 0); 19         protected Real brushSizeTerrainSpace = 0.02f; 20  21         protected TerrainGlobalOptions terrainGlobals; 22         protected TerrainGroup terrainGroup; 23         protected List<Entity> houseList = new List<Entity>(); 24  25         private SceneManager SceneManager; 26  27         public TerrainManager(SceneManager sceneManager) 28         { 29             this.heightUpdateRate = 1.0f / 2.0f; 30             this.SceneManager = sceneManager; 31         } 32  33         private ImportData _configureTerrainDefaults(Light l) 34         { 35             // Configure global 36             TerrainGlobalOptions.MaxPixelError = 8; 37             // testing composite map 38             TerrainGlobalOptions.CompositeMapDistance = 3000; 39             TerrainGlobalOptions.LightMapDirection = l.DerivedDirection; 40             //TerrainGlobalOptions.CompositeMapAmbient = SceneManager.AmbientLight; 41             TerrainGlobalOptions.CompositeMapDiffuse = l.Diffuse; 42  43             // Configure default import settings for if we use imported image 44             var defaultImp = this.terrainGroup.DefaultImportSettings; 45             defaultImp.TerrainSize = TerrainSize; 46             defaultImp.WorldSize = TerrainWorldSize; 47             defaultImp.InputScale = 600.0f;// 600.0f; 48             defaultImp.MinBatchSize = 33; 49             defaultImp.MaxBatchSize = 65; 50  51             // textures 52             defaultImp.LayerList = new List<LayerInstance>(); 53             var inst = new LayerInstance(); 54             inst.WorldSize = 30;// 100; 55             inst.TextureNames = new List<string>(); 56             inst.TextureNames.Add("dirt_grayrocky_diffusespecular.dds"); 57             inst.TextureNames.Add("dirt_grayrocky_normalheight.dds"); 58             defaultImp.LayerList.Add(inst); 59  60             inst = new LayerInstance(); 61             inst.WorldSize = 30;//30; 62             inst.TextureNames = new List<string>(); 63             inst.TextureNames.Add("grass_green-01_diffusespecular.dds"); 64             inst.TextureNames.Add("grass_green-01_normalheight.dds"); 65             defaultImp.LayerList.Add(inst); 66  67             inst = new LayerInstance(); 68             inst.WorldSize = 30;// 200; 69             inst.TextureNames = new List<string>(); 70             inst.TextureNames.Add("growth_weirdfungus-03_diffusespecular.dds"); 71             inst.TextureNames.Add("growth_weirdfungus-03_normalheight.dds"); 72             defaultImp.LayerList.Add(inst); 73  74             return defaultImp; 75         } 76  77         public void SetupContent() 78         { 79             var blankTerrain = false; 80  81             MaterialManager.Instance.SetDefaultTextureFiltering(TextureFiltering.Anisotropic); 82             MaterialManager.Instance.DefaultAnisotropy = 7; 83  84             SceneManager.SetFog(FogMode.Linear, new ColorEx(0.07f, 0.07f, 0.08f), 0, 10000, 25000); 85  86             var lightDir = new Vector3(0.55f, 0.3f, 0.75f); 87             lightDir.Normalize(); 88  89             var l = SceneManager.CreateLight("tsLight"); 90             l.Type = LightType.Directional; 91             l.Direction = lightDir; 92             l.Diffuse = ColorEx.White; 93             l.Specular = new ColorEx(0.8f, 0.8f, 0.8f); 94  95             SceneManager.AmbientLight = new ColorEx(0.2f, 0.2f, 0.2f); 96  97             this.terrainGroup = new TerrainGroup(SceneManager, Alignment.Align_X_Z, (ushort)TerrainSize, TerrainWorldSize); 98             this.terrainGroup.SetFilenameConvention(TerrainFilePrefix, TerrainFileSuffix); 99             this.terrainGroup.Origin = this.terrainPos;100 101             _configureTerrainDefaults(l);102 103             for (long x = TerrainPageMinX; x <= TerrainPageMaxX; ++x)104             {105                 for (long y = TerrainPageMinY; y <= TerrainPageMaxY; ++y)106                 {107                     _defineTerrain(x, y, blankTerrain);108                 }109             }110             // sync load since we want everything in place when we start111             this.terrainGroup.LoadAllTerrains(true);112             if (this.terrainsImported)113             {114                 foreach (var ts in this.terrainGroup.TerrainSlots)115                 {116                     _initBlendMaps(ts.Instance);117                 }118             }119             this.terrainGroup.FreeTemporaryResources();120 121             var e = SceneManager.CreateEntity("TudoMesh", "tudorhouse.mesh");122             var entPos = new Vector3(this.terrainPos.x + 2043, 0, this.terrainPos.z + 1715);123             var rot = new Quaternion();124             entPos.y = this.terrainGroup.GetHeightAtWorldPosition(entPos) + 65.5 + this.terrainPos.y;125             rot = Quaternion.FromAngleAxis(Utility.RangeRandom(-180, 180), Vector3.UnitY);126             var sn = SceneManager.RootSceneNode.CreateChildSceneNode(entPos, rot);127             sn.Scale = new Vector3(0.12, 0.12, 0.12);128             sn.AttachObject(e);129             this.houseList.Add(e);130 131             e = SceneManager.CreateEntity("TudoMesh1", "tudorhouse.mesh");132             entPos = new Vector3(this.terrainPos.x + 1850, 0, this.terrainPos.z + 1478);133             entPos.y = this.terrainGroup.GetHeightAtWorldPosition(entPos) + 65.5 + this.terrainPos.y;134             rot = Quaternion.FromAngleAxis(Utility.RangeRandom(-180, 180), Vector3.UnitY);135             sn = SceneManager.RootSceneNode.CreateChildSceneNode(entPos, rot);136             sn.Scale = new Vector3(0.12, 0.12, 0.12);137             sn.AttachObject(e);138             this.houseList.Add(e);139 140             e = SceneManager.CreateEntity("TudoMesh2", "tudorhouse.mesh");141             entPos = new Vector3(this.terrainPos.x + 1970, 0, this.terrainPos.z + 2180);142             entPos.y = this.terrainGroup.GetHeightAtWorldPosition(entPos) + 65.5 + this.terrainPos.y;143             rot = Quaternion.FromAngleAxis(Utility.RangeRandom(-180, 180), Vector3.UnitY);144             sn = SceneManager.RootSceneNode.CreateChildSceneNode(entPos, rot);145             sn.Scale = new Vector3(0.12, 0.12, 0.12);146             sn.AttachObject(e);147             this.houseList.Add(e);148         }149 150         private void _defineTerrain(long x, long y, bool flat)151         {152             if (flat)153             {154                 this.terrainGroup.DefineTerrain(x, y, 0);155             }156             else157             {158                 var filename = this.terrainGroup.GenerateFilename(x, y);159                 if (ResourceGroupManager.Instance.ResourceExists(this.terrainGroup.ResourceGroup, filename))160                 {161                     this.terrainGroup.DefineTerrain(x, y);162                 }163                 else164                 {165                     var img = _getTerrainImage(x % 2 != 0, y % 2 != 0);166                     this.terrainGroup.DefineTerrain(x, y, img);167                     this.terrainsImported = true;168                 }169             }170         }171 172         private Image _getTerrainImage(bool flipX, bool flipY)173         {174             var img = Image.FromFile("terrain.png", ResourceGroupManager.DefaultResourceGroupName);175 176             if (flipX)177             {178                 img.FlipAroundY();179             }180 181             if (flipY)182             {183                 img.FlipAroundX();184             }185 186             return img;187         }188 189         private void _initBlendMaps(Axiom.Components.Terrain.Terrain terrain)190         {191             var blendMap0 = terrain.GetLayerBlendMap(1);192             var blendMap1 = terrain.GetLayerBlendMap(2);193             Real minHeight0 = 70;194             Real fadeDist0 = 40;195             Real minHeight1 = 70;196             Real fadeDist1 = 15;197 198             var pBlend1 = blendMap1.BlendPointer;199             var blendIdx = 0;200             for (var y = 0; y < terrain.LayerBlendMapSize; y++)201             {202                 for (var x = 0; x < terrain.LayerBlendMapSize; x++)203                 {204                     Real tx = 0;205                     Real ty = 0;206                     blendMap0.ConvertImageToTerrainSpace(x, y, ref tx, ref ty);207                     Real height = terrain.GetHeightAtTerrainPosition(tx, ty);208                     Real val = (height - minHeight0) / fadeDist0;209                     val = Utility.Clamp(val, 0, 1);210 211                     val = (height - minHeight1) / fadeDist1;212                     val = Utility.Clamp(val, 0, 1);213                     pBlend1[blendIdx++] = val;214                 }215             }216 217             blendMap0.Dirty();218             blendMap1.Dirty();219             blendMap0.Update();220             blendMap1.Update();221         }222 223         public void GetTerrainReyHighter(ref Vector3 chaPos, float timeSinceLastFrame)224         {225             var ray = new Ray(new Vector3(chaPos.x, this.terrainPos.y + 10000, chaPos.z), Vector3.NegativeUnitY);226 227             TerrainGroup.RayResult rayResult = this.terrainGroup.RayIntersects(ray);228             Real distanceAboveTerrain = 5;229             Real fallSpeed = 300;230             Real newy = chaPos.y;231             if (rayResult.Hit)232             {233                 if (chaPos.y > rayResult.Position.y + distanceAboveTerrain)234                 {235                     this.fallVelocity += timeSinceLastFrame * 20;236                     this.fallVelocity = Utility.Min(this.fallVelocity, fallSpeed);237                     newy = chaPos.y - this.fallVelocity * timeSinceLastFrame;238                 }239                 newy = Utility.Max(rayResult.Position.y + distanceAboveTerrain, newy);240                 // Camera.Position = new Vector3(chaPos.x, newy, chaPos.z);241                 chaPos = new Vector3(chaPos.x, newy, chaPos.z);242             }243             if (this.heightUpdateCountDown > 0)244             {245                 this.heightUpdateCountDown -= timeSinceLastFrame;246                 if (this.heightUpdateCountDown <= 0)247                 {248                     this.terrainGroup.Update();249                     this.heightUpdateCountDown = 0;250                 }251             }252         }253     }
TerrainManager

  在方法GetTerrainReyHighter里,我们在角色上面取一点与角色下面取一点组成Ray,然后计算与地形的交互点,得到正确的高度,这个具体过程有点像我原来用PyOpengl里的相关过程初试PyOpenGL二 (Python+OpenGL)基本地形生成与高度检测。地形的LOD等级主要与二方面有关,一个是当前块与摄像机位置,一个是当前块的高度。一般来说,与摄像机与近,当前块越高,lod数值越低,精度越高。

  关于角色的控制SinbadCharacterController用的就是例子程序的,只做了一点小变动,就不说了。

  UI我主要用到一个进度条与光标,提取例子里这些位置组合成下面这个。

  1     public class UIManager : IResourceGroupListener  2     {  3         private string mName = "xinGame";  4   5         protected RenderWindow mWindow;  6         protected ProgressBar LoadBar;  7         protected Real groupInitProportion; // proportion of load job assigned to initialising one resource group  8         protected Real groupLoadProportion; // proportion of load job assigned to loading one resource group  9         protected Real loadInc; 10  11         protected Overlay backdropLayer; // backdrop layer 12         protected OverlayElementContainer backdrop; // backdrop 13  14         protected Overlay cursorLayer; // cursor layer 15         protected OverlayElementContainer cursor; // cursor 16  17         protected Overlay mPriorityLayer; // top priority layer 18         protected OverlayElementContainer mDialogShade; // top priority dialog shade 19  20         public Overlay BackdropLayer 21         { 22             get 23             { 24                 return this.backdropLayer; 25             } 26             protected set 27             { 28                 this.backdropLayer = value; 29             } 30         } 31         public OverlayElement CursorImage 32         { 33             get 34             { 35                 return this.cursor.Children[this.cursor.Name + "/CursorImage"]; 36             } 37         } 38  39         public UIManager(RenderWindow window) 40         { 41             this.mWindow = window; 42             OverlayManager om = OverlayManager.Instance; 43  44             String nameBase = this.mName + "/"; 45             nameBase.Replace( , _); 46             //背景 47             BackdropLayer = om.Create(nameBase + "BackdropLayer"); 48             BackdropLayer.ZOrder = 100; 49             this.backdrop = (OverlayElementContainer)om.Elements.CreateElement("Panel", nameBase + "Backdrop"); 50             BackdropLayer.AddElement(this.backdrop); 51             //光标 52             this.cursorLayer = om.Create(nameBase + "CursorLayer"); 53             this.cursorLayer.ZOrder = 400; 54             this.cursor = (OverlayElementContainer)om.Elements.CreateElementFromTemplate("SdkTrays/Cursor", "Panel", nameBase + "Cursor"); 55             this.cursorLayer.AddElement(this.cursor); 56  57             //进度条 58             this.mPriorityLayer = om.Create(nameBase + "PriorityLayer"); 59             this.mPriorityLayer.ZOrder = 300; 60             this.mDialogShade = (OverlayElementContainer)om.Elements.CreateElement("Panel", nameBase + "DialogShade"); 61             this.mDialogShade.MaterialName = "SdkTrays/Shade"; 62             this.mDialogShade.Hide(); 63             this.mPriorityLayer.AddElement(this.mDialogShade); 64             this.mPriorityLayer.Show(); 65         } 66  67         public void ShowBackdrop(String materialName) 68         { 69             if (materialName != String.Empty) 70             { 71                 this.backdrop.MaterialName = materialName; 72             } 73             BackdropLayer.Show(); 74         } 75  76         public void HideBackdrop() 77         { 78             BackdropLayer.Hide(); 79         } 80  81         public void ShowLoadingBar(int numGroupsInit, int numGroupsLoad, Real initProportion) 82         { 83             if (this.LoadBar != null) 84             { 85                 HideLoadingBar(); 86                 return; 87             } 88             this.LoadBar = new ProgressBar(this.mName + "/LoadingBar", "Loading...", 400, 308); 89             OverlayElement e = this.LoadBar.OverlayElement; 90             this.mDialogShade.AddChild(e); 91             e.VerticalAlignment = VerticalAlignment.Center; 92             e.Left = (-(e.Width / 2)); 93             e.Top = (-(e.Height / 2)); 94             ResourceGroupManager.Instance.AddResourceGroupListener(this);           95             this.mDialogShade.Show(); 96             if (numGroupsInit == 0 && numGroupsLoad != 0) 97             { 98                 this.groupInitProportion = 0; 99                 this.groupLoadProportion = 1;100             }101             else if (numGroupsLoad == 0 && numGroupsInit != 0)102             {103                 this.groupLoadProportion = 0;104                 if (numGroupsInit != 0)105                 {106                     this.groupInitProportion = 1;107                 }108             }109             else if (numGroupsInit == 0 && numGroupsLoad == 0)110             {111                 this.groupInitProportion = 0;112                 this.groupLoadProportion = 0;113             }114             else115             {116                 this.groupInitProportion = initProportion / numGroupsInit;117                 this.groupLoadProportion = (1 - initProportion) / numGroupsLoad;118             }119         }120 121         public void HideLoadingBar()122         {123             if (this.LoadBar != null)124             {125                 this.LoadBar.Cleanup();126                 this.LoadBar = null;127                 ResourceGroupManager.Instance.RemoveResourceGroupListener(this);                128                 this.mDialogShade.Hide();129             }130         }131 132         public void ShowCursor()133         {134             ShowCursor(String.Empty);135         }136 137         public void ShowCursor(String materialName)138         {139             if (materialName != String.Empty)140             {141                 CursorImage.MaterialName = materialName;142             }143 144             if (!this.cursorLayer.IsVisible)145             {146                 this.cursorLayer.Show();147                 RefreshCursor();148             }149         }150 151         public void HideCursor()152         {153             this.cursorLayer.Hide();154         }155 156         public void RefreshCursor()157         {158             var mouseStatus = Mouse.GetState();159             this.cursor.SetPosition(mouseStatus.X, mouseStatus.Y);160         }161 162         public void RefreshCursor(int x, int y)163         {164             this.cursor.SetPosition(x, y);165         }166 167         #region IResourceGroupListener168 169         public void ResourceGroupScriptingStarted(string groupName, int scriptCount)170         {171             this.loadInc = this.groupInitProportion / scriptCount;172             this.LoadBar.Caption = "Parsing...";173             this.mWindow.Update();174         }175 176         public void ScriptParseStarted(string scriptName, ref bool skipThisScript)177         {178             this.LoadBar.Comment = System.IO.Path.GetFileName(scriptName);179             this.mWindow.Update();180         }181 182         public void ScriptParseEnded(string scriptName, bool skipped)183         {184             this.LoadBar.Progress = this.LoadBar.Progress + this.loadInc;185             this.mWindow.Update();186         }187 188         public void ResourceGroupScriptingEnded(string groupName)189         {190         }191 192         public void ResourceGroupPrepareStarted(string groupName, int resourceCount)193         {194         }195 196         public void ResourcePrepareStarted(Resource resource)197         {198         }199 200         public void ResourcePrepareEnded()201         {202         }203 204         public void ResourceGroupPrepareEnded(string groupName)205         {206         }207 208         public void ResourceGroupLoadStarted(string groupName, int resourceCount)209         {210             this.loadInc = this.groupLoadProportion / resourceCount;211             this.LoadBar.Caption = "Loading...";212             this.mWindow.Update();213         }214 215         public void ResourceLoadStarted(Resource resource)216         {217             this.LoadBar.Comment = resource.Name;218             this.mWindow.Update();219         }220 221         public void ResourceLoadEnded()222         {223             this.LoadBar.Progress = this.LoadBar.Progress + this.loadInc;224             this.mWindow.Update();225         }226 227         public void WorldGeometryStageStarted(string description)228         {229             this.LoadBar.Comment = description;230             this.mWindow.Update();231         }232 233         public void WorldGeometryStageEnded()234         {235             this.LoadBar.Progress = this.LoadBar.Progress + this.loadInc;236             this.mWindow.Update();237         }238 239         public void ResourceGroupLoadEnded(string groupName)240         {241         }242         #endregion243     }
UIManager

  效果和例子里的一样。

  UIManager这个主要是熟悉下相应的Overlay里相关类的用法,相关label,button,checkbox等这些控件都可以由Overlay,OverlayElementContainer,OverlayElement这些元素组成,这些元素相关属性都可以从对应的overlay文本文件反序列化得到,这样我们可以直接修改相关属性产生不同的效果,不需要改动代码,后面应该会详细介绍Overlay相关元素的代码与方法。需要说明的是,这个项目是控制台应用程序,建winform项目在这没用,上下文环境不一样,相关窗口,UI是不能混用的。

  CameraManager这个类在这列出来,这里没怎么用,相关的鼠标与键盘处理都在TerrainManager中,在这也不列出来了。

  附件XinGame.zip.需要大家自己下载Axiom项目,相关bin目录里引用的DLL有些多,上传限制就不发了。里面因地图比较大,我移动速度调的很快,大家可以自己修改。wsad方向,鼠标按下移动调整方向。

  

 

Axiom3D写游戏:第一个窗口