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 ...
随机推荐
- 后台设置gridview不换行
GridView1.Style.Add("word-break", "keep-all"); GridView1.Style.Add(&q ...
- [Java基础]java的main函数
1.main函数详解 main在编程中的形式:public static void main(String[] args) { - } public : 公共的. 权限是最大,在任何情况下都可以访问. ...
- 归并排序-java
排序-归并排序 基本思想:是指将两个或两个以上的有序表合并成一个新的有序表. 具体步骤: (1首先将整个表看成是n个有序子表,每个子表的长度为1. (2)然后两两归并,得到n/2个长度为2的有序子表. ...
- 关于网页pc端以及移动端的兼容性——测试
对于经常做网页设计的人员来说,网页的兼容性测试是不可缺少的,记得刚来单位的新手,都是要安装一款浏览器测试软件的,看自己制作的网页是否在各大浏览器中正常显示,有没有变形,或者网页效果不兼容等. 不仅仅是 ...
- shell脚本调试
转自:http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/ 一. 前言 shell编程在unix/linux世界中使用得非常广泛,熟 ...
- 利用HtmlAgilityPack库进行HTML数据抓取
主要介绍基于XPATH的文本分析方式的实现,代码如下: using System; using System.Collections.Generic; using System.Linq; using ...
- hander消息机制原理
基本原理 线程中调用Handler.sendMsg()方法(参数是Message对象),将需要Main线程处理的事件 添加到Main线程的MessageQueue中,Main线程通过MainLoope ...
- Blend 2015 教程 (一) 基础
微软公司在Visual Studio 2015产品套件中作出了许多革命性的变更,包括.NET开源,.NET服务器端部分跨平台,推出向个人和小团队免费的社区版,移动应用开发部分跨平台支持,商店应用支持C ...
- Cloneable接口和Object的clone()方法
为什么要克隆 为什么要使用克隆,这其实反映的是一个很现实的问题,假如我们有一个对象: public class SimpleObject implements Cloneable { private ...
- 人人都是 DBA(I)SQL Server 体系结构
在了解 SQL Server 数据库时,可以先从数据库的体系结构来观察.SQL Server 的体系结构中包含 4 个主要组成部分: 协议层(Protocols) 关系引擎(Relational En ...