回合卡牌的战斗模块

开发日志系列(十四)

cocos2d-x lua binding C/C++

在我目前参与的这款无操回合卡牌手游中,客户端战斗模块是由我实现的,在项目上线之前一直都生怕出现不可逆转的设计问题,所以我把模块流程描述给美术,别的程序同事听 同时尽量写出来.这个模块几乎全部使用C++实现.

战斗流程

解析服务端战斗数据创建战斗场景创建战斗对象进入场景按回合播放战斗特效对象退出场景显示战斗结果销毁回收内存/重播

  • 战斗数据解析器(parser)

这个解析器主要工作是解析和管理战斗初始数据.除此之外不做任何事情,目前项目中一般的战斗数据长度大概是1.5kb(1200~1600字节),在按数据结构(struct)解析数据的时候需要注意 服务端和客户端 的struct定义数据位对其的问题:

#pragma pack(1)
define some struct..
#pragma pack()

继续阅读“回合卡牌的战斗模块”

2dx中使用自定义位图字体

开发日志系列(十三)

成品预览

Font

环境与工具

cocos2d-x 2.2.3,AngelCode,PhotoShop

在我参与的项目中,由于美术对战斗表现非常苛刻,飘血等飘字的样式否需要逐字自定义,例如上图中的Zzz,闪避,反击,等等,最初他们设想是把所有飘字做成各个散图,用飘图来代替最初的飘文字.这绝对不是一种好的方法.于是我想到了自定义位图字体.在 cocos2d-x源码中发现CCLabelBMFont对位图字体支持的非常不错.因为CCLabelBMFont中仅仅看某个字的矩阵范围,而这个矩阵里是什么 有什么 完全不会干涉.创建位图字体首先需要使用一款叫做AngelCode的工具.步骤如下:

AngelCode的使用

  • 新建一个文本文件,写入要做成位图的字,例如:反击 挡 连击 闪 睡(Zzz)
  • 保存为UTF8格式
  • AngelCode中Edit->Select chars form file
  • AngelCode中Options->Font Setting 设置size大小
  • AngelCode中Options->Font Setting 选择默认字体
  • AngelCode中Options->Export Options修改 间隙像素 建议 > 3,
  • AngelCode中Options->Export Options修改Texture寛高
  • AngelCode中Options->Export Options修改Bit深度为32
  • AngelCode中Options->Export Options修改File Format为Text
  • AngelCode中Options->Export Options修改Textures为png文件
  • AngelCode中Options->Save Bitmap font as生成位图字体

继续阅读“2dx中使用自定义位图字体”

像素碰撞的一种实现

开发日志系列(十二)

环境: cocos2d-x 2.2.3 , win7

碰撞相信大家都不陌生了,无论在手游的游戏按钮对触摸事件的响应 还是页游时代游戏按钮对鼠标点击事件的侦听,都需要使用碰撞来判断.例如以下一个按钮,规格为:200X200,第二张图绿色部分原本为透明

按钮的原图

ButtonHasAlpha

绿色为透明部分

ButtonNoAlpha

坐标矩阵碰撞

类似这样一个按钮 如果是单纯使用坐标碰撞的话,只要坐标落在按钮图片内部,就会判断为点中,在cocos2d-x中坐标碰撞的一般做法是:

//pt 为鼠标 或者 触摸 击落时候的屏幕坐标
//cc  为按钮对象 继承CCNode
CCPoint pos = cc->convertToNodeSpace(pt);
CCRect rect = cc->boundingBox();
rect = CCRectApplyAffineTransform(rect, nodeToParentTransform());
if (rect.containsPoint( pos ))
{
    printf("用户点击了cc");
}

继续阅读“像素碰撞的一种实现”

C++静态绑定与动态绑定

开发日志系列(十一)

目前我参与的项目中使用了大量的接口调用,大量的继承(多重继承,多重派生等),其中的大部分都是使用了C++语言的动态绑定特性.既然有动态绑定,哪就相应的肯定有静态绑定,今晚复习了下这个特性.

静态绑定与动态绑定

静态绑定又名前期绑定(early binding),绑定的是对象的静态类型发生在编译期.动态绑定又名后期绑定(late binding),绑定的是对象的动态类型,发生在运行期.简单的说就是:静态绑定在编译完成后已经确定,动态绑定则需要在运行的时候才确定.考虑如下继承:

继续阅读“C++静态绑定与动态绑定”

2dx给Widget添加CCNode

项目开发日志(十)

环境:cocos2d-x 2.2.3,Lua 5.1.

