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 ...
随机推荐
- iPython notebook 安装使用
pip install jupyter jupyter notebook --allow-root
- 深入剖析SolrCloud(四)
作者:洞庭散人 出处:http://phinecos.cnblogs.com/ 本博客遵从Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由 ...
- libevent源码深度剖析四
libevent源码深度剖析四 ——libevent源代码文件组织 1 前言 详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将是大有裨益的.本节内容不多,我想并不 ...
- cocos2d-x 初探helloWorld
cocos2d-x的main函数代码很少,把一些复杂的接口封装到AppDelegate类里了,“AppDelegate”从词意可以得出是app的代理类,而一些最早的场景都会在AppDelegate类里 ...
- dubbo 相关面试题 有用
调用关系说明: · 0. 服务容器负责启动,加载,运行服务提供者. · 1. 服务提供者在启动时,向注册中心注册自己提供的服务. · 2. 服务消费者在启动时,向注册中心订阅自己所需的服务. · 3. ...
- Math.max()
返回两个指定的数中带有较大的值的那个数.
- Selenium家谱
自动化测试一词也算是整个测试行业里面比较热门的一个词儿,工资高,前景好,有实力,有态度等等,很多企业的管理者也在不断的扩大自己的队伍和职能,这也是导致自动化测试比较流行的原因之一.但是很多企业做自动化 ...
- python核心编程第5章课后题答案
5-8Geometry import math def sqcube(): s = float(raw_input('enter length of one side: ')) print 'the ...
- python2.7响应数据中unicode转中文
print ("响应结果:%s" % r.content.decode('unicode_escape')) 一. 在爬虫抓取网页信息时常需要将类似"\u4eba\u75 ...
- mysql数据库学习小结
数据库的学习可以从以下几个层次了解掌握,这样思路清晰后后面不管怎么变化都可以随时应变: 1.mysql基础知识 2.操作数据库的方法,增 删 改 查 3.jdbc连接数据库,工作原理 难点重点,如:P ...