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来处理键盘与鼠标反馈,经过修改,下面是代码.

     public abstract class Game : IDisposable, IWindowEventListener
{
protected Root Engine;
protected IConfigurationManager ConfigurationManager;
protected ResourceGroupManager Content;
protected SceneManager SceneManager;
protected Camera Camera;
protected Viewport Viewport;
protected RenderWindow Window;
protected Axiom.Graphics.RenderSystem RenderSystem; public virtual void Run()
{
PreInitialize();
LoadConfiguration();
Initialize();
CreateRenderSystem();
CreateRenderWindow();
LoadResource();
CreateSceneManager();
CreateCamera();
CreateViewports();
CreateInput();
CreateScene();
this.Engine.StartRendering();
} private void PreInitialize()
{
this.ConfigurationManager = new DefaultConfigurationManager(); // instantiate the Root singleton
this.Engine = new Root(this.ConfigurationManager.LogFilename); // add event handlers for frame events
this.Engine.FrameStarted += Engine_FrameRenderingQueued;
} public virtual void LoadConfiguration()
{
this.ConfigurationManager.RestoreConfiguration(this.Engine);
} private void Engine_FrameRenderingQueued(object source, FrameEventArgs e)
{
Update(e.TimeSinceLastFrame);
} public virtual void Initialize()
{
} public virtual void CreateRenderSystem()
{
if (this.Engine.RenderSystem == null)
{
this.RenderSystem = this.Engine.RenderSystem = this.Engine.RenderSystems.First().Value;
}
else
{
this.RenderSystem = this.Engine.RenderSystem;
}
} public virtual void CreateRenderWindow()
{
this.Window = Root.Instance.Initialize(true, "Xin Game"); WindowEventMonitor.Instance.RegisterListener(this.Window, this);
} public virtual void LoadResource()
{
ResourceGroupManager.Instance.InitializeAllResourceGroups();
} public virtual void CreateSceneManager()
{
// Get the SceneManager, a generic one by default
this.SceneManager = this.Engine.CreateSceneManager("DefaultSceneManager", "GameSMInstance");
this.SceneManager.ClearScene();
} public virtual void CreateCamera()
{
// create a camera and initialize its position
this.Camera = this.SceneManager.CreateCamera("MainCamera");
this.Camera.Position = new Vector3(, , );
this.Camera.LookAt(new Vector3(, , -)); } public virtual void CreateViewports()
{
// create a new viewport and set it's background color
this.Viewport = this.Window.AddViewport(this.Camera, , , 1.0f, 1.0f, );
this.Viewport.BackgroundColor = ColorEx.SteelBlue;
} public virtual void CreateInput()
{
//var window = this.Window["nativewindow"];
} public abstract void CreateScene(); public virtual void Update(float timeSinceLastFrame)
{
} #region IDisposable Implementation #region IsDisposed Property /// <summary>
/// Determines if this instance has been disposed of already.
/// </summary>
public bool IsDisposed { get; set; } #endregion IsDisposed Property /// <summary>
/// Class level dispose method
/// </summary>
/// <remarks>
/// When implementing this method in an inherited class the following template should be used;
/// protected override void dispose( bool disposeManagedResources )
/// {
/// if ( !IsDisposed )
/// {
/// if ( disposeManagedResources )
/// {
/// // Dispose managed resources.
/// }
///
/// // If there are unmanaged resources to release,
/// // they need to be released here.
/// }
///
/// // If it is available, make the call to the
/// // base class's Dispose(Boolean) method
/// base.dispose( disposeManagedResources );
/// }
/// </remarks>
/// <param name="disposeManagedResources">True if Unmanaged resources should be released.</param>
protected virtual void dispose(bool disposeManagedResources)
{
if (!IsDisposed)
{
if (disposeManagedResources)
{
if (this.Engine != null)
{
// remove event handlers
this.Engine.FrameStarted -= Engine_FrameRenderingQueued;
}
if (this.SceneManager != null)
{
this.SceneManager.RemoveAllCameras();
}
this.Camera = null;
if (Root.Instance != null)
{
Root.Instance.RenderSystem.DetachRenderTarget(this.Window);
}
if (this.Window != null)
{
WindowEventMonitor.Instance.UnregisterWindow(this.Window);
this.Window.Dispose();
}
if (this.Engine != null)
{
this.Engine.Dispose();
}
} // There are no unmanaged resources to release, but
// if we add them, they need to be released here.
}
IsDisposed = true;
} /// <summary>
/// Call to when class is no longer needed
/// </summary>
public void Dispose()
{
dispose(true);
GC.SuppressFinalize(this);
} ~Game()
{
dispose(false);
} #endregion IDisposable Implementation #region IWindowEventListener Implementation /// <summary>
/// Window has moved position
/// </summary>
/// <param name="rw">The RenderWindow which created this event</param>
public void WindowMoved(RenderWindow rw)
{
} /// <summary>
/// Window has resized
/// </summary>
/// <param name="rw">The RenderWindow which created this event</param>
public void WindowResized(RenderWindow rw)
{
} /// <summary>
/// Window has closed
/// </summary>
/// <param name="rw">The RenderWindow which created this event</param>
public void WindowClosed(RenderWindow rw)
{
// Only do this for the Main Window
if (rw == this.Window)
{
Root.Instance.QueueEndRendering();
}
} /// <summary>
/// Window lost/regained the focus
/// </summary>
/// <param name="rw">The RenderWindow which created this event</param>
public void WindowFocusChange(RenderWindow rw)
{
} #endregion
}

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组件显示。

     public class OpenTKGame : Game
{
public OpenTKGame()
{ } #region property
public KeyboardState KeyboardState
{
get
{
return Keyboard.GetState();
}
} public MouseState MouseState
{
get
{
return Mouse.GetState();
}
} #endregion protected SinbadCharacterController chara;
protected CameraManager cameraManager;
protected UIManager uiManager;
protected TerrainManager terrainManager; private bool bLoadTerrain = true;
private bool bDown = false;
public override void CreateRenderWindow()
{
base.CreateRenderWindow();
} public override void LoadResource()
{
//ResourceGroupManager.Instance.InitializeResourceGroup("Popular");
ResourceGroupManager.Instance.InitializeResourceGroup("Essential");
uiManager = new UIManager(this.Window);
} public override void CreateCamera()
{
//uiManager.ShowBackdrop("SdkTrays/Bands");
base.CreateCamera();
this.cameraManager = new CameraManager(this.Camera);
this.terrainManager = new TerrainManager(this.SceneManager);
} public override void CreateInput()
{
base.CreateInput();
var window = this.Window["nativewindow"] as OpenTK.NativeWindow;
if (window == null)
{
throw new NotImplementedException();
}
var mouse = window.InputDriver.Mouse[];
var keyboard = window.InputDriver.Keyboard[];
mouse.ButtonDown += mouse_ButtonDown;
mouse.ButtonUp += mouse_ButtonUp;
mouse.Move += mouse_Move; keyboard.KeyDown += keyboard_KeyDown;
keyboard.KeyUp += keyboard_KeyUp;
} public override void CreateScene()
{
// set background and some fog
Viewport.BackgroundColor = new ColorEx(1.0f, 1.0f, 0.8f);
SceneManager.SetFog(FogMode.Linear, new ColorEx(1.0f, 1.0f, 1.0f), , , );
// set shadow properties
//SceneManager.ShadowTechnique = ShadowTechnique.TextureModulative;
SceneManager.ShadowColor = new ColorEx(0.5f, 0.5f, 0.5f);
SceneManager.ShadowTextureSize = ;
SceneManager.ShadowTextureCount = ;
// disable default camera control so the character can do its own
cameraManager.setStyle(CameraStyle.Manual);
// use a small amount of ambient lighting
SceneManager.AmbientLight = new ColorEx(0.3f, 0.3f, 0.3f); // add a bright light above the scene
Light light = SceneManager.CreateLight("CharacterLight");
light.Type = LightType.Point;
light.Position = new Vector3(-, , );
light.Specular = ColorEx.White; this.uiManager.ShowBackdrop("SdkTrays/Bands");
this.uiManager.ShowLoadingBar(, , 0.7f);
ResourceGroupManager.Instance.InitializeResourceGroup("Popular");
if (this.bLoadTerrain)
{
terrainManager.SetupContent();
}
this.uiManager.HideLoadingBar();
this.uiManager.HideBackdrop(); ShowCursor();
uiManager.ShowCursor(); MeshManager.Instance.CreatePlane("floor", ResourceGroupManager.DefaultResourceGroupName,
new Plane(Vector3.UnitY, ), , , , , true, , , , Vector3.UnitZ); // create a floor entity, give it a material, and place it at the origin
Entity floor = SceneManager.CreateEntity("Floor", "floor");
floor.MaterialName = "Examples/Rockwall";
floor.CastShadows = false;
SceneManager.RootSceneNode.AttachObject(floor); // create our character controller
this.chara = new SinbadCharacterController(Camera);
this.chara.bodyNode.Position = new Vector3(, , );
} public override void Update(float timeSinceLastFrame)
{
base.Update(timeSinceLastFrame);
this.cameraManager.frameRenderingQueued(timeSinceLastFrame);
this.chara.AddTime(timeSinceLastFrame);
if (bLoadTerrain)
{
var position = this.chara.bodyNode.Position;
terrainManager.GetTerrainReyHighter(ref position, timeSinceLastFrame);
this.chara.bodyNode.Position = position;
}
} [DllImport("user32.dll", EntryPoint = "ShowCursor", CharSet = CharSet.Auto)]
public extern static void ShowCursor(int status); #region event
void keyboard_KeyUp(object sender, KeyboardKeyEventArgs e)
{
this.cameraManager.injectKeyUp(e);
this.chara.InjectKeyUp(e);
} void keyboard_KeyDown(object sender, KeyboardKeyEventArgs e)
{
switch (e.Key)
{
case Key.R:
switch (this.Camera.PolygonMode)
{
case PolygonMode.Points:
this.Viewport.Camera.PolygonMode = PolygonMode.Solid;
break;
case PolygonMode.Solid:
this.Viewport.Camera.PolygonMode = PolygonMode.Wireframe;
break;
case PolygonMode.Wireframe:
this.Viewport.Camera.PolygonMode = PolygonMode.Points;
break;
}
break;
default:
break;
} this.cameraManager.injectKeyDown(e);
this.chara.InjectKeyDown(e);
} void mouse_Move(object sender, MouseMoveEventArgs e)
{
this.cameraManager.injectMouseMove(e);
if (bDown)
{
this.chara.InjectMouseMove(e);
}
uiManager.RefreshCursor(e.X, e.Y);
} void mouse_ButtonUp(object sender, MouseButtonEventArgs e)
{
this.cameraManager.injectMouseUp(e);
bDown = false;
} void mouse_ButtonDown(object sender, MouseButtonEventArgs e)
{
this.cameraManager.injectMouseDown(e);
this.chara.InjectMouseDown(e);
bDown = true;
}
#endregion
}