在前几天给cocostudio增加了一个控件 – BigMap(大地图控件),用于承载游戏地图上的所有游戏实体,包括NPC,玩家,可能存在的宝箱,传送门等等.

但是在把实体(基类是CCNode)addChild到控件的时候,只显示了图片的一半,也就是一个三角形,一开始以为是底层BUG,后面发现在UIWidget.cpp源码中有这一句:

void Widget::addChild(CCNode* child, int zOrder, int tag)
{
    CCAssert(dynamic_cast(child) != NULL, "Widget only supports Widgets as children");
    CCNode::addChild(child, zOrder, tag);
    _widgetChildren->addObject(child);
}

继续阅读“2dx给Widget添加CCNode”

实体对象的设计(一)

项目开发日志(九)

在我参与的项目中的实体管理系统据说经过了很多游戏的磨练,本身已经很复杂,我只能记录我的理解,可能在往后的日子里有更深入的理解后会修改下面的一些认识.关于实体系统我可能会分比较多的部分来慢慢说,目前能想到的内容会有:实体对象的设计原则与继承关系,实体对象的属性分配,实体对象的消息.

  • 游戏实体对象

游戏实体系统在游戏编程系列著作< 游戏编程精粹四>中提及.以下摘抄一小段:


现代游戏充满各种各样的实体,玩家,敌人和发射的炮弹.这些要素在游戏世界里乱哄哄的跑来跑去,地形,建筑物,天空和云彩的定义环境.路径点(waypoint),触发器和脚本指导玩家体验.得分,破坏和无力方面组成游戏世界的逻辑和规则.不是对所有东西分别对待,作为特殊目的的要素,把他们结合为一个系统并且提供一个公用的结构和通信方法是很方便的,基于消息的方法来管理实体能够解决很多问题并提供一种方法统一大部分的关键游戏要素 – 摘自< 游戏编程精粹4> 第58页


继续阅读“实体对象的设计(一)”

2dx增加GUI控件

项目开发日志(八)

今天给cocos2dx2.2.3的cocostudio GUI增加遮罩控件,发现步骤还挺多.这里记录一下(省去编写代码的部分):

增加控件相关pkg文件

代码编写完成后,我把代码放到extensions\CocoStudio\GUI文件夹下.这个文件夹是cocostudio用来存放UI控件的默认文件夹.然后在文件夹tools\tolua++中增加控件pkg文件,例如我增加了UIBigMap.pkg,把需要导出给外界使用的函数从控件头文件中复制到pkg中.pkg的写法参照同文件夹下的其他pkg.

继续阅读“2dx增加GUI控件”

让模块接口化

项目开发日志(七) 模块逻辑解耦

(注:项目跨平台采用cocos2d-x,服务端 和 客户端游戏逻辑部分 采用C++,客户端UI部分采用Lua)

游戏项目一般模块众多功能庞杂,合理的逻辑解耦会让整个项目看起来结构清晰且易于维护,接口事件是逻辑解耦的常用方式,C++和C#,JAVA不同 并没有明确的接口化编程,也没有像AS3那种功能完备事件处理,但C++是一种高度自由的语言,我们可以通过C++的继承与动态绑定来实现接口化.下面是我参与的项目中模块与模块的关系设计原型.

继续阅读“让模块接口化”

定时器的设计

项目开发日志(六)

定时器在游戏项目中使用非常普遍,特别是在游戏服务端项目中,定时器检查函数会在每一帧调用.而保证游戏性能的关键就是在每一帧尽可能的运行少的代码,我们知道系统运行每一行代码其实是有时间的.所以每帧必然调用的代码量越少越好,在定时器的设计上 就要求我们尽可能的追求性能的极致.

一般的定时器设计原理

假设把整个游戏注册的所有定时器都放到一个vector容器中,一般的做法可能是每帧搜索一下这个vector,然后拿出来对比时间,时间到了则触发.这种在处理定时事件少的情况下是没有问题的,但是如果定时事件非常多的话,则需要改进下算法了.

继续阅读“定时器的设计”

多重继承中的动态绑定

项目开发日志(四)

C++是一种自由度非常高的语言,特别是面向对象中的动态绑定特性,正确使用这种特性会你写代码非常舒畅.但是错误的使用有时候也很致命.

今天在编写战斗逻辑层代码的时候在释放技能回调的地方使用了C++的多重继承,在一个继承了几个回调基类(技能释放完毕回调,移动完毕回调等)的派生类里面出现了无法绑定到正确的基类对象的问题.仔细分析后 发现是由于在第一层类型转换的时候已经把指针转换为void*,然后在最后一层调用无法正确转换.下面把问题重现:

继续阅读“多重继承中的动态绑定”