这个游戏来源于一篇较早的国外作品,不过原作是以Cocos2D为基础实现的,链接见下:
http://www.raywenderlich.com/37701/how-to-make-a-tower-defense-game-tutorial
这里呢,采用Qt5.1的库,进行移植了,这里就直接如主题了:
先看下游戏运行效果:
这里的图片,感谢原作者无私的资源,嘿嘿,借来用用
背景地图大小是480*320,这也是游戏界面的固定大小,setFixedSize(480, 320)
好了,看完效果,一步步走吧!
这是我的编译环境,去下最新版本的Qt就可以了,Qt4.x的不保证可以跑起来,没测过啦~,\(^o^)/~
1、绘制游戏背景,加载炮塔安放位置
创建一个工程,窗口采用QMainWindow或QWidget都可以,这里就用QWidget了,配上ui文件
设置好固定大小,就可以和ui文件说拜拜了,接下来基本上就都是绘制了
添加图片资源文件,这里的源代码在文章结束后会放出链接的,资源文件都在那了,友情设置0分,欢迎下载,给我长点分呗,hohoho~
然后就先画上背景图片呗~
重载MainWindow(就是QWidget)中的paintEvent函数:
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawPixmap(0, 0, QPixmap(":/image/Bg.png"));
}
再来需要加载可以安放塔的位置,这个,原作者采用读取xml文件的,这里做测试,就直接先用数组替换了,那个xml文件的数值似乎有问题,因此这里我查找了下点,这里是我找的测试安放坐标点(这样貌似缺少弹性,以后再改为xml读取嘛,笑~)
关于TowerPosition应该有的信息见下:
这里,明显m_pos就是安放塔位置进行绘制的坐标点(左上角)
m_hasTower,用于表明该位置是否有塔
m_sprite则是保存绘制图片
需要的主要就是绘制方法,已经获得将来判断点击的点是否包含在该区域内,以决定是否可以安放塔
先查看下声明吧!
class TowerPosition
{
public:
TowerPosition(QPoint pos, const QPixmap &sprite = QPixmap(":/image/open_spot.png"));
void setHasTower(bool hasTower = true);
bool hasTower() const;
const QPoint centerPos() const;
bool containPoint(const QPoint &pos) const;
void draw(QPainter *painter) const;
private:
QPoint m_pos;
bool m_hasTower;
QPixmap m_sprite;
static const QSize ms_fixedSize;
};
这里很简单,相应的实现也非常的直白,见下:
const QSize TowerPosition::ms_fixedSize(44, 44);
TowerPosition::TowerPosition(QPoint pos, const QPixmap &sprite/* = QPixmap(":/image/open_spot.png")*/)
: m_pos(pos)
, m_hasTower(false)
, m_sprite(sprite)
{
}
const QPoint TowerPosition::centerPos() const
{
QPoint offsetPoint(ms_fixedSize.width() / 2, ms_fixedSize.height() / 2);
return m_pos + offsetPoint;
}
bool TowerPosition::containPoint(const QPoint &pos) const
{
bool isXInHere = m_pos.x() < pos.x() && pos.x() < (m_pos.x() + ms_fixedSize.width());
bool isYInHere = m_pos.y() < pos.y() && pos.y() < (m_pos.y() + ms_fixedSize.height());
return isXInHere && isYInHere;
}
bool TowerPosition::hasTower() const
{
return m_hasTower;
}
void TowerPosition::setHasTower(bool hasTower/* = true*/)
{
m_hasTower = hasTower;
}
void TowerPosition::draw(QPainter *painter) const
{
painter->drawPixmap(m_pos.x(), m_pos.y(), m_sprite);
}
之后所有对象信息,几乎都会包含这3个,坐标点,尺寸大小,图片信息,管理也都集中放在容器交给MainWindow管理
在MainWindow的private区域添加
QList<TowerPosition> m_towerPositionsList;
同时添加私有方法loadTowerPosition用于从XML文件中读取塔安放位置的信息(m_pos),不过目前从简
查看下实现:
void MainWindow::loadTowerPositions()
{
QPoint pos[] =
{
QPoint(65, 220),
QPoint(155, 220),
QPoint(245, 220),
QPoint(335, 220),
QPoint(100, 125),
QPoint(195, 125),
QPoint(280, 125),
QPoint(370, 125),
QPoint(80, 35),
QPoint(170, 35),
QPoint(260, 35),
QPoint(350, 35)
};
int len = sizeof(pos) / sizeof(pos[0]);
for (int i = 0; i < len; ++i)
m_towerPositionsList.push_back(pos[i]);
}
这里没有new,是因为TowerPosition结构简单,而且本身也只会被初始化一次,因此就这样愉快决定简单处理啦,哦也
然后再paintEvent中补上绘制就Ok了!
对了,还需要在MainWindow的构造中调用loadTowerPosition才可以,可以看下效果图啦~
终于完成第一步了,起码游戏界面现在看起来是那么回事了,想来应该是要添加一些家园卫士了,攻击塔搞起
2、攻击塔的初步实现
这里只是一个简单,简单,非常简单,不带减速的小小攻击塔,可以自由添加发挥~\(≧▽≦)/~啦啦啦
同样,先看下攻击塔有哪些必要属性:
首先有必备的3件套,坐标点,尺寸大小(这个是静态常量,其实就是图片的大小),图片
额外的属性,根据名字也很好猜测
m_attackRange,攻击范围,就是以塔的中心为原点,绘制一个圆,这个的半径就是攻击范围,默认为70
m_damage,塔的伤害值,后期用到,对敌人造成的费血,原作者看来相当不残忍,攻击力居然只有10点,好吧,我数值平衡很差啦~
m_fireRate,攻击平率,这里是用毫秒记,默认是1000ms,也就是1秒攻击1次
需要添加的方法,目前只有draw方法:
draw中干2件事,先绘制一个白色的圆,这是攻击范围(主要是方便测试观察),再绘制塔
这里的m_pos则表示塔的圆心
painter->save();
painter->setPen(Qt::white);
// 绘制攻击范围
painter->drawEllipse(m_pos, m_attackRange, m_attackRange);
// 绘制偏转坐标,由中心+偏移=左上
// 尺寸大小派上用场了,当然也可以直接获取图片大小,是一样的
static const QPoint offsetPoint(-ms_fixedSize.width() / 2, -ms_fixedSize.height() / 2);
// 绘制炮塔并选择炮塔
// 这里将坐标原点移到m_pos,绘制的适合,就要加上那个偏移点到左上角
painter->translate(m_pos);
painter->drawPixmap(offsetPoint, m_sprite);
painter->restore();
这里由于要改变painter一些设置,因此最好加上save/restore来作恢复使用
这样,就完成了攻击塔的绘制,赶快来解决下点击事件吧,看看效果吧!
为MainWindow重载mousePressEvent和添加canBuyTower处理函数(名字就该清晰了吧,后期需要用钱买,现在先免费)
同时为MainWindow添加成员
QList<Tower *> m_towersList; // 用来管理攻击塔的信息
重点查看下那两个方法呗:
void MainWindow::mousePressEvent(QMouseEvent *event)
{
QPoint pressPos = event->pos();
auto it = m_towerPositionsList.begin();
while (it != m_towerPositionsList.end())
{
if (canBuyTower() && it->containPoint(pressPos) && !it->hasTower())
{
it->setHasTower();
Tower *tower = new Tower(it->centerPos(), this);
m_towersList.push_back(tower);
update();
break;
}
++it;
}
}
bool MainWindow::canBuyTower() const
{
return true;
}
这里auto it = xxx.begin();是用到了C++11的新语法,这个随意啦,需要使用,在pro文件中添加
CONFIG += c++11
这里代码逻辑简单,遍历所有安放位置,点在安放位置 && 有钱(暂时肯定有啦~) && 没有别的塔
就添加入m_towerList管理中,看下效果吧!
还需要在paintEvent中添加相应绘制代码:
foreach (Tower *tower, m_towersList)
tower->draw(&painter);
之后,可以看下效果图,点中区域,就好了:
现在塔也出来了,nice!
3、为敌人出现做准备,添加敌人行走路线,航点
塔防游戏路线其实不需要什么特别算法,针对本游戏更加简单,左上为入口,右下为基地,路线其实就是这么一个形状,先看效果图:
一共也就6个点,路线也是很正,都是直上直下,直男天下,哈哈
可惜不知道怎么了,原作者给的那几个点我用来都是有问题的,因此自己又重新测了下
下面是我添加的这几点:
void MainWindow::addWayPoints()
{
WayPoint *wayPoint1 = new WayPoint(QPoint(420, 285));
m_wayPointsList.push_back(wayPoint1);
WayPoint *wayPoint2 = new WayPoint(QPoint(35, 285));
m_wayPointsList.push_back(wayPoint2);
wayPoint2->setNextWayPoint(wayPoint1);
WayPoint *wayPoint3 = new WayPoint(QPoint(35, 195));
m_wayPointsList.push_back(wayPoint3);
wayPoint3->setNextWayPoint(wayPoint2);
WayPoint *wayPoint4 = new WayPoint(QPoint(445, 195));
m_wayPointsList.push_back(wayPoint4);
wayPoint4->setNextWayPoint(wayPoint3);
WayPoint *wayPoint5 = new WayPoint(QPoint(445, 100));
m_wayPointsList.push_back(wayPoint5);
wayPoint5->setNextWayPoint(wayPoint4);
WayPoint *wayPoint6 = new WayPoint(QPoint(35, 100));
m_wayPointsList.push_back(wayPoint6);
wayPoint6->setNextWayPoint(wayPoint5);
}
这里用一个小封装的WayPoint来存储节点,WayPoint行为像是一个逆序链表,第一点其实是基地,(我比较懒,没有修改,其实觉得用Qt自带容器就可以了,而且为毛要逆序,顺序不就可以了),那个setNextWayPoint,其实存放的也是后一个节点,只不过节点6才是起始节点,蛋疼ing,对于WayPoint不解释了,也是那几点,在MainWindow中也需要管理:
1.声明:
// 敌人移动的航线
class WayPoint
{
public:
WayPoint(QPoint pos);
void setNextWayPoint(WayPoint *nextPoint);
WayPoint* nextWayPoint() const;
const QPoint pos() const;
void draw(QPainter *painter) const;
private:
QPoint m_pos;
WayPoint * m_nextWayPoint;
};
2.实现
WayPoint::WayPoint(QPoint pos)
: m_pos(pos)
, m_nextWayPoint(NULL)
{
}
void WayPoint::setNextWayPoint(WayPoint *nextPoint)
{
m_nextWayPoint = nextPoint;
}
WayPoint* WayPoint::nextWayPoint() const
{
return m_nextWayPoint;
}
const QPoint WayPoint::pos() const
{
return m_pos;
}
void WayPoint::draw(QPainter *painter) const
{
painter->save();
painter->setPen(QColor(0, 255, 0));
painter->drawEllipse(m_pos, 6, 6);
painter->drawEllipse(m_pos, 2, 2);
if (m_nextWayPoint)
painter->drawLine(m_pos, m_nextWayPoint->m_pos);
painter->restore();
}
3.MainWindow中添加
QList<WayPoint *> m_wayPointsList; // 在paintEvent中需要进行绘制,那个类似的foreach(xxx) xxx.draw(xxx)
void addWayPoints(); // 在构造函数中调用
这几步曲几乎是后续元素添加的顺序
这里航点是由一个两个圆套起来显示的,敌人会和航点圆心做碰撞检查,碰撞到了,就要开始调换方向,向下一个原点进发
好了,今天太晚了,先写到这里,之后继续~
未完Go on!
文章浏览阅读1.4k次。1、Ctrl+←或→ :跳过(左边或右边)一个光标相邻的单词或词组(标点符号相当于一个单词)。点击前光标位置:点击后光标位置:2、Shift+←或→:选中(左边或右边)一个光标相邻的字符。点击前显示:点击后显示: 3、Shift+Ctrl+←或→:选中(左边或右边)一个光标相邻的单词或词组(标点符号相当于一个单词)。点击前显示:点击后显示:4、Home/End:光标定位到当前行的行头/行尾。点击前:点击Home后:点击End后:5、Ctrl+Home/End:从光标所在位置直接回到当前文件开头/结尾。点击前_改代码快捷键
树是一个有n个有限数据元素的集合,其中有一个根节点,并且每个节点可以有多个子节点。树的深度与查找有关,可通过改进合并算法来减少树的深度,提高算法效率。
文章浏览阅读938次,点赞23次,收藏21次。反欺诈策略是为防范恶意客户采取欺诈行为谋取利益而制订的策略,目的是通过对欺诈行为的识别,遏制欺诈风险,为金融机构止损。根据欺诈的不同维度,欺诈的分类目前,应对欺诈风险的有效措施包括反欺诈规则和反欺诈模型。
文章浏览阅读10w+次,点赞40次,收藏332次。yum是用来管理rpm的,就跟maven管理jar包相似。yum源(库)分为本地库、网络库。首先要配置yum源,可支持多个源。先查看一下挂载情况:df -h这里我们要更换光盘,并挂载:mount /dev/cdrom /mnt(如果不能成功挂载,点击一下连接即可)之后再次使用 df -h命令,就能查看到光盘的内容。下面我们cd到 /mnt下查看一下:首先关注一下Pa..._安装yum
文章浏览阅读3.8k次,点赞5次,收藏12次。1.在设置CanTxMsg.StdId时注意需要将其右移一位,比如如下滤波器配置:CAN_FilterInitStructure.CAN_FilterNumber=0;CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;CAN_FilterInitStructure.CAN_FilterScale=CAN_Filter..._stm32can mailbox filter
文章浏览阅读373次。att has N friends. They are playing a game together. Each of Matt’s friends has a magic number. In the game, Matt selects some (could be zero) of his friends. If the xor (exclusive-or) sum of the selected friends’magic numbers is no less than M , Matt wi_matt has n friends. they are playing a game together.
文章浏览阅读861次。vue前端运行别人的项目_前端项目运行不了
文章浏览阅读161次。在先前关于Linux文件系统的文章中,我写了一份说明书去介绍Linux文件系统,里面有一些高级的概念,比如说,一切都是文件。我很想去深入地讨论更多EXT文件系统的特性的信息。所以,首先让我们来回答这个问题:什么是文件系统?一个文件系统应该遵循以下特点: 1.数据存储:文件系统主要的功能是结构化存储和取回数据。 2.命名空间:提供一套命名和组织的方法,就是命名和结构化数据的规则。 3.安全模型:一种访问控制的策略。 4.API:系统操控文件系统对象的函数,就像操作文件夹...
文章浏览阅读308次。西雅图IT圈:seattleit【今日作者】Dexter读书巨慢理事会会长别人家的公司什么样?坐拥巨额现金流的微软,一言不合就发钱。01昨天微软首席人事官凯瑟琳霍根宣布——将向微软全球员工..._微软 西雅图 年底奖金
文章浏览阅读2k次。通过配置远程扩展词典,可以读取远程词典,当改变远程词典时,不必重启服务器,elasticsearch会自动加载并进行分词。步骤:配置文件服务器,把远程扩展词典放到服务器下。修改elasticsearch目录下plugins\ik\config\IKAnalyzer.cfg.xml文件并保存,如下: <properties> <comment>IK A..._ik analyzer 扩展词典配置远程词典 可实时编辑
文章浏览阅读553次,点赞2次,收藏2次。代码已提交至Github,有兴趣的同学可以下载来看看(git版本号:bea4d6f7ec9f7309033bcfa43316a660171ae5b6):https://github.com/ylw-github/Zookeeper-Demo本文目录结构:l____1. 知识点回顾l________1.1 多线程l________1.2 Java共享内存模型l____2. 分布式锁的解决方..._分布式锁 的具体实现工具
文章浏览阅读9.3k次,点赞9次,收藏51次。Nginx网站服务详解,Nginx服务的主配置文件,修改,监听,配置,密码认证,以及IP和端口虚拟主机配置方法,含图文步骤拆解讲解_nginx.conf