OpenTKGame

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

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

     public class TerrainManager
{
private const string TerrainFilePrefix = "TestTerrain";
private const string TerrainFileSuffix = "dat";
private const float TerrainWorldSize = ;
private const int TerrainSize = ; private const int TerrainPageMinX = ;
private const int TerrainPageMinY = ;
private const int TerrainPageMaxX = ;
private const int TerrainPageMaxY = ; protected bool terrainsImported = false; protected Real fallVelocity;
protected Real heightUpdateCountDown;
protected Real heightUpdateRate;
protected Vector3 terrainPos = new Vector3(, , );
protected Real brushSizeTerrainSpace = 0.02f; protected TerrainGlobalOptions terrainGlobals;
protected TerrainGroup terrainGroup;
protected List<Entity> houseList = new List<Entity>(); private SceneManager SceneManager; public TerrainManager(SceneManager sceneManager)
{
this.heightUpdateRate = 1.0f / 2.0f;
this.SceneManager = sceneManager;
} private ImportData _configureTerrainDefaults(Light l)
{
// Configure global
TerrainGlobalOptions.MaxPixelError = ;
// testing composite map
TerrainGlobalOptions.CompositeMapDistance = ;
TerrainGlobalOptions.LightMapDirection = l.DerivedDirection;
//TerrainGlobalOptions.CompositeMapAmbient = SceneManager.AmbientLight;
TerrainGlobalOptions.CompositeMapDiffuse = l.Diffuse; // Configure default import settings for if we use imported image
var defaultImp = this.terrainGroup.DefaultImportSettings;
defaultImp.TerrainSize = TerrainSize;
defaultImp.WorldSize = TerrainWorldSize;
defaultImp.InputScale = 600.0f;// 600.0f;
defaultImp.MinBatchSize = ;
defaultImp.MaxBatchSize = ; // textures
defaultImp.LayerList = new List<LayerInstance>();
var inst = new LayerInstance();
inst.WorldSize = ;// 100;
inst.TextureNames = new List<string>();
inst.TextureNames.Add("dirt_grayrocky_diffusespecular.dds");
inst.TextureNames.Add("dirt_grayrocky_normalheight.dds");
defaultImp.LayerList.Add(inst); inst = new LayerInstance();
inst.WorldSize = ;//30;
inst.TextureNames = new List<string>();
inst.TextureNames.Add("grass_green-01_diffusespecular.dds");
inst.TextureNames.Add("grass_green-01_normalheight.dds");
defaultImp.LayerList.Add(inst); inst = new LayerInstance();
inst.WorldSize = ;// 200;
inst.TextureNames = new List<string>();
inst.TextureNames.Add("growth_weirdfungus-03_diffusespecular.dds");
inst.TextureNames.Add("growth_weirdfungus-03_normalheight.dds");
defaultImp.LayerList.Add(inst); return defaultImp;
} public void SetupContent()
{
var blankTerrain = false; MaterialManager.Instance.SetDefaultTextureFiltering(TextureFiltering.Anisotropic);
MaterialManager.Instance.DefaultAnisotropy = ; SceneManager.SetFog(FogMode.Linear, new ColorEx(0.07f, 0.07f, 0.08f), , , ); var lightDir = new Vector3(0.55f, 0.3f, 0.75f);
lightDir.Normalize(); var l = SceneManager.CreateLight("tsLight");
l.Type = LightType.Directional;
l.Direction = lightDir;
l.Diffuse = ColorEx.White;
l.Specular = new ColorEx(0.8f, 0.8f, 0.8f); SceneManager.AmbientLight = new ColorEx(0.2f, 0.2f, 0.2f); this.terrainGroup = new TerrainGroup(SceneManager, Alignment.Align_X_Z, (ushort)TerrainSize, TerrainWorldSize);
this.terrainGroup.SetFilenameConvention(TerrainFilePrefix, TerrainFileSuffix);
this.terrainGroup.Origin = this.terrainPos; _configureTerrainDefaults(l); for (long x = TerrainPageMinX; x <= TerrainPageMaxX; ++x)
{
for (long y = TerrainPageMinY; y <= TerrainPageMaxY; ++y)
{
_defineTerrain(x, y, blankTerrain);
}
}
// sync load since we want everything in place when we start
this.terrainGroup.LoadAllTerrains(true);
if (this.terrainsImported)
{
foreach (var ts in this.terrainGroup.TerrainSlots)
{
_initBlendMaps(ts.Instance);
}
}
this.terrainGroup.FreeTemporaryResources(); var e = SceneManager.CreateEntity("TudoMesh", "tudorhouse.mesh");
var entPos = new Vector3(this.terrainPos.x + , , this.terrainPos.z + );
var rot = new Quaternion();
entPos.y = this.terrainGroup.GetHeightAtWorldPosition(entPos) + 65.5 + this.terrainPos.y;
rot = Quaternion.FromAngleAxis(Utility.RangeRandom(-, ), Vector3.UnitY);
var sn = SceneManager.RootSceneNode.CreateChildSceneNode(entPos, rot);
sn.Scale = new Vector3(0.12, 0.12, 0.12);
sn.AttachObject(e);
this.houseList.Add(e); e = SceneManager.CreateEntity("TudoMesh1", "tudorhouse.mesh");
entPos = new Vector3(this.terrainPos.x + , , this.terrainPos.z + );
entPos.y = this.terrainGroup.GetHeightAtWorldPosition(entPos) + 65.5 + this.terrainPos.y;
rot = Quaternion.FromAngleAxis(Utility.RangeRandom(-, ), Vector3.UnitY);
sn = SceneManager.RootSceneNode.CreateChildSceneNode(entPos, rot);
sn.Scale = new Vector3(0.12, 0.12, 0.12);
sn.AttachObject(e);
this.houseList.Add(e); e = SceneManager.CreateEntity("TudoMesh2", "tudorhouse.mesh");
entPos = new Vector3(this.terrainPos.x + , , this.terrainPos.z + );
entPos.y = this.terrainGroup.GetHeightAtWorldPosition(entPos) + 65.5 + this.terrainPos.y;
rot = Quaternion.FromAngleAxis(Utility.RangeRandom(-, ), Vector3.UnitY);
sn = SceneManager.RootSceneNode.CreateChildSceneNode(entPos, rot);
sn.Scale = new Vector3(0.12, 0.12, 0.12);
sn.AttachObject(e);
this.houseList.Add(e);
} private void _defineTerrain(long x, long y, bool flat)
{
if (flat)
{
this.terrainGroup.DefineTerrain(x, y, );
}
else
{
var filename = this.terrainGroup.GenerateFilename(x, y);
if (ResourceGroupManager.Instance.ResourceExists(this.terrainGroup.ResourceGroup, filename))
{
this.terrainGroup.DefineTerrain(x, y);
}
else
{
var img = _getTerrainImage(x % != , y % != );
this.terrainGroup.DefineTerrain(x, y, img);
this.terrainsImported = true;
}
}
} private Image _getTerrainImage(bool flipX, bool flipY)
{
var img = Image.FromFile("terrain.png", ResourceGroupManager.DefaultResourceGroupName); if (flipX)
{
img.FlipAroundY();
} if (flipY)
{
img.FlipAroundX();
} return img;
} private void _initBlendMaps(Axiom.Components.Terrain.Terrain terrain)
{
var blendMap0 = terrain.GetLayerBlendMap();
var blendMap1 = terrain.GetLayerBlendMap();
Real minHeight0 = ;
Real fadeDist0 = ;
Real minHeight1 = ;
Real fadeDist1 = ; var pBlend1 = blendMap1.BlendPointer;
var blendIdx = ;
for (var y = ; y < terrain.LayerBlendMapSize; y++)
{
for (var x = ; x < terrain.LayerBlendMapSize; x++)
{
Real tx = ;
Real ty = ;
blendMap0.ConvertImageToTerrainSpace(x, y, ref tx, ref ty);
Real height = terrain.GetHeightAtTerrainPosition(tx, ty);
Real val = (height - minHeight0) / fadeDist0;
val = Utility.Clamp(val, , ); val = (height - minHeight1) / fadeDist1;
val = Utility.Clamp(val, , );
pBlend1[blendIdx++] = val;
}
} blendMap0.Dirty();
blendMap1.Dirty();
blendMap0.Update();
blendMap1.Update();
} public void GetTerrainReyHighter(ref Vector3 chaPos, float timeSinceLastFrame)
{
var ray = new Ray(new Vector3(chaPos.x, this.terrainPos.y + , chaPos.z), Vector3.NegativeUnitY); TerrainGroup.RayResult rayResult = this.terrainGroup.RayIntersects(ray);
Real distanceAboveTerrain = ;
Real fallSpeed = ;
Real newy = chaPos.y;
if (rayResult.Hit)
{
if (chaPos.y > rayResult.Position.y + distanceAboveTerrain)
{
this.fallVelocity += timeSinceLastFrame * ;
this.fallVelocity = Utility.Min(this.fallVelocity, fallSpeed);
newy = chaPos.y - this.fallVelocity * timeSinceLastFrame;
}
newy = Utility.Max(rayResult.Position.y + distanceAboveTerrain, newy);
// Camera.Position = new Vector3(chaPos.x, newy, chaPos.z);
chaPos = new Vector3(chaPos.x, newy, chaPos.z);
}
if (this.heightUpdateCountDown > )
{
this.heightUpdateCountDown -= timeSinceLastFrame;
if (this.heightUpdateCountDown <= )
{
this.terrainGroup.Update();
this.heightUpdateCountDown = ;
}
}
}
}

