第四部分 结论和展望 通过对游戏世界的演绎,对游戏逻辑的归纳,以及对游戏底层工具的融合,逐步诞生了我们现在的框架。为了使框架更能经得起检验,我们使用了一些设计模式提供的方法来保证框架的安全性。现在的高层游戏引擎说明了一点,对于这个游戏,我们的框架达标了。但用这个框架开发一个新的游戏是否也会达标?答案是否定的。如果用这个框架去开发一个RPG游戏,那我们还缺技能、道具等诸多系统;如果去开发一个纸牌游戏,似乎我们的框架对于规则支持还不是太方便;如果去开发一个足球游戏,似乎我们的框架对AI的支持太差劲……而且就OGRE本身也在不断更新、提供新的功能,甚至有时候否定原有的类和接口。不过把话说回来,现在什么游戏引擎没有这种问题呢?写底层引擎容易,但是写一个通用的高层引擎层却很复杂,因为底层引擎只是跟变化缓慢的平台技术相关,但高层引擎层却是跟丰富多彩的游戏相关的。不仅如此,高层引擎的存在同时受底层引擎的功能限制。 需要走的路还很长,即便是对于国际知名的大公司,也是在不断的探索和实践中。但这并不表明前途就是渺茫的,我们通过这次实践作出了一次从底而上和自上而下的归纳和演绎的过程,在变化多端的具体游戏和变化缓慢的引擎中间寻找到了一个引擎层契合点。内容繁复、采用的表现手段多姿多彩的游戏世界,即便是再复杂,也可以通过一定程度的归纳演绎得出适应其规则和表现的契合点和高层引擎层架构。
附录
Terrain Example /* -----------------------------------------------------------------------------
This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright ?2000-2003 The OGRE Team Also see acknowledgements in Readme.html You may use this sample code for anything you like, it is not covered by the LGPL like the rest of the engine.
----------------------------------------------------------------------------- */
/** /file Terrain.h /brief Specialisation of OGRE's framework application to show the terrain rendering plugin */
#include "ExampleApplication.h"
#include "OgreStringConverter.h"
#define FLOW_SPEED 0.2
#define FLOW_HEIGHT 0.8
class TerrainListener : public ExampleFrameListener { public: TerrainListener(RenderWindow* win, Camera* cam) :ExampleFrameListener(win, cam) { };
// Override frameStarted event to process that (don't care about frameEnded)
bool frameStarted(const FrameEvent& evt)
{ float moveScale; float rotScale;
float waterFlow; static float flowAmount = 0.0f;
static bool flowUp = true;
// local just to stop toggles flipping too fast
static Real timeUntilNextToggle = 0;
if (timeUntilNextToggle >= 0)
timeUntilNextToggle -= evt.timeSinceLastFrame;
// If this is the first frame, pick a speed
if (evt.timeSinceLastFrame == 0)
{ moveScale = 1; rotScale = 0.1; waterFlow = 0.0f; }
// Otherwise scale movement units by time passed since last frame
else
{
// Move about 100 units per second,
moveScale = 10.0 * evt.timeSinceLastFrame;
// Take about 10 seconds for full rotation
rotScale = 36 * evt.timeSinceLastFrame;
// set a nice waterflow rate
waterFlow = FLOW_SPEED * evt.timeSinceLastFrame;
}
// Grab input device state
mInputDevice->capture();
SceneNode *waterNode =
static_cast( mCamera->getSceneManager()->getRootSceneNode()->getChild("WaterNode"));
if(waterNode) { if(flowUp)
flowAmount += waterFlow;
else
flowAmount -= waterFlow;
if(flowAmount >= FLOW_HEIGHT)
flowUp = false;
else if(flowAmount <= 0.0f)
flowUp = true;
waterNode->translate(0, (flowUp ? waterFlow : -waterFlow), 0); }
static Vector3 vec; vec = Vector3::ZERO;
if (mInputDevice->isKeyDown(KC_A))
{ // Move camera left vec.x = -moveScale; }
if (mInputDevice->isKeyDown(KC_D))
{
// Move camera RIGHT
vec.x = moveScale;
}
if (mInputDevice->isKeyDown(KC_UP) || mInputDevice->isKeyDown(KC_W))
{
// Move camera forward
vec.z = -moveScale;
}
if (mInputDevice->isKeyDown(KC_DOWN) || mInputDevice->isKeyDown(KC_S))
{
// Move camera backward
vec.z = moveScale;
}
if (mInputDevice->isKeyDown(KC_PGUP))
{
// Move camera up
vec.y = moveScale;
}
if (mInputDevice->isKeyDown(KC_PGDOWN))
{ // Move camera down
vec.y = -moveScale;
}
if (mInputDevice->isKeyDown(KC_RIGHT))
{ mCamera->yaw(-rotScale); }
if (mInputDevice->isKeyDown(KC_LEFT))
{ mCamera->yaw(rotScale); }
if( mInputDevice->isKeyDown( KC_ESCAPE) )
{ return false; }
// Rotate view by mouse relative position float rotX, rotY; rotX = -mInputDevice->getMouseRelativeX() * 0.13;
rotY = -mInputDevice->getMouseRelativeY() * 0.13;
// Make all the changes to the camera
// Note that YAW direction is around a fixed axis (freelook stylee) rather than a natural YAW (e.g. airplane)
mCamera->yaw(rotX);
mCamera->pitch(rotY);
mCamera->moveRelative(vec);
// Rotate scene node if required
SceneNode* node = mCamera->getSceneManager()->getRootSceneNode();
if (mInputDevice->isKeyDown(KC_O))
{ node->yaw(rotScale); }
if (mInputDevice->isKeyDown(KC_P))
{ node->yaw(-rotScale); }
if (mInputDevice->isKeyDown(KC_I))
{ node->pitch(rotScale); }
if (mInputDevice->isKeyDown(KC_K))
{ node->pitch(-rotScale); }
if (mInputDevice->isKeyDown(KC_F) && timeUntilNextToggle <= 0)
{ mStatsOn = !mStatsOn;
//Root::getSingleton().showDebugOverlay(mStatsOn); showDebugOverlay(mStatsOn); timeUntilNextToggle = 1; }
// Return true to continue rendering
return true; } };
class TerrainApplication : public ExampleApplication
{ public: TerrainApplication() {}
protected: virtual void createFrameListener(void) { mFrameListener= new TerrainListener(mWindow, mCamera);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}
virtual void chooseSceneManager(void)
{ // Get the SceneManager, in this case a generic one mSceneMgr = mRoot->getSceneManager( ST_EXTERIOR_CLOSE );
}
virtual void createCamera(void)
{
// Create the camera mCamera = mSceneMgr->createCamera("PlayerCam");
// Position it at 500 in Z direction
mCamera->setPosition(Vector3(128,25,128));
// Look back along -Z
mCamera->lookAt(Vector3(0,0,-300));
mCamera->setNearClipDistance( 1 );
mCamera->setFarClipDistance( 384 );
}
virtual void createViewports(void)
{
// Create one viewport, entire window
Viewport* vp = mWindow->addViewport(mCamera);
vp->setBackgroundColour(ColourValue::White); }
// Just override the mandatory create scene method
void createScene(void)
{ Entity *waterEntity;
Plane waterPlane;
// Set ambient light mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
// create a water plane/scene node
waterPlane.normal = Vector3::UNIT_Y;
waterPlane.d = -1.5;
MeshManager::getSingleton().createPlane( "WaterPlane", waterPlane, 2800, 2800, 20, 20, true, 1, 10, 10, Vector3::UNIT_Z );
waterEntity = mSceneMgr->createEntity("water", "WaterPlane");
waterEntity->setMaterialName("Examples/TextureEffect4"); SceneNode *waterNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("WaterNode");
waterNode->attachObject(waterEntity);
waterNode->translate(1000, 0, 1000);
// Create a light
Light* l = mSceneMgr->createLight("MainLight");
// Accept default settings: point light, white diffuse, just set position
// NB I could attach the light to a SceneNode if I wanted it to move automatically with
// other objects, but I don't
l->setPosition(20,80,50);
mSceneMgr -> setWorldGeometry( "terrain.cfg" ); mSceneMgr->setFog( FOG_EXP2, ColourValue::White, .008, 0, 250 );
//mRoot -> showDebugOverlay( true ); } };
/* -----------------------------------------------------------------------------
This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright ?2000-2003 The OGRE Team Also see acknowledgements in Readme.html You may use this sample code for anything you like, it is not covered by the LGPL like the rest of the engine.
----------------------------------------------------------------------------- */
/** @file Terrain.cpp @brief Shows OGRE's terrain rendering plugin. */
#include "Ogre.h"
#include "Terrain.h"
#if OGRE_PLATFORM == PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
#if OGRE_PLATFORM == PLATFORM_WIN32 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else int main(int argc, char *argv[])
#endif {
// Create application object TerrainApplication app; SET_TERM_HANDLER;
try { app.go();
} catch( Ogre::Exception& e )
{
#if OGRE_PLATFORM == PLATFORM_WIN32 MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL); #else std::cerr << "An exception has occured: " << e.getFullDescription().c_str() << std::endl; #endif
}
return 0;
}
参考
(1)《游戏之王》——孙百英主编,科学普及出版社,ISBN:7-110-04493-9
(2)《设计模式》——Erich Gamma等,机械工业出版社,ISBN:7-111-07575-7
(3)OGRE文档和源代码——Ogre制作组(英国)
(4)《游戏引擎剖析》——Jake Simpson
(5)《圣剑群英传》文档和源代码——金点工作室(中国)
(6)《Ogre使用指南》——mage工作室(中国)