游戏开发中的一些基本方法

一.              检测对象变化的两种基本方式:

学过《微机原理》的人应该都了解这两种方式

1.       轮询

1) 每帧轮询

2) 定时轮询

按业务需求和性能问题选择

2.       中断(并非硬件中断,而是软件的事件通知方式)

两种模式:

1)       观察者模式

优点:① 扩展性强,事件发起接口不变,只需增加事件类型

② 只通知对某件事有兴趣的对象,不会浪费性能

③ 每种事件对应一种回调函数,对于回调函数,事件参数类型是固定的,MouseMove事件的参数类型肯定是EventMouseMove,不会是EventKeyDown

缺点:① 需要动态维护观察者列表,维护不好可能会通知到不存在的对象

形态A: 独立观察者模式:事件回调列表由每个被观察对象独立维护

①   可能也会附带一个全局回调列表,只是起辅助作用

②   CEGUI使用的是这种方式

形态B: 全局观察者模式:事件回调列表由全局消息中心统一维护

① 全局回调列表比较庞大,消息中心的负载可能很大

2)  固定接口广播模式

优点:① 不需要维护观察者列表,从根节点往下广播到整棵树

缺点:① 对某事件不感兴趣的对象也会被通知到,性能稍许有浪费

② 只有树上的节点才能被通知到,所有逻辑对象不一定都在树上

(如果这棵树只代表游戏中各业务流程,那么非业务流程的对象不能被通知到;如果这棵树代表游戏中所有逻辑对象,那么所有对象都能被通知到,但是代价就是这棵树可能有点庞大)

形态A: 特定接口模式:一种事件对应一个发起接口

①   对于每个发起接口,事件参数类型是固定的

②   扩展性较差,增加事件类型需要增加接口

③   适合于事件类型较少的框架结构

形态B: 统一接口模式:所有事件对应同一个发起接口

①   扩展性强,事件发起接口不变,只需增加事件类型

②   事件发起接口的参数类型是不固定的,响应时需要根据消息类型去判断

形态C: 分组接口模式:类型接近的事件作为一个组,对应同一个发起接口;比如鼠标事件有好几种,这几种事件使用同一个发起接口,但是这个接口不会发起键盘事件,会有另一个接口负责

①   扩展性较强,同一组内事件发起接口不变,只需增加事件类型

②   增加事件组需要增加发起接口

③   事件发起接口的参数类型是不固定的,响应时需要根据消息类型去判断

两种调用方法:

1)     立即模式:事件立刻处理,保证逻辑顺序,调试链不会断裂,相当于SendMessage

2)  间接模式:事件保存在事件列表中,下次更新处理,不一定能保证逻辑顺序,调试链断裂,相当于PostMessage

轮询的逻辑耦合性强,代码容易变得混乱,C++代码中必须包含目标对象头文件,性能也不一定好,适用于比较简单的程序结构。

中断的逻辑耦合性弱,代码结构相对简单、清晰,C++代码中不需要包含目标对象头文件,只需要包含事件定义的头文件,只在必要的时候触发相应逻辑,性能较好,适用于较复杂和扩展性要求较高的程序结构。

总结:看情况使用,轮询适用于业务需求较少、比较简单的程序结构;

如果业务需求较多,考虑扩展性,中断是比较好的选择;中断的两种方法也可以结合使用。

举例:

主角3分钟无操作后:

1)  头顶名字加上“暂离”字样

2)  聊天信息框显示“你已进入暂离状态”

主角退出暂离状态时:

1)  去掉“暂离”字样

2)  聊天信息框显示“你退出了暂离状态”

解决方案:

使用轮询模式:

1)  Character Manager需要不停去查询主角是否进入、退出暂离状态,需要保存主角上一次状态并与当前状态比较,造成轻微硬编码。

2)  暂离不是常态,导致轻微性能浪费

3)不需要为此设计回调列表、不需要维护回调列表,几句if语句就可以处理

使用中断模式:

