Qt 学习之路 2(33):贪吃蛇游戏(3)
Qt 学习之路 2(33):贪吃蛇游戏(3)
继续前面一章的内容。上次我们讲完了有关蛇的静态部分,也就是绘制部分。现在,我们开始添加游戏控制的代码。首先我们从最简单的四个方向键开始:
{
head.rx() -= SNAKE_SIZE;
if (head.rx() < -100) {
head.rx() = 100;
}
}
void Snake::moveRight()
{
head.rx() += SNAKE_SIZE;
if (head.rx() > 100) {
head.rx() = -100;
}
}
void Snake::moveUp()
{
head.ry() -= SNAKE_SIZE;
if (head.ry() < -100) {
head.ry() = 100;
}
}
void Snake::moveDown()
{
head.ry() += SNAKE_SIZE;
if (head.ry() > 100) {
head.ry() = -100;
}
}
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
29
30
31
|
void Snake::moveLeft()
{
head.rx() -= SNAKE_SIZE;
if (head.rx() < -100) {
head.rx() = 100;
}
}
void Snake::moveRight()
{
head.rx() += SNAKE_SIZE;
if (head.rx() > 100) {
head.rx() = -100;
}
}
void Snake::moveUp()
{
head.ry() -= SNAKE_SIZE;
if (head.ry() < -100) {
head.ry() = 100;
}
}
void Snake::moveDown()
{
head.ry() += SNAKE_SIZE;
if (head.ry() > 100) {
head.ry() = -100;
}
}
|
我们有四个以 move 开头的函数,内容都很类似:分别以 SNAKE_SIZE 为基准改变头部坐标,然后与场景边界比较,大于边界值时,设置为边界值。这么做的结果是,当蛇运动到场景最右侧时,会从最左侧出来;当运行到场景最上侧时,会从最下侧出来。
然后我们添加一个比较复杂的函数,借此,我们可以看出 Graphics View Framework 的强大之处:
{
QList collisions = collidingItems();
// Check collisions with other objects on screen
foreach (QGraphicsItem *collidingItem, collisions) {
if (collidingItem->data(GD_Type) == GO_Food) {
// Let GameController handle the event by putting another apple
controller.snakeAteFood(this, (Food *)collidingItem);
growing += 1;
}
}
// Check snake eating itself
if (tail.contains(head)) {
controller.snakeAteItself(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void Snake::handleCollisions()
{
QList collisions = collidingItems();
// Check collisions with other objects on screen
foreach (QGraphicsItem *collidingItem, collisions) {
if (collidingItem->data(GD_Type) == GO_Food) {
// Let GameController handle the event by putting another apple
controller.snakeAteFood(this, (Food *)collidingItem);
growing += 1;
}
}
// Check snake eating itself
if (tail.contains(head)) {
controller.snakeAteItself(this);
}
}
|
顾名思义,handleCollisions()
的意思是处理碰撞,也就是所谓的“碰撞检测”。首先,我们使用collidingItems()
取得所有碰撞的元素。这个函数的签名是:
Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
1
2
|
QList<QGraphicsItem *> QGraphicsItem::collidingItems(
Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
|
该函数返回与这个元素碰撞的所有元素。Graphcis View Framework 提供了四种碰撞检测的方式:
Qt::ContainsItemShape
:如果被检测物的形状(shape()
)完全包含在检测物内,算做碰撞;Qt::IntersectsItemShape
:如果被检测物的形状(shape()
)与检测物有交集,算做碰撞;Qt::ContainsItemBoundingRect
:如果被检测物的包含矩形(boundingRect()
)完全包含在检测物内,算做碰撞;Qt::IntersectsItemBoundingRect
:如果被检测物的包含矩形(boundingRect()
)与检测物有交集,算做碰撞。
注意,该函数默认是Qt::IntersectsItemShape
。回忆一下,我们之前编写的代码,Food
的boundingRect()
要大于其实际值,却不影响我们的游戏逻辑判断,这就是原因:因为我们使用的是Qt::IntersectsItemShape
判断检测,这与boundingRect()
无关。
后面的代码就很简单了。我们遍历所有被碰撞的元素,如果是食物,则进行吃食物的算法,同时将蛇的长度加 1。最后,如果身体包含了头,那就是蛇吃了自己的身体。
还记得我们在 Food 类中有这么一句:
1
|
setData(GD_Type, GO_Food);
|
QGraphicsItem::setData()
以键值对的形式设置元素的自定义数据。所谓自定义数据,就是对应用程序有所帮助的用户数据。Qt 不会使用这种机制来存储数据,因此你可以放心地将所需要的数据存储到元素对象。例如,我们在Food
的构造函数中,将GD_Type
的值设置为GO_Food
。那么,这里我们取出GD_Type
,如果其值是GO_Food
,意味着这个QGraphicsItem
就是一个Food
,因此我们可以将其安全地进行后面的类型转换,从而完成下面的代码。
下面是advance()
函数的代码:
{
if (!step) {
return;
}
if (tickCounter++ % speed != 0) {
return;
}
if (moveDirection == NoMove) {
return;
}
if (growing > 0) {
QPointF tailPoint = head;
tail << tailPoint;
growing -= 1;
} else {
tail.takeFirst();
tail << head;
}
switch (moveDirection) {
case MoveLeft:
moveLeft();
break;
case MoveRight:
moveRight();
break;
case MoveUp:
moveUp();
break;
case MoveDown:
moveDown();
break;
}
setPos(head);
handleCollisions();
}
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
29
30
31
32
33
34
35
36
37
38
39
|
void Snake::advance(int step)
{
if (!step) {
return;
}
if (tickCounter++ % speed != 0) {
return;
}
if (moveDirection == NoMove) {
return;
}
if (growing > 0) {
QPointF tailPoint = head;
tail << tailPoint;
growing -= 1;
} else {
tail.takeFirst();
tail << head;
}
switch (moveDirection) {
case MoveLeft:
moveLeft();
break;
case MoveRight:
moveRight();
break;
case MoveUp:
moveUp();
break;
case MoveDown:
moveDown();
break;
}
setPos(head);
handleCollisions();
}
|
QGraphicsItem::advance()
函数接受一个 int 作为参数。这个 int 代表该函数被调用的时间。QGraphicsItem::advance()
函数会被QGraphicsScene::advance()
函数调用两次:第一次时这个 int 为 0,代表即将开始调用;第二次这个 int 为 1,代表已经开始调用。在我们的代码中,我们只使用不为 0 的阶段,因此当 !step 时,函数直接返回。
tickCounter
实际是我们内部的一个计时器。我们使用 speed 作为蛇的两次动作的间隔时间,直接影响到游戏的难度。speed 值越大,两次运动的间隔时间越大,游戏越简单。这是因为随着 speed 的增大,tickCounter % speed != 0 的次数响应越多,刷新的次数就会越少,蛇运动得越慢。
moveDirection
显然就是运动方向,当是 NoMove 时,函数直接返回。
growing
是正在增长的方格数。当其大于 0 时,我们将头部追加到尾部的位置,同时减少一个方格;当其小于 0 时,我们删除第一个,然后把头部添加进去。我们可以把 growing 看做即将发生的变化。比如,我们将 growing 初始化为 7。第一次运行advance()
时,由于 7 > 1,因此将头部追加,然后 growing 减少 1。直到 growing 为 0,此时,蛇的长度不再发生变化,直到我们吃了一个食物。
下面是相应的方向时需要调用对应的函数。最后,我们设置元素的坐标,同时检测碰撞。
Qt 学习之路 2(33):贪吃蛇游戏(3)的更多相关文章
- Qt 学习之路 2(32):贪吃蛇游戏(2)
Qt 学习之路 2(32):贪吃蛇游戏(2) 豆子 2012年12月27日 Qt 学习之路 2 55条评论 下面我们继续上一章的内容.在上一章中,我们已经完成了地图的设计,当然是相当简单的.在我们的游 ...
- Qt 学习之路 2(34):贪吃蛇游戏(4)
Qt 学习之路 2(34):贪吃蛇游戏(4) 豆子 2012年12月30日 Qt 学习之路 2 73条评论 这将是我们这个稍大一些的示例程序的最后一部分.在本章中,我们将完成GameControlle ...
- Qt 学习之路 2(31):贪吃蛇游戏(1)
Qt 学习之路 2(31):贪吃蛇游戏(1) 豆子 2012年12月18日 Qt 学习之路 2 41条评论 经过前面一段时间的学习,我们已经了解到有关 Qt 相当多的知识.现在,我们将把前面所讲过的知 ...
- 《Qt 学习之路 2》目录
<Qt 学习之路 2>目录 <Qt 学习之路 2>目录 豆子 2012年8月23日 Qt 学习之路 2 177条评论 <Qt 学习之路 2>目录 序 Qt ...
- Qt 学习之路 2(71):线程简介
Qt 学习之路 2(71):线程简介 豆子 2013年11月18日 Qt 学习之路 2 30条评论 前面我们讨论了有关进程以及进程间通讯的相关问题,现在我们开始讨论线程.事实上,现代的程序中,使用线程 ...
- Qt 学习之路 2(69):进程
Qt 学习之路 2(69):进程 豆子 2013年11月9日 Qt 学习之路 2 15条评论 进程是操作系统的基础之一.一个进程可以认为是一个正在执行的程序.我们可以把进程当做计算机运行时的一个基础单 ...
- Qt 学习之路 2(67):访问网络(3)
Qt 学习之路 2(67):访问网络(3) 豆子 2013年11月5日 Qt 学习之路 2 16条评论 上一章我们了解了如何使用我们设计的NetWorker类实现我们所需要的网络操作.本章我们将继续完 ...
- Qt 学习之路 2(66):访问网络(2)
Home / Qt 学习之路 2 / Qt 学习之路 2(66):访问网络(2) Qt 学习之路 2(66):访问网络(2) 豆子 2013年10月31日 Qt 学习之路 2 27条评论 上一 ...
- Qt 学习之路 2(63):使用 QJson 处理 JSON
Home / Qt 学习之路 2 / Qt 学习之路 2(63):使用 QJson 处理 JSON Qt 学习之路 2(63):使用 QJson 处理 JSON 豆子 2013年9月9日 Qt ...
随机推荐
- Apache与Tomcat联系及区别
Apache与Tomcat都是Apache开源组织开发的用于处理HTTP服务的项目,两者都是免费的,都可以做为独立的Web服务器运行.Apache是Web服务器而Tomcat是Java应用服务器. A ...
- Solr4.3---4.6删除数据的办法
Solr4.6的管理界面上,如果不配置数据导入的功能,将看不到清除数据的按钮.我表示很遗憾,正好我们线上没有配置数据导入的功能. 网上搜到的各种清理solr数据的HTTP请求,拿到我的solr4.6上 ...
- 【bzoj1083】[SCOI2005]繁忙的都市
1083: [SCOI2005]繁忙的都市 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2424 Solved: 1591[Submit][Sta ...
- windows7 Sql server 2012 尝试读取或写入受保护的内存。这通常指示其他内存已损坏的修复
项目中,使用了sql server2012数据库,服务端是2012,客户端如果是2008的话,就会报错: 索引错误. 没办法,就安装了sql server2012客户端.但是还是报错,无法连上数据库服 ...
- 怎样使用Mock Server
转载自:http://www.cnblogs.com/111testing/p/6091460.html 怎样使用Mock Server 一,去这里https://github.com/dream ...
- jQuery--加一行减一行
效果: 知识点: 克隆--clone() 追加--append() 移除--remove() 代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2 ...
- Luogu 4159 [SCOI2009]迷路
BZOJ 1297 应当是简单题. 发现边权的数量很小,所以我们暴力把一个点拆成$9$个点,然后把$(x, i)$到$(x, i + 1)$连边,代表转移一次之后可以走回来:对于每一条存在的边$(i, ...
- 智能IC卡中的文件系统
1.文件系统是COS的重要模块之一,它负责组织.管理.维护IC卡内存储的所有数据. 文件系统的设计和实现既是COS系统中最灵活.最有个性的部分,也是对系统整体结构影响最大的模块之一. 2.在IC卡内, ...
- ThinkPHP3.2.3完整版中对Auth.class.php的使用
一,先创建数据表 1.think_auth_rule,规则表 id:主键, name:规则唯一标识, title:规则中文名称 status 状态:为1正常,为0禁用, condition:规则表达式 ...
- python2.7响应数据中unicode转中文
print ("响应结果:%s" % r.content.decode('unicode_escape')) 一. 在爬虫抓取网页信息时常需要将类似"\u4eba\u75 ...