好帖,浅显易懂,给的链接也有很大的阅读价值。
第六节 实现蛇的运动
我们往GameLayer.h中添加
成员函数:
void MoveStep();
void MoveBody();
virtua void update(float dt);
我们先写MoveBody这个函数。
MoveBody这个函数的作用是移动蛇所有的身体节点。
他的原理就是将每个身体节点的位置设为前一个身体节点的位置,其中如果是第一个身体节点的话,就用蛇头的位置来设置身体节点的位置。
代码如下
void GameLayer::MoveBody(){
int n=body.size();
Point HeadPosition = head->getPosition();
Point a,temp;
for (int i = 0; i < n; i++) {
if(i==0){
a = body.at(i)->getPosition();
body.at(i)->setPosition(HeadPosition);
}
else {
temp = a;
a = body.at(i)->getPosition();
body.at(i)->setPosition(temp);
}
}
}
```
接着,实现MoveStep函数
代码如下
void GameLayer::MoveStep(){
movedflag = true;
Direction temp = head->getDirec();
Point po = head->Node::getPosition();
switch (temp) {
case up:
//log("upupup");
po.y += 20;
break;
case down:
po.y -= 20;
break;
case left:
po.x -= 20;
break;
case right:
po.x += 20;
break;
default:
break;
}
MoveBody();
head->setPosition(po);
}
```
函数中,我们先获得蛇头运动方向,然后根据蛇头运动的方向来设置蛇头接下来的位置。
接着将MoveStep加入到update函数中,并在init函数中添加下面这行代码
```
this->schedule(schedule_selector(GameLayer::update), 0.6);
```
好了,我们现在已经实现蛇的运动啦。
第六节结束
第七节 放置食物及吃掉食物的判定
实现蛇的运动后我们就开始放置食物了,首先在GameLayer.h中添加
成员
Sprite* food;
Point lastbodyposi;
函数
void SetFood();
Point RandomPosition()
bool ifCollideBody(Point pos);
bool ifGetFood();
void AddBody();
首先是SetFood函数
代码如下
void GameLayer::SetFood(){
this->removeChild(food);
Point foodposi = RandomPosition();
Point headposi = head->getPosition();
while (foodposi==headposi||ifCollideBody(foodposi)) {
foodposi = RandomPosition();
}
food = Sprite::create("Food.png");
food->setPosition(foodposi);
this->addChild(food);
}
```
里面有用到RandomPosition和ifCollideBody两个函数,
先说RandomPosition,代码如下
Point GameLayer::RandomPosition(){
int x = (arc4random()%24);
int y = (arc4random()%16);
Point position = Point(x*20+10,y*20+10);
return position;
}
```
这个函数返回的是一个Point类型的随机点
再是ifCollideBody函数,代码如下
bool GameLayer::ifCollideBody(Point pos){
bool value = false;
Snake* node;
for (int i =0; igetPosition();
if(nodepos==pos){
value = true;
}
}
return value;
}
```
这个函数的作用主要是检查给定的位置是否与蛇的身体节点重合,重合返回true,不重合返回false。
回到SetFood这个函数,因为整个游戏画面上同时只能存在一个食物,因此,当每执行SetFood的时候,要把前面设置的食物从layer中移除,所以要执行一下removeChildh函数。
接下来先获取蛇头的位置,然后产生一个随机点,检查该随机点是否与蛇头和蛇身体重合,如果重合,则继续生成随机点,直到生成的随机点与蛇头蛇身不重合为止。然后设置食物的位置为该随机点,设置食物的步骤就结束了。
接下来我们判断食物是否被吃掉,用到的是ifGetFood这个函数
代码如下
bool GameLayer::ifGetFood(){
bool value = false;
if(food->getPosition() == head->getPosition()){
value = true;
}
return value;
}
```
非常简单吧
如果食物被吃掉,则调用AddBody函数代码如下
void GameLayer::AddBody(){
head->setPosition(food->getPosition());
Snake* node = Snake::create();
node->setNode(Sprite::create("Snake.png"));
node->setPosition(lastbodyposi);
body.pushBack(node);
this->addChild(node);
}
```
我们要在蛇的末尾加入一个身体节点,就要用到lastbodyposi这个成员了,这个成员保存的是上一步运动过程中,蛇的最后一个身体节点的位置。
这个lastbodyposi是在update函数中更新的,我们先来看一下update函数
void GameLayer::update(float dt){
MoveStep();
if(ifGetFood()){
AddBody();
SetFood();
}
lastbodyposi = body.at(body.size()-1)->getPosition();
}
```
第七节结束
第八节 游戏结束的判定
贪食蛇里GameOver的原因只有2种,1是蛇头碰到身体,2是蛇头碰到墙壁
我们在GameLayer.h里添加
成员
bool ifgameover;
函数
bool OutofRange();
bool HeadCollideBody(Direction headdirec);
void GameOver();
void PauseGame();
void SetSnakeVisible(bool value);
先说OutofRange函数,代码如下
bool GameLayer::OutofRange(){
Point po = head->getPosition();
if (po.x < 0||po.x > visiblesize.width||po.y < 0||po.y > visiblesize.height) {
return true;
}
return false;
}
```
这个函数用来判断蛇头是否超出游戏区域。
接下来是HeadCollideBody,代码如下
bool GameLayer::HeadCollideBody(Direction headdirec){
float x = head->getPosition().x;
float y = head->getPosition().y;
bool iscollide = false;
switch (headdirec) {
case up:
y += 20;
break;
case down:
y -= 20;
break;
case left:
x -= 20;
break;
case right:
x += 20;
break;
default:
break;
}
Point headnextpos = Point (x,y);
iscollide = ifCollideBody(headnextpos);
return iscollide;
}
```
这个函数是用来判断蛇头下一步移动的位置是否与身体相撞。
再下来是GameOver函数,代码如下
void GameLayer::GameOver(){
PauseGame();
SetSnakeVisible(false);
playbutton->setPosition(Point(visiblesize.width/2,visiblesize.height/2-gameover->getContentSize().height));
gameover->setVisible(true);
ifgameover = true;
}
```
里面又用到了另外两个函数,PauseGame和SetSnakeVisible。
PauseGame函数的作用主要是用来停止游戏,代码如下
void GameLayer::PauseGame(){
this->pause();
SetSnakeVisible(false);
playbutton->setVisible(true);
}
```
SetSnakeVisible就是用来显示或隐藏蛇和食物的,代码如下
void GameLayer::SetSnakeVisible(bool val){
food->setVisible(val);
head->getNode()->setVisible(val);
for (int i =0; isetVisible(val);
}
}
```
回到GameOver函数,为了美观,我们需要调整一下开始按钮的位置,因为我们还要在开始按钮上面显示“GameOver!”的label,接着将ifgameover设置为true。
最后将update函数改为下面这个样子
void GameLayer::update(float dt){
if (!HeadCollideBody(head->getDirec()) && !OutofRange()) {
MoveStep();
if(ifGetFood()){
AddBody();
SetFood();
}
lastbodyposi = body.at(body.size()-1)->getPosition();
}
else{
GameOver();
}
}
```
第八节结束
第九节 游戏的暂停和恢复
我们往GameLayer.h里添加
函数
void StartGame();
StartGame主要是用来恢复游戏的,是playbutton的回调函数,先看代码
void GameLayer::StartGame(){
if(!ifgameover){
SetSnakeVisible(true);
this->resume();
playbutton->setVisible(false);
}else{
this->unscheduleAllSelectors();
this->removeAllChildren();
body.clear();
this->init();
}
}
```
游戏过程中,在两种情况下我们会去点击开始游戏按钮
第一种是人为的暂停,恢复游戏时就要恢复游戏暂停前的状态
第二种是GameOver了,我们需要重新开始游戏。
这里我们用到了上一节的ifgameover来判断具体是哪种情况。
注意,在init函数中我们要将ifgameover设置为false。
bool GameLayer::init(){
bool bRect = false;
do {
ifgameover = false;
SetApperance();
SetSnake();
SetFood();
this->schedule(schedule_selector(GameLayer::update), 0.6);
bRect = true;
} while (0);
return bRect;
}
```
第九节 结束
最终章 一个小bug的解决
在玩的过程中发现了一个小bug,如果方向切换的过快,就会造成HeadCollideBody的误判
解决方法如下
在GameLayer.h中添加一个成员
bool movedflag;
然后修改 SetDirection函数
void GameLayer::SetDirection(Ref* psender,Direction direc){
Direction headdir = head->getDirec();
if (movedflag && direc!= headdir) {
switch (direc) {
case up:
if(headdir==left||headdir==right){
head->setDirec(up);
}
break;
case down:
if(headdir==left||headdir==right){
head->setDirec(down);
}
break;
case left:
if(headdir==up||headdir==down){
head->setDirec(left);
}
break;
case right:
if(headdir==up||headdir==down){
head->setDirec(right);
}
break;
default:
break;
}
movedflag = false;
}else{
}
}
```
然后在MoveStep函数的第一行添加一行代码
```
movedflag = true;
```
解决!
哇你这个好都告诉写在哪个文件了,对我这种菜鸟来说太好了,不过这是ios的么?适用于android吗
楼主,我只能点32个赞!
太棒了!这个修改一下直接就可以商业化了。:801::801::801:
适用的哦,就是导出app的时候方式不一样
还想了好几个点子呢,有一个就是蛇的节点是有磁铁组成的分 N级和S级,食物产生的时候也有N级和S级之分,具体怎么用这个还没想好
嘛,这只是个开始啦~
您的文章已被推荐到CocoaChina首页热门文章精选,感谢您的分享。
大赞,学习,学习!!
真详细啊,吓得我都趴地上了
学习了。。
感谢!非常详细!非常适合新手!
楼主能发个项目压缩包给我吗,你这个文件我打不开。476855582@qq.com 多谢给赞
新手学习下
新手用2.21,还是好多错误,完全改不来,难道真是智商捉急。
新手学习.+32.
楼主,我运行前两节,里面声明的变量,都报错“undeclared identifier”,崩溃中