1)只在必要的时候调用相应逻辑,代码整洁很多,复杂性也降低。

2)需要为这么一个简单的功能设计并维护回调列表,杀鸡用牛刀

如何选择:

如果对主角对象还有若干类似的观察需求,则可以使用中断模式,比如观察主角移动

如果对主角对象仅有这么一个简单需求,则可以选择轮询模式

根本原因:

对象变量当前值是什么是比较容易获取的,但是变量的跃迁一般不太会通知给外部对象,而且业务上对变量的跃迁需求较少,所以设计上容易忽略这方面内容。

二.      对象引用的三种基本方式:

1.  直接引用,直接保存目标对象指针

优点:性能较好;对象不存在了马上就可以知道,因为崩溃了

缺点:对象生命期同步处理不好,会导致野指针,程序崩溃。新手容易犯错

2.  间接引用,保存目标对象句柄/ID,需要引用对象时(向Manager)查询

优点:如果对象不存在,不会导致程序崩溃

缺点:性能较差,每次需要查询取得对象指针

对象不存在了,外部程序可能还不知道,程序不崩溃,但是行为可能不正确,可能造成程序性能轻微浪费

3.      引用计数,直接引用的改进方式

优点:性能较好,即使不能处理好对象生命期,也不会导致野指针

缺点:不能保证很好的掌握对象生命期,有时候就是不想要这个对象了,却不知道谁还保持了对它的引用,容易导致内存被无故占用

总结:看情况选择

三.      参数传递的两种基本方式:

1. 直接引用,直接保存目标对象指针

优点:性能较好,没有复制开销

缺点:逻辑耦合性强,对象生命期同步不好,会导致野指针,程序崩溃

包含了大量不必要的信息,代码容易变得混乱,C++代码中必须包含目标对象头文件,使用目标对象的地方需要了解目标对象的知识

2. 信息复制,定义“传递信息的中间对象”,复制需要的信息

优点:逻辑耦合性弱,只复制需要的信息,即使不能处理好对象生命期,也不会导致野指针

代码结构相对简单、清晰,C++代码中不需要包含目标对象头文件,只需要包含“传递信息中间对象”定义的头文件

缺点:性能较差,稍微有点复制开销,能接受

总结:信息量较少时推荐使用信息复制

举例:

右击NetPlayer弹出操作菜单:私聊、加好友、组队、查看等

这是一个异步操作,先弹出操作菜单,再从菜单上点击按钮,也可能不选择

解决方案:

使用直接引用方式:

1)  显示菜单的代码需要了解NetPlayer对象的知识,需要包含NetPlayer头文件

2)  菜单弹出后点击按钮前,NetPlayer断线或离开视野,如果未被通知到,再点击按钮,程序崩溃

使用信息复制方式:

1)  将菜单显示和按钮操作需要的信息,比如id、name、level等,从NetPlayer指针复制中间结构体,作为菜单的userdata,从而菜单回调函数与NetPlayer对象断开连接

2)  显示菜单的代码不需要包含NetPlayer头文件

3)  即使点击菜单按钮前NetPlayer消失,也不会导致程序崩溃。

使用事件通知(中断模式)

1)  NetPlayer消失的时候隐藏操作菜单,就不会导致程序崩溃了

2)  可以跟‘信息复制方式’ 结合使用

四.对象成员变量创建/销毁资源的两种方式:

1)使用构造函数与析构函数

① 构造函数做默认初始化,并创建资源

② 析构函数销毁具体资源,不一定需要将成员变量设置回默认值

③ 构造函数无返回值,不能通过返回值知道资源是否创建成功

④ 适用于含有较少资源的简单对象、或者创建资源不太会失败的对象

2)使用Init/Create与Fini/Destroy函数

① 构造函数做默认初始化,可以将对象先创建出来,占好位置,激活时再调用Init

② Init函数负责具体资源创建,可以有返回值判断资源创建情况

③ Fini函数销毁具体资源,将成员变量设置回默认值,通常该函数可以重入

