基于 Ogre 和 ODE 的 3D RPG
一个基于 Ogre, ODE, 3D Max, CEGUI 等的 3D 游戏
引言
在这篇文章中,我介绍一个基于 Ogre 图形引擎、ODE 物理引擎、CEGUI 和其他工具的 3D RPG 解决方案。 框架被描述,完整的资源和源代码在https://github.com/lxdfigo/Space-Knight。
背景
太空骑士是我小时候看的一部动画片,我也是一个游戏迷。所以我用我之前学到的东西把这部动画片开发成一个RPG。 在这里,展示了我在游戏中使用的库
- Ogre:我使用 Ogre 作为图形引擎。它可以选择 DirectX 或 OpenGL 来渲染场景。
- OED:我使用 OgreOde,它将 Ogre 与 ODE 结合,在我的游戏中构建物理世界。
- CEGUI:它用于构建游戏中的 UI,例如主菜单、交易菜单、功能菜单等。
- 3D Max:我们游戏中的模型由 3D Max 构建,并通过插件导出。
- DirectShow 和 Fmod:它们用于播放视频和音频。
Using the Code
如前所述,该应用程序基于几个库。 这是我的游戏框架的 UML 图。
应用程序中有几个模块。 有任务模块、角色模块、AI模块、UI模块、工具模块和媒体模块。
任务模块控制游戏的任务,它包含BaseMission
类,FirstMission
类等。 每个类代表游戏中的一个任务。 基类也控制着大多数模块。
角色模块控制游戏中的玩家,例如主角、太空骑士、怪物 Lada、NPC 等。 这些实体由实体管理器管理,并在每一帧中检查它们是否发生碰撞。 角色模块的一部分也由 AI 模块控制。
AI 模块使用状态机来模拟角色的 AI。 每个拥有 AI 的角色都有自己的状态类,该类定义了它的行为。 它可以很容易地更改和扩展。
UI模块使用CEGUI显示游戏的UI。 它接收来自用户的输入并将消息发布到其他模块。 它使用 OIS 接收键盘消息和鼠标消息。 屏幕上的每个菜单都是一个由 CEGUI 渲染的脚本文件。
工具模块控制游戏中的工具、药品和其他对象,例如路上的箱子或打开的门。 大多数工具都由 XML 文件中的状态构建。
媒体模块是 Fmod 和 DirectShow 的一个简单适配器。 它播放战斗或背景的声音,以及开场播放的视频。
任务的基类包含许多成员和函数。 它使用 UI 模块来显示菜单,使用媒体模块来播放声音,使用角色模块来更新实体。 它还构建图形世界和物理世界。 它具有移动到下一个任务、加载保存的任务或游戏结束的功能。
状态机显示它包含三个主要状态 - 前一个状态、当前状态和全局状态。 该状态有四种行为 - Enter
,Exit
,Execute
和OnMessage
。 如果一个角色收到一条消息,并将其传递给状态机,它会执行当前状态并转移到下一个状态。
//
//Here is the Base Mission class
//
class BaseMission:
public OgreOde::CollisionListener,
public OgreOde::StepListener,
public OgreOde::TriangleMeshRayListener
{
public:
....
OgreOde::World *mWorld;
OgreOde::Space *mSpace;
OgreOde::StepHandler *mStepper;
OgreOde::TriangleMeshGeometry* terrainTriMeshGeom;
OgreOde::InfinitePlaneGeometry *mGround;
GameFrame *gameFrame;
Ogre::Root* mRoot;
Ogre::Camera* mCamera;
Ogre::SceneManager* mSceneMgr;
Ogre::RenderWindow* mWindow;
CEGUI::System* mGUISystem;
FrameListener* mFrameListener;
BaseMissionListener* mMissionListener;
GuiListener *mGuiListener;
Overlay *talkOverlay,*gameOverlay,*blackOverlay,*loadingOverlay,*missionIntruOverlay;
CEGUI::Window* gameSheet,* opSheet,* quitSheet,
* gameOverSheet,* loadSheet,* intruduceSheet,
* useButton,*noUseButton,* currentPackage;
std::vector<AnimationState *> animationStates;
std::vector<Model *> mModels;
std::vector<OgreOde::Geometry*> mGeoms;
std::vector<SceneNode*> nodes;
std::vector<StaticThing*> staticThings;
std::vector<Role*> monsters;
Role *mainModel;
BaseSound *mSound;
SceneNode * _rocket_node_explosion;
ParticleSystem * _rocketParticles_explosion;
public :
BaseMission(Ogre::RenderWindow* mW, Ogre::Root* mR, Ogre::Camera* mC,
Ogre::SceneManager* mS,CEGUI::System* mG,GameFrame *);
virtual void Render();
virtual void Clean();
virtual void InitObjects();
virtual void createFrameListener();
virtual void NextMission();
virtual void upDate();
virtual void createScene();
virtual void UpdateGUI(void);
virtual void initDemoEventWiring(void);
...
Ogre::SceneManager* getSceneManager(void) const { return mSceneMgr; }
Ogre::RenderWindow* getRenderWindow(void) const { return mWindow; }
void resetParticleSystem(Ogre::ParticleSystem *ps, bool enable, const Ogre::Real delay);
virtual void GameOver();
void LoadGame(int i);
};
//
// Here is part of the state machine of the AI in the game.
//
template <class entity_type>
class StateMachine
{
private:
entity_type *m_pOwner;
State<entity_type> *m_pCurrentState;
State<entity_type> *m_pPreviousState;
State<entity_type> *m_pGlobalState;
public:
StateMachine(entity_type *owner):m_pOwner(owner),
m_pCurrentState(NULL),m_pPreviousState(NULL),m_pGlobalState(NULL){}
void SetCurrentState(State<entity_type> *s){m_pCurrentState = s;}
void SetPreviousState(State<entity_type> *s){m_pPreviousState = s;}
void SetGlobalState(State<entity_type> *s){m_pGlobalState = s;}
...
};
template <class entity_type>
class State
{
public:
virtual bool OnMessage(entity_type *,Telegram &msg) = 0;
virtual void Enter(entity_type *) = 0;
virtual void Exit(entity_type *) = 0;
virtual void Execute(entity_type *) = 0;
virtual ~State(){}
};
操作和结果
游戏的操作
- 移动:W, A, S, D
- 射击:鼠标, Z, C, V
- 任务通过:Y
- 菜单:F1
我们的游戏有七个任务,包括两个boss任务和一个转移任务。 这里展示了我游戏的一些图片。
游戏的主菜单。
玩家击败了一个怪物。
历史
- 2013 年 4 月 18 日:初始版本