Cocos2dx学习笔记(28)——游戏开发小技巧

游戏开发用到了很多小技巧,本章将不断更新,记录一些实用的知识。

本文仅供个人记录和复习,不用于其他用途

简单的碰撞检测

如果看了项目实战,就会发现那几个小游戏的碰撞检测实现的很巧妙。我们为角色建立一个等身的矩形,然后判断敌人的矩形和角色是否相互包含。这种方法实现起来很简单,不过要注意矩形的大小以及矩形的中心,在实际运用中要尽量贴近精灵的大小。

矩形与矩形碰撞

1
2
3
4
5
6
7
8
9
10
11
// 通过CCRect自定义的函数判断是否相交
rect1.intersectsRect(rect2);
// 具体实现如下
bool CCRect::intersectsRect(const CCRect& rect) const
{
return !( getMaxX() < rect.getMinX() ||
rect.getMaxX() < getMinX() ||
getMaxY() < rect.getMinY() ||
rect.getMaxY() < getMinY());
}

圆与圆碰撞

有时候物体的碰撞范围是一个圆,那么在与另一个圆碰撞时,只需检测圆心距是否小于两圆半径之和即可。

1
2
3
4
5
6
7
8
bool collision_CircleWithCircle(CCPoint p1, float r1, CCPoint p2, float r2)
{
// 计算圆心距离
float dist = p1.getDistance(p2);
// 判断两圆是否相交
return dist < (r1+r2) ;
}

矩形与圆

看起来情况很多很复杂,但是我们只需简化一下,如下图:

我们让圆绕着矩形滚一圈,这个时候可以得到一个圆角矩形。很显然,只要圆心在这个圆角矩形内,那么圆和矩形就发生了碰撞。我们可以将圆角矩形分成四个圆角和十字形,然后判断圆心有没有落在十字形区域内。如果没有,那么再判断圆心是否在四个角,都没有的话证明没有发生碰撞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool collision_RectWithCircle(CCRect rect, CCPoint p, float r)
{
// 获取矩形信息
// 左下角坐标:( lx , ly )
// 右上角坐标:( rx , ry )
float lx = rect.getMinX();
float ly = rect.getMinY();
float rx = rect.getMaxX();
float ry = rect.getMaxY();
// 计算圆心到四个顶点的距离
float d1 = p.getDistance( ccp(lx, ly) );
float d2 = p.getDistance( ccp(lx, ry) );
float d3 = p.getDistance( ccp(rx, ly) );
float d4 = p.getDistance( ccp(rx, ry) );
// 判断是否碰撞
// 判断距离是否小于半径
if( d1<r || d2<r || d3<r || d4<r ) return true;
// 是否在圆角矩形的,横向矩形内
if( p.x > (lx-r) && p.x < (rx+r) && p.y > ly && p.y < ry ) return true;
// 是否在圆角矩形的,纵向矩形内
if( p.x > lx && p.x < rx && p.y > (ly-r) && p.y < (ry+r) ) return true;
// 不发生碰撞
return false;
}

最后要说的一点,这个并不是Box2D物理碰撞,这个只是小游戏中经常用到的技巧。

头文件相互包含

除了使用ifndef之外,还可以将头文件的包含放到.cpp中,这样不会发生问题。如果相互包含后出现找不到类的情况,那么可以在头文件中声明这个类。比如A.hB.h相互包含,如果找不到类B,那么可以在A.h中声明class B

获取当前时间

我们使用随机函数或者做时钟时,经常要获取当前时间,那么可以使用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 获取时间戳
int getTimeStamp()
{
timeval tm;
gettimeofday(&tm, NULL);
return tm.tv_sec; // 单位:秒
// return tm.tv_usec; // 单位:毫秒
}
// 获取本地时间
time_t t = time(NULL);
tm* lt = localtime(&t);
int year = lt->tm_year + 1900; // 相对1900年的过去的年数
int month = lt->tm_mon + 1; // 1月份:为0
int yday = lt->tm_yday; // 年第几天:从1开始
int mday = lt->tm_mday; // 月第几天:从1开始
int wday = lt->tm_wday; // 周第几天:从1开始
int hh = lt->tm_hour; // 时
int mm = lt->tm_min; // 分
int ss = lt->tm_sec; // 秒
printf("%d %d\n", year, month);
printf("%d %d %d\n", yday, mday, wday);
printf("%d %d %d\n", hh, mm, ss);

顺便说一下,随机函数需要设立随机数种子,否则永远都是相同的数:

1
srand((unsigned int)time(NULL));

定义文件路径

每次在加载文件时,总要打上一长串的文件路径,显得很繁琐。我们可以找到applicationDidFinishLaunching()这个方法,然后加上下面的代码:

1
2
3
4
5
std::vector<std::string> paths; // 创建一个string型的容器
paths.push_back("fonts"); // 往容器中添加图片目录所在的路径
paths.push_back("image");
paths.push_back("sounds");
FileUtils::getInstance()->setSearchResolutionsOrder(paths);

这样一来,我们就将fontsimagesounds三个文件进行了定义,那么输入文件路径的时候也不需要再加上这些。

获取文件夹下所有文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
std::vector<std::string> VisibleRect::getFilePathAtVec(std::string filePath)
{
std::vector<std::string> path_vec;
const char* path = filePath.c_str();
char *dir = (char*)malloc(filePath.size() + 1);
sprintf(dir, path);
DIR *dp;
struct dirent *entry;
struct stat statbuf;
int i=0;
if((dp=opendir(dir))==NULL)
{
fprintf(stderr,"cannot open %s",dir);
exit(1);
}
chdir(dir);
while((entry=readdir(dp))!=NULL&&i<255)
{
stat(entry->d_name,&statbuf);
if(!S_ISREG(statbuf.st_mode))
continue;
path_vec.push_back(StringUtils::format("%s",entry->d_name));
}
return path_vec;
}