④ 析构函数中需要调用Fini函数,或者加断言判断资源是否已销毁

⑤ 对象所占用内存不需要重新申请就可以再次使用

⑥ Init时推荐使用do{}while(0)结构:

do{

member1 = create_ member 1()

if (!member1)

break;

member2 = create_ member 2()

if (!member2)

break;

……

}while(0)

五.对象使用前初始化两种方式:

1)谁使用,谁初始化

① 用完以后,成员变量仍然保留了上一次对象状态

② 不能直接拿来用,必须先初始化为默认值

③ 适用于不太含有资源的较简单对象,用完不销毁可能有资源泄露

④ 适用于入口比较统一的程序结构

2)谁使用,用完恢复

① 用完以后,成员变量恢复为默认值,不保留任何对象状态

② 可以直接拿来用,对象成员变量已经是默认值

③ 适用于需要新创建资源的地方,用完时需要销毁资源

④ 适用于出口比较统一的程序结构

总结:UI编程经常使用到,界面打开与关闭时各控件的状态

六.几种基本的容错问题:

1. 重入问题

1) 不可重入:按钮灰态、加断言等方式来保证函数不被重复调用

2) 可重入:按钮使能但是有提示、容错处理来保证函数即使被重复调用也不会产生问题

比如Init函数,资源创建,不能重复调用,资源创建前加断言判断资源指针是否为空

比如Destroy函数,资源销毁,每份资源销毁前加判断指针是否为空,函数可重入

2. 边界问题

1) 内存边界检查

2) 数值边界检查

数值边界问题不一定是有害的,比如::GetTickCount()返回的当前时间与上一帧时间相减,如果当前值超出临界值,相减溢出,结果也是正确的。

3.       子对象间一致性问题

1)       利用信息冗余,减少子对象数量

2)       利用断言判断子对象间的值一致

比如定时器会有个计时用的浮点变量m_fCountTime,和一个表示使能状态的布尔变量m_bEnabled,某些情况下,m_fCountTime <= 0就表示m_bEnabled
== false,m_fCountTime > 0就表示m_bEnabled == true,因此两者之间存在信息冗余,m_bEnabled可以被删除掉,
成员函数bool IsEnabled() { return m_bEnabled; }可以改成bool IsEnabled() { return m_fCountTime
> 0; }

总结:利用断言检查可能发生错误的地方,比如逻辑是否可以运行到switch的default语句,在有Destroy成员函数的情况下,在析构函数中判断子对象指针是否为空(表示Destroy是否被外部程序调用到并且已经销毁资源)

七.逻辑状态及其变化的解释

1).   计算机中基本的逻辑状态就是0和1,这个地球人都知道,如图,低电平表示0,高电平表示1,比如一个bool值,false就是0,低电平,true就是非0,通常是1,高电平。

2).  大部分人的问题在于不能理解上升沿和下降沿,其实数电课上都讲过这些概念。上升沿就是电平从0变到1的瞬间,下降沿就是电平从1变到0的瞬间。

当电平从0变到1的瞬间,上升沿是1,其它时候都是0;当电平从1变到0的时候,下降沿是1,其它时候都是0。

举例:

1).  上升沿、下降沿用的最多的地方是输入设备编程,按钮从松开(0)状态,到按下(1),这一瞬间、这一帧,上升沿为1,之后上升沿马上变为0,虽然按键仍然保持按下(1)。反过来,按键松开的一瞬间、那一帧,下降沿为1,之后下降沿马上变为0。

2).  另外比如上面讲的例子,轮询主角状态变化,主角从非暂离切到暂离的一瞬间,才需要去加上“暂离”字样,并显示提示信息“你已进入暂离状态”,并非需要在主角暂离后的每一帧都显示这个提示信息。主角从暂离切回非暂离状态,那一瞬间,才需要去掉“暂离”字样。

八. 程序出现复杂问题的处理方法:

1).  差异比较法

2).  控制变量法

比如修电脑的时候,用好的电脑上的模块替换问题电脑上的模块,找出问题所在