TerrainManager

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

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

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

     public class UIManager : IResourceGroupListener
{
private string mName = "xinGame"; protected RenderWindow mWindow;
protected ProgressBar LoadBar;
protected Real groupInitProportion; // proportion of load job assigned to initialising one resource group
protected Real groupLoadProportion; // proportion of load job assigned to loading one resource group
protected Real loadInc; protected Overlay backdropLayer; // backdrop layer
protected OverlayElementContainer backdrop; // backdrop protected Overlay cursorLayer; // cursor layer
protected OverlayElementContainer cursor; // cursor protected Overlay mPriorityLayer; // top priority layer
protected OverlayElementContainer mDialogShade; // top priority dialog shade public Overlay BackdropLayer
{
get
{
return this.backdropLayer;
}
protected set
{
this.backdropLayer = value;
}
}
public OverlayElement CursorImage
{
get
{
return this.cursor.Children[this.cursor.Name + "/CursorImage"];
}
} public UIManager(RenderWindow window)
{
this.mWindow = window;
OverlayManager om = OverlayManager.Instance; String nameBase = this.mName + "/";
nameBase.Replace(' ', '_');
//背景
BackdropLayer = om.Create(nameBase + "BackdropLayer");
BackdropLayer.ZOrder = ;
this.backdrop = (OverlayElementContainer)om.Elements.CreateElement("Panel", nameBase + "Backdrop");
BackdropLayer.AddElement(this.backdrop);
//光标
this.cursorLayer = om.Create(nameBase + "CursorLayer");
this.cursorLayer.ZOrder = ;
this.cursor = (OverlayElementContainer)om.Elements.CreateElementFromTemplate("SdkTrays/Cursor", "Panel", nameBase + "Cursor");
this.cursorLayer.AddElement(this.cursor); //进度条
this.mPriorityLayer = om.Create(nameBase + "PriorityLayer");
this.mPriorityLayer.ZOrder = ;
this.mDialogShade = (OverlayElementContainer)om.Elements.CreateElement("Panel", nameBase + "DialogShade");
this.mDialogShade.MaterialName = "SdkTrays/Shade";
this.mDialogShade.Hide();
this.mPriorityLayer.AddElement(this.mDialogShade);
this.mPriorityLayer.Show();
} public void ShowBackdrop(String materialName)
{
if (materialName != String.Empty)
{
this.backdrop.MaterialName = materialName;
}
BackdropLayer.Show();
} public void HideBackdrop()
{
BackdropLayer.Hide();
} public void ShowLoadingBar(int numGroupsInit, int numGroupsLoad, Real initProportion)
{
if (this.LoadBar != null)
{
HideLoadingBar();
return;
}
this.LoadBar = new ProgressBar(this.mName + "/LoadingBar", "Loading...", , );
OverlayElement e = this.LoadBar.OverlayElement;
this.mDialogShade.AddChild(e);
e.VerticalAlignment = VerticalAlignment.Center;
e.Left = (-(e.Width / ));
e.Top = (-(e.Height / ));
ResourceGroupManager.Instance.AddResourceGroupListener(this);
this.mDialogShade.Show();
if (numGroupsInit == && numGroupsLoad != )
{
this.groupInitProportion = ;
this.groupLoadProportion = ;
}
else if (numGroupsLoad == && numGroupsInit != )
{
this.groupLoadProportion = ;
if (numGroupsInit != )
{
this.groupInitProportion = ;
}
}
else if (numGroupsInit == && numGroupsLoad == )
{
this.groupInitProportion = ;
this.groupLoadProportion = ;
}
else
{
this.groupInitProportion = initProportion / numGroupsInit;
this.groupLoadProportion = ( - initProportion) / numGroupsLoad;
}
} public void HideLoadingBar()
{
if (this.LoadBar != null)
{
this.LoadBar.Cleanup();
this.LoadBar = null;
ResourceGroupManager.Instance.RemoveResourceGroupListener(this);
this.mDialogShade.Hide();
}
} public void ShowCursor()
{
ShowCursor(String.Empty);
} public void ShowCursor(String materialName)
{
if (materialName != String.Empty)
{
CursorImage.MaterialName = materialName;
} if (!this.cursorLayer.IsVisible)
{
this.cursorLayer.Show();
RefreshCursor();
}
} public void HideCursor()
{
this.cursorLayer.Hide();
} public void RefreshCursor()
{
var mouseStatus = Mouse.GetState();
this.cursor.SetPosition(mouseStatus.X, mouseStatus.Y);
} public void RefreshCursor(int x, int y)
{
this.cursor.SetPosition(x, y);
} #region IResourceGroupListener public void ResourceGroupScriptingStarted(string groupName, int scriptCount)
{
this.loadInc = this.groupInitProportion / scriptCount;
this.LoadBar.Caption = "Parsing...";
this.mWindow.Update();
} public void ScriptParseStarted(string scriptName, ref bool skipThisScript)
{
this.LoadBar.Comment = System.IO.Path.GetFileName(scriptName);
this.mWindow.Update();
} public void ScriptParseEnded(string scriptName, bool skipped)
{
this.LoadBar.Progress = this.LoadBar.Progress + this.loadInc;
this.mWindow.Update();
} public void ResourceGroupScriptingEnded(string groupName)
{
} public void ResourceGroupPrepareStarted(string groupName, int resourceCount)
{
} public void ResourcePrepareStarted(Resource resource)
{
} public void ResourcePrepareEnded()
{
} public void ResourceGroupPrepareEnded(string groupName)
{
} public void ResourceGroupLoadStarted(string groupName, int resourceCount)
{
this.loadInc = this.groupLoadProportion / resourceCount;
this.LoadBar.Caption = "Loading...";
this.mWindow.Update();
} public void ResourceLoadStarted(Resource resource)
{
this.LoadBar.Comment = resource.Name;
this.mWindow.Update();
} public void ResourceLoadEnded()
{
this.LoadBar.Progress = this.LoadBar.Progress + this.loadInc;
this.mWindow.Update();
} public void WorldGeometryStageStarted(string description)
{
this.LoadBar.Comment = description;
this.mWindow.Update();
} public void WorldGeometryStageEnded()
{
this.LoadBar.Progress = this.LoadBar.Progress + this.loadInc;
this.mWindow.Update();
} public void ResourceGroupLoadEnded(string groupName)
{
}
#endregion
}

