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 ...
随机推荐
- 教你如何快速下载旧版本的Firefox浏览器
http://blog.csdn.net/gh0st007/article/details/18937421 更新后的ff使用过程中经常出现卡顿的现象,之前并没有出现,于是想找老版本的ff安装一下.发 ...
- 【转】【Linux】 临界区,互斥量,信号量,事件的区别
原文地址:http://blog.itpub.net/10697500/viewspace-612045/ Linux中 四种进程或线程同步互斥的控制方法1.临界区:通过对多线程的串行化来访问公共资源 ...
- MyEclipse使用技巧
1. 大小写切换: Ctrl + Shift + X 大写: Ctrl + Shift + Y 小写: 2. 自动导包: Ctrl + Shift + O 3. 运行前自动保存 ...
- C#操作内存读写方法的主要实现代码
C#操作内存读写方法是什么呢?让我们来看看具体的实例实现: using System.Runtime.InteropServices; using System.Text; publicclass F ...
- crtmpserver系列(二):搭建简易流媒体直播系统
crtmpserver简介 我们在第一章的时候已经简要说明了crtmpserver,crtmpserver是一个由C++语言编写的开源的RTMP流媒体服务器,与其对应的商业产品自然是Adobe公司的F ...
- centos 安装ffmpeg
wget http://www.ffmpeg.org/releases/ffmpeg-3.1.tar.gz tar -zxvf ffmpeg-3.1.tar.gz cd ffmpeg-3.1 ./co ...
- Xamarin.Android中使用ResideMenu实现侧滑菜单
上次使用Xamarin.Android实现了一个比较常用的功能PullToRefresh,详情见:Xamarin. Android实现下拉刷新功能 这次将实现另外一个手机App中比较常用的功能:侧滑菜 ...
- CoinPunk项目介绍
CoinPunk是一个bitcoin比特币钱夹服务web应用程序,你可以自己构建钱夹服务.开源,免费. 轻量级,高效 响应式设计 轻易创建新账户 详细的交易记录 构建于Node.js与H ...
- Java语法糖2:自动装箱和自动拆箱
前言 一开始想学学自动拆箱和自动装箱是被这个名字吸引到,听上去好像很高端的样子,其实自动拆箱.自动装箱是很简单的内容. 自动拆箱和自动装箱 Java为每种基本数据类型都提供了对应的包装器类型.举个例子 ...
- 源代码版本管理与项目管理软件的认识与github的注册
源代码版本管理软件: 主要有:svn,cvs,hg,git,VSS 这些工具主要是一种记录代码更改历史, 可以无限回溯, 用于代码管理,多个程序员开发协作的工具.Perforce,StarTeam)- ...