3).  排除法,屏蔽不相关代码

4).  尝试恢复到一个没问题的版本

5).  更换测试环境

九. 版本出现问题的处理方法:

使用二分法:先恢复到一个老版本,看问题是否正常,不正常就使用更老的版本,正常就二分计算中间版本号,递归使用此方法

十.定位/查找代码的处理方法:

确定代码切入点,污染源查找法,扩散查找

如果量污染太大不能掌控,可能是切入点找的不对

十一. UI编程要点:

1).  键盘快捷操作用的三个基本按键:Esc、Enter、Tab

2).  异步操作状态下,控件使能状态的控制,特别是按钮

辅助工具:cloud,用来模拟网络延迟

3).  控件的输入焦点与选中状态,减少用户操作

4).  文本输入框的初始值、有效字符及最大值控制

5).  界面重新打开时的初始状态

6).  界面关闭时是否要恢复状态

如果你不知道该做些什么,该怎么做,答案很简单:参考标准。什么是标准?比如操作系统,比如暴雪的游戏等,暴雪在细节方面做的很到位的。

举例:比如邮件发送金币,弹出一个输入框,框显示的时候,输入焦点要定位在Editbox上,Editbox初始值0,自动选中文本0。这时候按数字键,文本框中的0直接被替换掉,如果按字母键,应该无响应或者错提示音,不能输入负值;如果输入超出角色身上所有金额,应该限制在最大金额或忽略输入。输入完直接按Enter,接受输入值,对话框消失;不想继续输入或者想取消的时候,直接按Esc键,对话框消失;如果按Tab键,当前焦点应该在Editbox、Ok、Cancel之间按顺序切换,焦点切在Ok上,按回车等同于点击Ok,焦点切在Cancel上,按回车等同于点击Cancel;任何情况下按Esc,都是Esc效果;如果鼠标点击底框,焦点切到底框上,按Esc,也应该相应。

十二.Yes/No对话框与Ok/Cancel对话框的区别

Yes与Ok的操作一般都是一样的,接受某个事情

No跟Cancel有点区别,No代表拒绝,Cancel表示忽略、什么也不做

Yes、No、Ok、Cancel任何一个按钮按了,对话框都应该消失

举例:比如游戏中有人向你发起组队邀请,如果弹出一个Yes/No对话框,点了Yes,接受组队,点了No,会向服务器发送拒绝消息,发起人会收到一个被拒绝的消息;如果弹出一个Ok/Cancel对话框,点了Ok,接受组队,点了Cancel,表示忽略请求,不会向服务器发送拒绝消息,这个邀请就此结束了。

所以还有一类对话框,Yes/No/Cancel都有。

总结:使用的时候,应该考虑清楚当下是使用Yes/No、Ok/Cancel,还是Yes/No/Cancel

十三. 代码Check
In的好习惯:

1). 提交之前确定哪些是需要提交的文件,临时文件、不相关的文件统统剔除

2). 提交之前再次比对每个文件修改,确保每一行修改是正确且相关的

3). 按功能、分块提交

如果Commit的时候发现这次修改中涉及到几个功能修改,则分多次提交,每次都是独立的功能;

如果是同一功能但是量比较大,可以把底层支持、业务无关的功能先提交,再提交各业务功能

如果一个文件中涉及到不同功能的修改,则备份文件,恢复不相关的代码行,一次只提交一个功能修改,完了之后再从备份文件中恢复其它修改,如此反复,不要嫌麻烦,确保一次提交中只涉及一个功能块。

总结:1)和2)加起来是两段式提交;这些习惯分别是为了“减少垃圾文件”、“降低出错几率”、“版本出问题后方便定位”

十四. 优化

1).  古话云:过早的优化是万恶之源

2).  过晚的优化会让你极其痛苦

3).  优化应该融入到你的血液当中,时刻想着要设计比较优化、简化的代码和流程

4).  优化也是个不断调整、不断迭代的过程,是个不断修改设计的过程,一次性很难做好