UIManager

  效果和例子里的一样。

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

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

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

  

Axiom3D写游戏:第一个窗口的更多相关文章

  1. Axiom3D写游戏:用Overlay实现Mesh浏览.

    从网上找了些资源,大多搜Ogre,Mesh资源,然后为了方便查看各个Mesh,以及对应骨骼动画.为了实用性,考虑放在原游戏窗口里实现.最开始打算窗口新建viewport来实现,后发现这种方式的局限性, ...

  2. Python初学者随笔(一)_ 用Python写的第一个游戏“猜数字”

    如标题所写,这篇随笔主要记录下学习Python过程中用Python写的第一个游戏--"猜数字"_跟着"小甲鱼"学Python,链接: https://b23.t ...

  3. 【C语言探索之旅】 第三部分第二课:SDL开发游戏之创建窗口和画布

    内容简介 1.第三部分第二课: SDL开发游戏之创建窗口和画布 2.第三部分第三课预告: SDL开发游戏之显示图像 第三部分第二课:SDL开发游戏之创建窗口和画布 在上一课中,我们对SDL这个开源库做 ...

  4. 怎样成为一个游戏制作人——第五章:使用GGE图形库来写游戏

    怎样成为一个游戏制作人--第五章:使用GGE图形库来写游戏 前言: 细致想了一下,来看博客的一般都是有自学能力的了.C++基础多少也会有一些了. 于是决定以下的章节.会教大家做一些小游戏. 来巩固自己 ...

  5. DirectX全屏游戏中弹出窗口(转)

    一直有人问如何在DirectX全屏游戏中弹出窗口就象金山游侠一样.我答应过要给出原码,只是一直没有时间整理,不过现在总算是弄玩了.代码不长,大致作了些注释,但愿你能看懂:)按照我的说明一步步作应该就能 ...

  6. 自定义View4-塔防小游戏第一篇:一个防御塔+多个野怪(简易版)*

    塔防小游戏 第一篇:一个防御塔+多个野怪(简易版)    1.canvas画防御塔,妖怪大道,妖怪行走路线    2.防御塔攻击范围是按照妖怪与防御塔中心距离计算的,大于防御塔半径则不攻击,小于则攻击 ...

  7. WinUI 3 踩坑记:第一个窗口

    本文是 WinUI 3 踩坑记 的一部分,该系列发布于 GitHub@Scighost/WinUI3Keng,文中的代码也在此仓库中,若内容出现冲突以 GitHub 上的为准. WinUI 3 应用的 ...

  8. 回合对战制游戏第一篇(初识java)

    回合对战制游戏第一篇 一,所谓的java. java是一门完全面向对象的编程语言,而之前所接触到的C语言是一门面向有一个过程的语音,对于两个的区别应该有一个清楚的认识. java的第一个内容. 类和对 ...

  9. 万事开头难,用HTML写的第一个界面,收获颇多

        很开心跟了叶老师学习和做项目,基础不好,前期他会帮你安排好学习路线和计划.前期没有项目做,叶老师先让我先学习jQuery,给我推荐了一些网站,叫我一边学习,一边写博客.其实很早就有想写博客的想 ...

