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. [sql]mysql参数(配置)手册

    my-innodb-heavy-4G.cnf配置文件注解 [client] #客户端 port = 3306 #mysql客户端连接时的默认端口号 socket = /application/mysq ...

  2. node webkit(nw.js) 设置自动更新

    原理:把更新的文件放在服务器上,设置一个客户端版本号,每次打开客户端的时候,通过接口获取服务器上的版本,如果高于本地的版本就下载服务器上的代码,低于或等于就不更新 <script> var ...

  3. 用NPOI创建Excel、合并单元格、设置单元格样式、边框的方法

    本篇文章小编为大家介绍,用NPOI创建Excel.合并单元格.设置单元格样式.边框的方法.需要的朋友参考下 今天在做项目中,遇到使用代码生成具有一定样式的Excel,找了很多资料,最后终于解决了,Ex ...

  4. angular.js测试框架protracotr安装所需的node版本

    protractor内代码的语法是基于ES6的,比如:里面用到了展开运算符“...”,node.js 6.0以下是不支持该语法特性. 所以,安装protractor是不会报错,但运行webdriver ...

  5. u3d中刚体与碰撞体的理解以及is Trigger属性的意义

    刚体:个人理解就是具有物理属性(如:质量),接受物理作用(如:重力)的组件. 碰撞体:个人理解就是计算碰撞后的物理量(如:弹力). 刚体与碰撞体的关系:个人理解判断碰撞体就是需要计算力,如果碰撞的物体 ...

  6. python 三元表达式 if for 构建List 进阶用法

    1.简单的for...[if]...语句 Python中,for...[if]...语句一种简洁的构建List的方法,从for给定的List中选择出满足if条件的元素组成新的List,其中if是可以省 ...

  7. 【内核】探究linux内核,超详细解析子系统

    Perface 前面已经写过一篇<嵌入式linux内核的五个子系统>,概括性比较强,也比较简略,现在对其进行补充说明. 仅留此笔记,待日后查看及补充! Linux内核的子系统 内核是操作系 ...

  8. python用zipfile模块打包文件或是目录、解压zip文件实例

    #!/usr/bin/env python # -*- coding: utf-8 -*- from zipfile import * import zipfile #解压zip文件 def unzi ...

  9. 手机抓包fiddle4的安装及配置

    抓手机包可以用的是fiddle. 安装 先在下载页面下载--->Download Fiddler Web Debugging Tool for Free by Telerik 选择你“准备用fi ...

  10. jQuery 学习笔记2 点击时弹出一个对话框

    上次学习的是页面加载完成后弹出一个警告框,这里我们改为当用户点击后弹出一个警告框. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Trans ...