Cocos2d-x 3.2 学习笔记(十二)TimberMan!疯狂伐木工!
学习cocos2dx有一段时间了,试着做了2048游戏,最近又发现个经典游戏,啥也不说果断开工做自己的游戏——TimberMan!
首先说明:游戏资源摘自同类游戏,感谢这些游戏的资源让我完成自己的开发。
一、TimberMan玩法--根本停不下来!
这款游戏的玩法比较简单,通过手指点击左右屏幕来决定砍树站立的方向,不能让树枝碰触Hero,同时有时间限制(时间通过砍树增加),如果停止砍树时间结束=游戏结束。
让我们看看成品的效果吧!(ps:录像失帧,看到的不直观,可以下载已打包好的apk 在最下方)


二、代码结构
HelloWorldScene 主场景
TreeModel 树木(由树节点组合而成)
Timber 伐木Hero
TreeNode 树木节点
GameScore 场景生命和得分
GameOver 游戏结束
当然由小到大,一颗大树是由无数的树节点组成的,因此先写树结类,然后才是大树类,最后才是场景。这样拆分之后就很简单的做出了demo。
三、树节点TreeNode
树节点是一颗树的基础,包括树枝。当然,有没有树枝、树枝的方向是由 大树控制的。因此有如下枚举贯穿游戏
enum TreeBranchDirection
{
DEFINE,//无节点
LEFT,//左
RIGHT//右
};
节点的基本功能:1、设置树枝2、获取树枝类型(返回TreeBranchDirection)
void TreeNode::setBranch(TreeBranchDirection enums)
{
enumBranch = enums;
auto branch = this->getChildByName("branch");
auto body = this->getChildByName("body");
branch->setVisible(enums!=DEFINE);
if(enums==DEFINE)return;
if(enums == RIGHT)
{
branch->setScaleX();
branch->setPositionX(body->getContentSize().width);
}
else
{
branch->setScaleX(-);
branch->setPositionX(-body->getContentSize().width);
}
} TreeBranchDirection TreeNode::getHasBranch()
{
return enumBranch;
}
四、大树TreeModel
大树是树节点的集合,由一个一个的节点依次排列组成。最基本的功能如下
TreeNode* getTreeHeadNode();获得头节点
TreeNode* deleteTreeHeadNode();删除头节点
void initTree();初始化
TreeBranchDirection getFirstBranch();获得头节点的树枝方向
void onReset();重置整个树
Vector<TreeNode*> treeQueue;树节点列表
Vector<TreeNode*> treeCache;树节点缓存列表
优化:这个游戏一直在变化的是树节点,如果不停的删除和new节点 将会使程序不健康!为此除了要有树列表treeQueue外要有一个缓存队列treeCache,缓存队列的工作就是避免重复的new节点,同时回收砍掉的节点等待下次使用。
当然,作为大树的类是整个游戏的重点逻辑:生成什么样节点?
1、通过玩法得知必须在不同方向的树枝之间存在一个没有树枝的节点,使hero能生存。
2、如果前一个是有树枝的,那么以什么概率来产生下一个节点是否要有树枝(有树枝必须是同方向的 or 无树枝),使hero生存。
3、如果前一个树节点是无树枝的,那么再向前一个的树节点是否有树枝?根据难度来调节是否要产生树枝,增加难度。
围绕着这三个问题要有一个得到树枝的逻辑函数TreeModel::getBranch()
TreeBranchDirection TreeModel::getBranch()
{
auto isBranch = CCRANDOM_0_1()* < ;
if( treeQueue.size() == )
return DEFINE;
if( !isBranch ) return DEFINE;
auto protree = treeQueue.at(treeQueue.size()-);
switch (protree->getHasBranch())
{
case LEFT:
return (CCRANDOM_0_1()* < ) ? DEFINE : LEFT;
break;
case RIGHT:
return (CCRANDOM_0_1()* < ) ? DEFINE : RIGHT;
break;
case DEFINE:
return getAgainBranch();
break;
default:
return DEFINE;
break;
}
} TreeBranchDirection TreeModel::getAgainBranch()
{
if( treeQueue.size() < )
return DEFINE;
auto protree = treeQueue.at(treeQueue.size()-);
switch (protree->getHasBranch())
{
case LEFT:
return (CCRANDOM_0_1()* < ) ? RIGHT : LEFT;
break;
case RIGHT:
return (CCRANDOM_0_1()* < ) ? LEFT : RIGHT;
break;
case DEFINE:
return (CCRANDOM_0_1()* < ) ? LEFT : RIGHT;
break;
default:
return DEFINE;
break;
}
}
这其中的 概率随机数是可以调整的,如果你想增加难度 那就调整吧!
五、时间线GameScore
游戏结束有两个点1、碰到树枝2、时间终止
时间进度我用的ProgressTimer 进度表示时间百分比。
我想到了两种逻辑:
1、speed 法, 通过分数来决定速度,分数越高时间越少,不断的砍树来维持时间平衡。
2、addProgress 增量法, 通过分数来决定砍树获得每次增加的量,分数越高增量越低,最后维持在一个平衡点,在这个平衡点上保持速度均衡。
我最后选得增量,这两种方法相对都很不错。
六、数据存储UserDefault
整个游戏不需要大量的存储数据,因为只是记录最高分数,在设置游戏结束分数的时候进行读写
void GameOver::setScore(int score)
{
int maxScore = score;
char string[] = {};
sprintf(string, "Score %d", score);
_newScore->setString(string); maxScore = UserDefault::getInstance()->getIntegerForKey("maxScore");
if( maxScore < score )
{
UserDefault::getInstance()->setIntegerForKey("maxScore",score);
}
newScore->setVisible(( maxScore < score ));
char str2[] = {};
sprintf(str2, "Max Score %d", ( maxScore < score ) ? score : maxScore);
_highestScore->setString(str2); UserDefault::getInstance()->flush();
}
七、主场景 HelloWorldScene
主场景控制游戏的开始与结束。逻辑判断并不多。
点击判断:
bool HelloWorld::onTouchBegans(Touch *touch, Event *event)
{
auto pos = touch->getLocation();
Size visibleSize = Director::getInstance()->getVisibleSize();
auto model = TreeModel::getInstance(); auto isRight = pos.x > visibleSize.width/;
timber->playAction(isRight ? RIGHT : LEFT);
if(isRight)
{
timber->setPosition(visibleSize.width/+timber->getContentSize().width/+,);
}
else
{
timber->setPosition(visibleSize.width/-timber->getContentSize().width/-,);
} if(getIsOver())
{
timber->setTimberDie();
gameOver();
return false;
} auto dic = visibleSize.width*;
auto time = 0.5;
auto tree = model->deleteTreeHeadNode();
if( isRight )
{
tree->runAction(Spawn::create(RotateBy::create(time,-),MoveBy::create(time,Vec2(-dic,)),nullptr));
}
else
{
tree->runAction(Spawn::create(RotateBy::create(time,),MoveBy::create(time,Vec2(dic,)),nullptr));
} _score++;
score->setScore(_score);
if(getIsOver())
{
timber->setTimberDie();
gameOver();
} return true;
}
是否游戏结束:
bool HelloWorld::getIsOver()
{
auto model = TreeModel::getInstance(); if(model->getFirstBranch() == timber->getTimberDir()) return true;
return false;
}
重置游戏,从新开始:
void HelloWorld::onRest()
{
_score = ;
TreeModel::getInstance()->onReset();
score->onReset();
timber->onReset();
list->setEnabled(true);
auto isBgShow = (CCRANDOM_0_1()* < );
bg1->setVisible(isBgShow);
bg2->setVisible(!isBgShow);
Size visibleSize = Director::getInstance()->getVisibleSize();
timber->setPosition(visibleSize.width/-timber->getContentSize().width/-,);
}
当然coco2dx的粒子系统也很不错 我加入了 雪花特效以及声音特效:
ParticleSystem* pl = ParticleSnow::create();
pl->setTexture(Director::getInstance()->getTextureCache()->addImage("particle.png"));
pl->setPosition(visibleSize.width/,visibleSize.height);
this->addChild(pl);
八、总结
这个游戏算是我做的比较全的demo了,加入了屏幕适配、桌面图片icon、声音、粒子、数据。虽然比较简单,但能学习、做好、完善其实还是比较不错的,因为工作比较忙所以抽空能敲一敲代码,不过总算没有半途而废。
TimberMan.apk
链接:http://pan.baidu.com/s/1o6A0Dce 密码:29mz
TimberMan代码
链接: http://pan.baidu.com/s/1pJynvdT 密码: bt1v
Cocos2d-x 3.2 学习笔记(十二)TimberMan!疯狂伐木工!的更多相关文章
- python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL
python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL实战例子:使用pyspider匹配输出带.html结尾的URL:@config(a ...
- Go语言学习笔记十二: 范围(Range)
Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...
- java jvm学习笔记十二(访问控制器的栈校验机制)
欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 本节源码:http://download.csdn.net/detail/yfqnihao/4863854 这一节,我们 ...
- (C/C++学习笔记) 十二. 指针
十二. 指针 ● 基本概念 位系统下为4字节(8位十六进制数),在64位系统下为8字节(16位十六进制数) 进制表示的, 内存地址不占用内存空间 指针本身是一种数据类型, 它可以指向int, char ...
- Python学习笔记(十二)—Python3中pip包管理工具的安装【转】
本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...
- ROS学习笔记十二:使用gazebo在ROS中仿真
想要在ROS系统中对我们的机器人进行仿真,需要使用gazebo. gazebo是一种适用于复杂室内多机器人和室外环境的仿真环境.它能够在三维环境中对多个机器人.传感器及物体进行仿真,产生实际传感器反馈 ...
- JavaScript权威设计--命名空间,函数,闭包(简要学习笔记十二)
1.作为命名空间的函数 有时候我们需要声明很多变量.这样的变量会污染全局变量并且可能与别人声明的变量产生冲突. 这时.解决办法是将代码放入一个函数中,然后调用这个函数.这样全局变量就变成了 局部变量. ...
- MySQL学习笔记十二:数据备份与恢复
数据备份 1.物理备份与逻辑备份 物理备份 物理备份就是将数据库的数据文件,配置文件,日志文件等复制一份到其他路径上,这种备份速度一般较快,因为只有I/O操作.进行物理备份时,一般都需要关闭mysql ...
- Java基础学习笔记十二 类、抽象类、接口作为方法参数和返回值以及常用API
不同修饰符使用细节 常用来修饰类.方法.变量的修饰符 public 权限修饰符,公共访问, 类,方法,成员变量 protected 权限修饰符,受保护访问, 方法,成员变量 默认什么也不写 也是一种权 ...
- Python学习笔记十二
HTML全称:Hyper Text Markup Language超文本标记语言 不是编程语言 HTML使用标记标签来描述网页 2. HTML标签 开始标签,结束标签. 例如:<html&g ...
随机推荐
- [Java基础]字符串
1.字符串特点 字符串是常量,创建之后不能修改: 字符串的内容一旦修改,就会马上创建一个新的对象: 字符串实际为一个char value[]={'a','a'};数组: 2.==与equal判断字符串 ...
- SAP HANA专题分析目录
针对HANA的关键技术领域, 做深度解析. 1. HANA开发规范 HANA 各种对象的应用解析.版本管理,开发规范. 2. HANA系统管理 用户.系统权限.数据权限的深度解析. HANA系统配置. ...
- PHP——使用header()函数下载文件
思路:先指明内容的MIME类型,内容描述,内容长度(也即文件大小). 一.下载txt文件的程序: <?phpheader('Content-Type:text/plain');header('C ...
- day10---异步I/O,gevent协程
协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来 ...
- java解析xml
一.Document对象相关 1.读取XML文件,获得document对象. SAXReader reader = new SAXReader(); ...
- iOS 直播类APP开发流程分解:
1 . 音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放显示1.数据采集:摄像机及拾音器收集视频及音频数据,此时得到的为原始数据涉及技术或协议:摄像机:CCD.C ...
- java笔试题(金山网络)
今天参加金山的校园招聘,java笔试,回来仔细研究实现一下: 题目1:工厂两条生产线,三个工人,生产线上可以最多存放m个产品,当生产线满时,机器停止生产,等产品线不满时才继续生产,每条产线上一次只能允 ...
- bitmap算法
概述 所谓bitmap就是用一个bit位来标记某个元素对应的value,而key即是这个元素.由于采用bit为单位来存储数据,因此在可以大大的节省存储空间 算法思想 32位机器上,一个整形,比如int ...
- Java枚举类型getClass和getDeclaringClass区别(未完待续)
Java中的枚举类型有getClass()和getDeclaringClass()两个方法,在通常情况下这两个方法返回的类型一样,在某些场景下会有不同的表现 参照 http://stackoverfl ...
- Linux time命令
说明:喜欢写小程序的人都特别注重自己程序的执行效率,那么在Linux上,就有一个time的命令,用于测量命令的运行时间,还可以测量内存.I/O等的使用情况. 一个程序在运行时使用的系统资源通常包括CP ...