5).  代码优化的四(五)个层级:架构级、(模块级)、算法级、代码级、汇编级

代码级主要是指函数参数、返回值、循环、赋值表达式等。

汇编级太依赖于CPU,不做重点;并行处理的汇编指令(MMX/SSE等)可以作为学习内容。

6).  现在的编译器优化工作都做的很好,我们只需要做适当引导

十五. 游戏程序设计的宗旨:稳定和正确、简洁、高效、可扩展性

1). 稳定和正确是基本条件,没有什么比这更重要,程序宁可运行的慢一点,也要保证稳定和正确,错误的代码运行的再快,也没有意义

2). 简洁的设计容易让人理解,比较直观,通常也会带来高效的副作用

3). 高效的代码绝对能体现一个人编程水平,保证稳定和正确情况下的高效是令人赞赏的;高效有时候跟简洁是对立面,看情况取舍;高效对程序员的素质要求更高,在人力成本较高的情况下,通过提高硬件配置来改善程序运行效率也是可行的

4). 游戏编程策划需求千变万化,你的设计要比策划需求快半步,考虑到策划将来可能会增加的需求,降低代码的修改成本;太先进的可扩展性不一定可取,因为可能用不到,也有可能是通过降低性能来换取的,属于设计过度;可扩展性有时候跟简洁也是对立面。

代码是写给人看的,不是写给机器看的,设计思路一定要简单、直观、易懂、合理,少绕弯,杜绝奇葩思路,文档注释清楚齐全,代码保持整洁,版式清晰,该去的统统去掉。方便同事维护、方便后人维护,潜规则和硬编码尽量少定制,必须有潜规则、硬编码的情况下必须注释清楚,猜测别人的潜规则成本较高、也容易出问题。

十六. 游戏程序设计的若干技巧:

1).  任何计算机问题,都可以通过增加一个间接的中间层来解决

2).  减少模块间耦合(就像设计电路板时减少各组件间的连线)

3).  对象的继承与组合(m*n与m+n),用于防止子类数量爆炸

4).  lazy compute和pre compute

5).  查表(通常用于pre compute和提高程序效率、时空互换的典型)

6).  时空互换

7).  层级架构(树、图)vs 平坦架构(数组、链表)

8).  分时复用

9).  分布式

10). ……

PS:现实世界的设计往往都很巧妙,小而简洁的设计中往往包含着大哲学,大道至简,并不一定需要懂得复杂高深的算法才能设计程序,毕竟那些出现的几率很低。比如imageset,把小图片合并成大图片,原先老的显卡上不支持非2的幂的贴图,你一张界面底框即使800x600,也会占用1024x1024的内存,imageset把小图片合成在一起,即利用了原本浪费的内存,又减少了渲染时贴图切换的开销(dx的SetTextureStage),一定程度上还增加了读取磁盘文件的效率,一举三得,非常巧妙。

游戏开发中的一些基本方法,布布扣,bubuko.com

时间: 2024-10-27 02:24:16

游戏开发中的一些基本方法的相关文章

游戏开发中,图片资源的精简

在游戏开发中,包的大小总是与图片资源的大小密切相关,而图片资源中,大多为带有透明度信息的png图像. 那么,如何精简png图片资源呢? 1.图像压缩是一种方法,然而随着压缩率的增大.图片品质也越来越差.(舍弃) 2.我们另辟蹊径,采用png图像拆分.(近乎无损,资源精简) 一.原理:将png图像转化为两张jpeg图像进行存储 pngSplit下载 pngSplit使用说明 二.使用方法: 1.LibGdx中,通过Pixmap使用 // 如工程目录assets/texture/0_1.jpeg下:

游戏开发中的人工智能 复习