随机推荐

  1. MD5 和的价值体现在哪里,它是用来做什么的?

    MD5 和的价值体现在哪里,它是用来做什么的? MD5 和是由字母和数字构成的字符串,起到了文件指纹的作用.如果两个文件有相同的 MD5 和值,那么,文件完全相同.您可以为每一软件下载使用所提供的 M ...

  2. create-react-app 使用详解

    快速开始 npm install -g create-react-app create-react-app my-app cd my-app/ npm start 通过http://localhost ...

  3. mySql索引优化分析

    MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字 ...

  4. 我的IT之路2013(一)

    一眨眼又到了写总结的时候了.废话不多说了,直接切入正题. 春节过后 从春节前开始大概半个月的时间就开始在TKY做物资管理项目,中间穿插了两个考试和J2EE的学习:结束TKY工作后继续深入学习J2EE, ...

  5. angular中的jqLite所包含的jquery API

    Angular本身包含了一个叫做jqLite的可兼容性库. 使用过的angular.element()方法就返回一个jqLite对象,  jqLite是jQuery库的子集,它 允许Angular以跨 ...

  6. 构造函数 (C++)

    构造函数是一种可初始化其类的实例的成员函数. 构造函数具有与类相同的名称,没有返回值. 构造函数可以具有任意数量的参数,类可以具有任意数量的重载构造函数. 构造函数可以具有任何可访问性(公共.受保护或 ...

  7. python virtualenv使用

    1.什么是virtualenv virtualenv用来做环境隔离,比如项目A使用了python2,项目B使用了python3 使用virtualenv可以分别生成项目A和项目B的环境包 2.virt ...

  8. vue2.0如何自定义全局变量的方法

    方法一:http://www.jianshu.com/p/04dffe7a6b74 //在mian.js中写入函数 Vue.prototype.changeData = function (){ al ...

  9. pip升级Python程序包

    列出当前安装的包: pip list 列出可升级的包: pip list --outdate 升级一个包: pip install --upgrade requests // mac,linux,un ...

  10. Android.mk高级写法

    转:http://blog.csdn.net/langresser_king/article/details/8275291 原本只是想记录一些常用的使用技巧,但是越写越得意(>_<),忍 ...