﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Only Me-文章分类-3D引擎</title><link>http://www.cppblog.com/windeer/category/860.html</link><description>        ——静下心，坐坐……</description><language>zh-cn</language><lastBuildDate>Sun, 25 May 2008 09:58:51 GMT</lastBuildDate><pubDate>Sun, 25 May 2008 09:58:51 GMT</pubDate><ttl>60</ttl><item><title>游戏引擎模式设计</title><link>http://www.cppblog.com/windeer/articles/3389.html</link><dc:creator>alpha</dc:creator><author>alpha</author><pubDate>Tue, 21 Feb 2006 15:23:00 GMT</pubDate><guid>http://www.cppblog.com/windeer/articles/3389.html</guid><wfw:comment>http://www.cppblog.com/windeer/comments/3389.html</wfw:comment><comments>http://www.cppblog.com/windeer/articles/3389.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/windeer/comments/commentRss/3389.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/windeer/services/trackbacks/3389.html</trackback:ping><description><![CDATA[Using&nbsp;design&nbsp;patterns&nbsp;in&nbsp;game&nbsp;engines 
<P></P>
<P>By&nbsp;Rasmus&nbsp;Christian&nbsp;Kaae</P>
<P>kaae@daimi.au.dk<BR>Student&nbsp;of&nbsp;computer&nbsp;science&nbsp;and&nbsp;programmer&nbsp;at&nbsp;TietoEnator&nbsp;Consulting&nbsp;A/S<BR>August&nbsp;2001<BR>All&nbsp;rights&nbsp;reserved<BR>译：Room3rd@hotmail.com<BR>&nbsp;2005年4月5日</P>
<P>1.0&nbsp;简介<BR>　　关于模式设计，我们并不陌生，在项目设计初始阶段，能给予我们很大帮助，提供如何分段的方针及如何通过分化为若干函数成为更小的段。本文中我将利用模式设计就如何构造游戏引擎说明我的观点。这些认识主要基于个人经验及Aarhus综合大学计算机科学学院中的学习。出于不同目的及需要，设计模式也不同，这里我通过构造游戏的Model-view-control来说明基本的模式设计，以及3D引擎的内核构造。</P>
<P>　　阅读之前，建议您对C++的多态性先有所了解。</P>
<P>2.0&nbsp;引擎内核的Model-view-control</P>
<P>　　引擎内核应该能处理游戏中涉及的所有任务，这就意味着处理包括用户输入、其他用户的可能输入（网络游戏）、游戏事件，当然还应该包括音频、视频输出。为了达到该目的，所以我引出了Model-view-control设计模式，该模式把引擎划分为三个重要部分。</P>
<P>Model-view-control对于GUI程序设计而言非常有用，它可以提供某种程度的模块性，使得程序可以利用已有的图形输出设备实现。例如，利用最新的DirectX&nbsp;SDK，游戏引擎可以实现创建一个执行的DirectX程序，如果换成OpenGL也不成问题。由于关于游戏引擎及DirectX的对话框的实现都隐藏在view类中，所以移植就太简单了—应该只有view类需要变更。</P>
<P>关于model-view-control结构的详细描述可以到<A href="http://ootips.org/mvcpattern.html&nbsp;" target=_blank>http://ootips.org/mvcpattern.html&nbsp;</A>;及<A href="http://compsci.about.com/cs/mvcpattern/index.htm" target=_blank>http://compsci.about.com/cs/mvcpattern/index.htm</A>浏览察看。</P>
<P>2.1&nbsp;模型（model）</P>
<P>结构模型应包含所有游戏结构，例如3D-引擎结构、行为模式（behaviour&nbsp;pattern，针对用户输入如何处理）。</P>
<P></P>
<P>模型部分与结构的view及control部分无关，这就意味着在另外的环境中模型部分可单独使用。</P>
<P></P>
<P>2.2&nbsp;View</P>
<P></P>
<P>View部分包含所有对用户的输出，所以应该包含视频音频传达。例如，这里需要实现OpenGL及DirectX驱动。</P>
<P></P>
<P>　　该部分与模型部分有关系，也就是说view部分能够在用户与模型当前状态间传递信息。</P>
<P></P>
<P>2.3&nbsp;Control</P>
<P></P>
<P>　　Control部分是结构中最基本的部分。初始化后，它分配一个view实例及模型类实例，并等待用户输入。然后用户的输入经分析交模型部分处理，之后view被请求映射更新当前模型状态。</P>
<P></P>
<P>2.4&nbsp;示例代码</P>
<P></P>
<P>以下代码为伪-C++代码，所以不能被编译。</P>
<P></P>
<P>/*&nbsp;InputBlock&nbsp;包含用户的当前输入　*/</P>
<P></P>
<P>class&nbsp;InputBlock</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo&nbsp;:&nbsp;添加一些有意思的变量及函数来显示用户输入</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;游戏模型&nbsp;*/</P>
<P></P>
<P>class&nbsp;Model</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>Engine3D&nbsp;*m_engine;</P>
<P></P>
<P>Sound&nbsp;*m_sound;</P>
<P></P>
<P>Model();</P>
<P></P>
<P>UpdateControl(InputBlock&nbsp;*input)</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo&nbsp;:&nbsp;反作用于当前输入</P>
<P></P>
<P>}</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;ViewVisual是一个接口类，用来实现视频驱动&nbsp;*/</P>
<P></P>
<P>class&nbsp;ViewVisual</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>ViewVisual()</P>
<P></P>
<P>{</P>
<P></P>
<P>}</P>
<P></P>
<P>//&nbsp;纯虚函数用来处理当前模型中的变化</P>
<P></P>
<P>virtual&nbsp;void&nbsp;Update(Model&nbsp;*model)=0;</P>
<P></P>
<P>}</P>
<P></P>
<P>/*&nbsp;ViewVisualOpenGL是ViewVisual接口的一个实现&nbsp;*/</P>
<P></P>
<P>class&nbsp;ViewVisualOpenGL</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>ViewVisualOpenGL()</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo&nbsp;:&nbsp;初始化代码</P>
<P></P>
<P>}</P>
<P></P>
<P>//&nbsp;显示模型的当前状态</P>
<P></P>
<P>void&nbsp;Update(Model&nbsp;*model)</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo&nbsp;:&nbsp;绘制model-&gt;m_engine</P>
<P></P>
<P>}</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;ViewAudio为音频驱动实现的接口类&nbsp;*/</P>
<P></P>
<P>class&nbsp;ViewAudio</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>ViewAudio()</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo:&nbsp;初始化代码</P>
<P></P>
<P>}</P>
<P></P>
<P>//&nbsp;纯虚函数包含当前模型变化的处理</P>
<P></P>
<P>virtual&nbsp;void&nbsp;Update(Model&nbsp;*model)=0;</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;ViewAudioDirectSound&nbsp;为ViewAudio接口的DirectSound实现&nbsp;*/</P>
<P></P>
<P>class&nbsp;ViewAudioDirectSound</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>ViewAudioDirectSound()</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo&nbsp;:&nbsp;初始化代码</P>
<P></P>
<P>}</P>
<P></P>
<P>void&nbsp;Update(Model&nbsp;*model)</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;todo&nbsp;:&nbsp;从model-&gt;m_sound播放当前声音</P>
<P></P>
<P>}</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;View为连接了视频、音频的类&nbsp;*/</P>
<P></P>
<P>class&nbsp;View</P>
<P></P>
<P>{</P>
<P></P>
<P>protected:</P>
<P></P>
<P>ViewVisual&nbsp;*m_visual;</P>
<P></P>
<P>ViewAudio&nbsp;*m_audio;</P>
<P></P>
<P>public:</P>
<P></P>
<P>View(Model&nbsp;*model,&nbsp;ViewVisual&nbsp;*visual,&nbsp;ViewAudio&nbsp;*audio)</P>
<P></P>
<P>{</P>
<P></P>
<P>m_model=model;</P>
<P></P>
<P>m_visual=visual;</P>
<P></P>
<P>m_audio=audio;</P>
<P></P>
<P>}</P>
<P></P>
<P>void&nbsp;Update()</P>
<P></P>
<P>{</P>
<P></P>
<P>m_visual-&gt;Update(m_model);</P>
<P></P>
<P>m_audio-&gt;Update(m_model);</P>
<P></P>
<P>}</P>
<P></P>
<P>};</P>
<P></P>
<P><BR>&nbsp;</P>
<P><BR>/*&nbsp;Control类处理用户输入并启动输出*/</P>
<P></P>
<P>class&nbsp;Control</P>
<P></P>
<P>{</P>
<P></P>
<P>enum</P>
<P></P>
<P>{</P>
<P></P>
<P>ESCAPE_IS_PRESSED,</P>
<P></P>
<P>USER_INPUT</P>
<P></P>
<P>};</P>
<P></P>
<P>private:</P>
<P></P>
<P>View&nbsp;*m_view;</P>
<P></P>
<P>Model&nbsp;*m_model;</P>
<P></P>
<P>public:</P>
<P></P>
<P>Control()</P>
<P></P>
<P>{</P>
<P></P>
<P>m_model&nbsp;=&nbsp;new&nbsp;Model();</P>
<P></P>
<P>m_view&nbsp;=&nbsp;new&nbsp;View(m_model,&nbsp;new&nbsp;ViewVisualOpenGL(),</P>
<P></P>
<P>new&nbsp;ViewAudioDirectSound());</P>
<P></P>
<P>}</P>
<P></P>
<P>~Control()</P>
<P></P>
<P>{</P>
<P></P>
<P>delete&nbsp;m_model;</P>
<P></P>
<P>delete&nbsp;m_view;</P>
<P></P>
<P>}</P>
<P></P>
<P>InputBlock&nbsp;Input(int&nbsp;which)</P>
<P></P>
<P>{</P>
<P></P>
<P>switch&nbsp;(which)</P>
<P></P>
<P>{</P>
<P></P>
<P>case&nbsp;USER_EXIT&nbsp;:&nbsp;</P>
<P></P>
<P>if&nbsp;(escape_is_pressed)&nbsp;return&nbsp;newInputBlock(ESCAPE_IS_PRESSED);</P>
<P></P>
<P>break;</P>
<P></P>
<P>case&nbsp;USER_INPUT&nbsp;:&nbsp;</P>
<P></P>
<P>if(user_has_made_an_input)&nbsp;return&nbsp;new&nbsp;InputBlock(the_new_input_info);</P>
<P></P>
<P>break;</P>
<P></P>
<P>}</P>
<P></P>
<P>}</P>
<P></P>
<P>void&nbsp;Run()</P>
<P></P>
<P>{</P>
<P></P>
<P>while&nbsp;(!Input(USER_EXIT))</P>
<P></P>
<P>{</P>
<P></P>
<P>if&nbsp;(Input(USER_INPUT))</P>
<P></P>
<P>m_model-&gt;UpdateControl(Input(USER_INPUT));</P>
<P></P>
<P>m_view-&gt;Update();</P>
<P></P>
<P>}</P>
<P></P>
<P>}</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;main函数分配我们的结构，并开始运行*/</P>
<P></P>
<P>void&nbsp;Main(int&nbsp;argc,&nbsp;char&nbsp;**argv)</P>
<P></P>
<P>{</P>
<P></P>
<P>Control&nbsp;*control&nbsp;=&nbsp;new&nbsp;Control();</P>
<P></P>
<P>control-&gt;Run();</P>
<P></P>
<P>delete&nbsp;control;</P>
<P></P>
<P>}</P>
<P></P>
<P><BR>&nbsp;</P>
<P><BR>2.4.1&nbsp;关于代码的说明</P>
<P></P>
<P>　　以上代码将首先初始化分配一个控制器（controller），然后由控制器分配了一个OpenGL输出设备、一个DirectSound输出设备及一个模型，完成分配后，主程序调用了控制器的Run()函数，该函数在其内部某个位置实现了一个循环知道用户按下了退出键。程序将按照给出的输入一直更新模型及view直到用户退出。如果给出了输入，输入经过分析传递给模型模块进行处理。当输入（或缺少输入）已被处理时，控制器请求view进行更新。退出时，以上也很好的示范了清除代码。</P>
<P></P>
<P><BR>&nbsp;</P>
<P><BR>2.5&nbsp;一种图形表示</P>
<P></P>
<P></P>
<P><BR>上图示意了当前model-view-control结构之间的联系，Control向model及view两者传递消息，而view从model检索数据。在实际实现时，这些关系应该变更以适应实际需要。I我喜欢以上方式，因为各类间有充分的独立性。然而，可能添加一个model及view到controller之间的通讯线路比较好，这样允许在发生类似突然错误时发送通知。</P>
<P></P>
<P><BR>&nbsp;</P>
<P><BR>3.0&nbsp;3D引擎的层模式</P>
<P></P>
<P>组织3D引擎内核时使用层模式的初衷是为了尽可能简便的访问代码。作为一个示例，它应该能够“说”scene-&gt;m_activeCamera-&gt;LookAt(pos,target)，然后整个场景就会按此改变；同样应该在“隐藏”层中进行3D-model载入，因此任何事情都将在更高级别处理（因而绘制在引擎内核的view部分实现）。整个引擎将被分化为至少3层，每层都是上层的扩展，而且越来越低级。</P>
<P></P>
<P>3.1&nbsp;顶层</P>
<P></P>
<P>　　顶层提供了修改更新当前场景状态的基本接口。上层把输入传递给结构的低层，例如，玩家移动到一个新位置，顶层就会确认特定的玩家对象会得到一个新的平移点，下次重画时才能保证玩家在正确的位置上。灯光及摄像机也会如此（在第一人称的设计游戏中，摄像机就是玩家视角）。</P>
<P></P>
<P>3.2&nbsp;第二层</P>
<P></P>
<P>　　第二层更为深入，包含所有类及将来扩展的接口类，例如不同类型的光源（聚光灯、泛光灯、发光点等等）。</P>
<P></P>
<P>3.3&nbsp;第三层及更低的层次</P>
<P></P>
<P>这些层将包含具体实现，如灯光、hiearchy-objects等等。</P>
<P></P>
<P>3.4&nbsp;示例代码</P>
<P></P>
<P>为了说明（不明确的）3D-引擎层的定义，这里给出了一些示例代码。</P>
<P></P>
<P>/*&nbsp;第二层&nbsp;—&nbsp;灯光，包含要实现的各种不同类型光源接口&nbsp;*/</P>
<P></P>
<P>class&nbsp;Light</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>enum</P>
<P></P>
<P>{</P>
<P></P>
<P>OMNI</P>
<P></P>
<P>};</P>
<P></P>
<P>int&nbsp;m_light_type;</P>
<P></P>
<P>virtual&nbsp;void*&nbsp;GetData()=0;</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;第三层-&nbsp;lightomni&nbsp;–&nbsp;灯光的直接实现*/</P>
<P></P>
<P>class&nbsp;LightOmni&nbsp;:&nbsp;public&nbsp;Light</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>LightOmniData&nbsp;*m_light_omni_data;</P>
<P></P>
<P>LightOmni()&nbsp;{&nbsp;m_light_type=OMNI;&nbsp;}</P>
<P></P>
<P>void&nbsp;*GetData()&nbsp;{&nbsp;return&nbsp;m_light_omni_data;&nbsp;}</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;第二层&nbsp;—&nbsp;对象，包含要实现的对象接口&nbsp;*/</P>
<P></P>
<P>class&nbsp;Object</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>Vector&nbsp;*m_vertices;</P>
<P></P>
<P>Face&nbsp;*m_faces;</P>
<P></P>
<P>//&nbsp;设置指定对象的当前平移点</P>
<P></P>
<P>virtual&nbsp;void&nbsp;SetTranslation(Vector&nbsp;&amp;v)=0;</P>
<P></P>
<P>//&nbsp;以大量数据填充指针</P>
<P></P>
<P>virtual&nbsp;void&nbsp;GetObjectData(Vector&nbsp;*out_vertices,&nbsp;Face&nbsp;*out_faces,</P>
<P></P>
<P>int&nbsp;*out_num_faces)=0;</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;第三层—Objecthierachy&nbsp;–&nbsp;对象的直接实现&nbsp;*/</P>
<P></P>
<P>class&nbsp;ObjectHierachy&nbsp;:&nbsp;public&nbsp;Object</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>ObjectHiearchy&nbsp;*m_child;</P>
<P></P>
<P>Vector&nbsp;m_translation;</P>
<P></P>
<P>//&nbsp;设置当前平移点</P>
<P></P>
<P>void&nbsp;SetTranslation(Vector&nbsp;v)&nbsp;{&nbsp;translation=v;&nbsp;}</P>
<P></P>
<P>//&nbsp;以大量数据填充指针</P>
<P></P>
<P>void&nbsp;GetObjectData(Vector&nbsp;*out_vertices,&nbsp;Face&nbsp;*out_faces,</P>
<P></P>
<P>int&nbsp;*out_num_faces)</P>
<P></P>
<P>{</P>
<P></P>
<P>ObjectHiearchy&nbsp;*o&nbsp;=&nbsp;this;</P>
<P></P>
<P>while&nbsp;(o-&gt;child!=NULL)</P>
<P></P>
<P>{</P>
<P></P>
<P>//&nbsp;从"o"添加数据到out_vertices,&nbsp;out_faces及out_num_faces</P>
<P></P>
<P>}</P>
<P></P>
<P>}</P>
<P></P>
<P>};</P>
<P></P>
<P>/*&nbsp;包含分层结构的上层</P>
<P></P>
<P>注意：建议在场景类中使用链接列表(linkedlists)而不是静态数组，由于这里仅作范例，故不作详细描述*/</P>
<P></P>
<P>class&nbsp;Scene</P>
<P></P>
<P>{</P>
<P></P>
<P>public:</P>
<P></P>
<P>Camera&nbsp;*m_currentCamera;</P>
<P></P>
<P>Camera&nbsp;**m_cameras;</P>
<P></P>
<P>Light&nbsp;**m_lights;</P>
<P></P>
<P>Object&nbsp;**m_objects;</P>
<P></P>
<P>};</P>
<P></P>
<P><BR>&nbsp;</P>
<P></P>
<P>&nbsp;</P>
<P><BR>3.5&nbsp;图示</P>
<P></P>
<P>顶层</P>
<P><BR>&nbsp;<BR>SCENE<BR>&nbsp;<BR>第二层</P>
<P><BR>&nbsp;<BR>Objects<BR>&nbsp;<BR>Light<BR>&nbsp;<BR>Camera<BR>&nbsp;<BR>第三层</P>
<P><BR>&nbsp;<BR>HIEARCHY<BR>&nbsp;<BR>Other<BR>&nbsp;<BR>Spot<BR>&nbsp;<BR>Other<BR>&nbsp;</P>
<P></P>
<P></P>
<P>&nbsp;</P>
<P></P>
<P>　　上图示意了各层间类的关系。应用以上设计，你可以摆脱繁琐迂腐的细节，而对每个任意的对象类型进行抽象，并编写通用代码以完成Object类（还有Light、Camera）具体实现。</P>
<P></P>
<P>4.0&nbsp;结束语</P>
<P></P>
<P>　　由于需要，我写下了该文—我已经查找类似文章有段时间了（而且没那么幸运）。该文更应该被视为实现游戏引擎的一个启发（它也许不是完美的解决方案）。作为一名程序员，依我的经验，（开发中）使用设计模式非常好，since&nbsp;it&nbsp;allows&nbsp;you&nbsp;to&nbsp;lean&nbsp;back&nbsp;and&nbsp;use&nbsp;trusted&nbsp;structures,instead&nbsp;of&nbsp;reinventing&nbsp;the&nbsp;wheel（不知怎么翻译较好J）。</P>
<P></P>
<P>Chapter&nbsp;3.0&nbsp;and&nbsp;4.0&nbsp;might&nbsp;be&nbsp;subject&nbsp;to&nbsp;discussions&nbsp;since&nbsp;there&nbsp;are&nbsp;many&nbsp;individual&nbsp;elements&nbsp;to&nbsp;beconsidered.&nbsp;For&nbsp;the&nbsp;3D-engine&nbsp;the&nbsp;implementation&nbsp;may&nbsp;differ&nbsp;from&nbsp;one&nbsp;3D-editor&nbsp;to&nbsp;another.</P>
<P></P>
<P><BR>&nbsp;</P>
<P><BR>Have&nbsp;fun&nbsp;with&nbsp;the&nbsp;programming,</P>
<P></P>
<P>Rasmus&nbsp;Christian&nbsp;Kaae<BR></P><img src ="http://www.cppblog.com/windeer/aggbug/3389.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/windeer/" target="_blank">alpha</a> 2006-02-21 23:23 <a href="http://www.cppblog.com/windeer/articles/3389.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>