游戏开发中的人工智能 复习 (个人复习,一些仅是给自己的复习提示(=w=),转载注明出处:http://blog.csdn.net/hcbbt/article/details/42815479) 配套教材:游戏开发中的人工智能 知识点 移动 Bresenham,视线(略),拦截 // Bresenham if (deltaCol > deltaRow) { fraction = deltaRow * 2 - deltaCol; while (nextCol != endCol) { if (fr

&lt;游戏开发中的人工智能&gt; -- 阅读笔记

到家已经几天了, 休息了一阵, 是时候重新学习知识了. 接下去一段时间, 会啃<游戏开发中的人工智能>这本书, 顺便写写笔记. 马上就大三了, 想想自己选的游戏方向, 现在还蛋疼. 选了一个自己喜欢的方向, 但是确实最忙的一个,这也意味着少时间继续我的iOS学习. 也不知道是对是错. 既然选了,就学吧. 好不,不扯多了.接下去是该系列的笔记.(持续更新) 第一章: 游戏人工智能简介 1. 定性AI与非定性AI 定性行为或其表现是特定的,而且是可预测的,没有不确定性. 非定性行为有某种程度的不确

浅谈游戏开发中碰撞检测

原创整理不易,转载请注明出处:使用Memcached.Spring AOP构建数据库前端缓存框架 代码下载地址:http://www.zuidaima.com/share/1781569917635584.htm 数 据库访问可能是很多网站的瓶颈.动不动就连接池耗尽.内存溢出等.前面已经讲到如果我们的网站是一个分布式的大型站点,那么使用memcached实现数 据库的前端缓存是个很不错的选择:但如果网站本身足够小只有一个服务器,甚至是vps的那种,不推荐使用memcached,使用Hiberna

cocos2dx 游戏开发中常用场景切换方式以及特性

runWithScene(CCScene* scene):启动游戏,并运行scene 场景.这个方法在主程序启动时第一次启动主场景时调用. replaceScene(CCScene* scene):直接使用传入的scene 替换当前场景来切换画面,当前场景将被释放.这是切换场景时 最常用的方法. pushScene(CCScene* scene):在不释放旧场景内存的情况下运行新场景,推进新场景相当于在当前可见的纸上再放一张纸,而之前的纸位置何持不变.适用情况: 1.推进一个经常被用到的场景,例

android游戏开发中图形绘制:Canvas和Paint的使用

android游戏开发中,使用android.graphics中的类来绘制2D向量图和文字. 一 画布Canvas 在Android中的绘图应该继承View组件,并重写它的onDraw(Canvas canvas)方法. Canvas代表指定View上的画布,常用方法如图: 二 画刷Paint Paint代表Canvas上的画刷,主要用于绘制风格,包括画刷颜色.画刷笔触粗细.填充风格等. 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关. 常用方法如图: 三 路径Path Path表示

Redis在游戏开发中的应用

Redis是一个新兴的NoSql数据缓存组件,与memcache类似,但是功能却比memcache多一些.首先,Redis和memcache都是基于内存的,所以读取和写入速度都非常快.但是memcache只支持简单的key-value数据的存储方式,而Redis对key-value ,hash,list,set,SortSet等数据结构有很好的支持.下面就Redis在游戏的开发应用中做一些简单的介绍. IT图书网:http://www.myitbook.cn 一,数据的缓存 在这一点上,redi

iOS开发中自定义字体的方法

http://www.cnblogs.com/iyou/archive/2014/05/25/3751669.html 1. 首先下载你想要设置的字体库,例如设置方正启体简体 2. 添加到工程,一定要注意勾选红色框框处,默认是不勾选的  添加以后 3.在plist文件中添加 4.现在已经添加成功了,但是要使用就必须知道FontName,用以下代码可查到 NSArray *familyNames = [[NSArray alloc] initWithArray:[UIFont familyName

c++实现游戏开发中常用的对象池(含源码)

c++实现游戏开发中常用的对象池(含源码) little_stupid_child2017-01-06上传 对象池的五要素: 1.对象集合 2.未使用对象索引集合 3.已使用对象索引集合 4.当前使用量 5.最大使用量 http://download.csdn.net/download/little_stupid_child/9730912