cocos贪吃蛇 - CSDN
精华内容
参与话题
  • cocos2d-x 3.2 |如何实现一个贪吃蛇游戏
    cocos2d-x 3.2 |如何实现一个贪吃蛇游戏
    前情提要:今天我们来实现一个简单版的贪吃蛇游戏,利用这些经典的游戏上手cocos2d最好不过了。
    忽略:本章忽略菜单、按钮、跳转、帮助等功能、主要实现贪吃蛇逻辑与效果

    第一步:新建Game类,定义贪吃蛇主要功能与方法 Game.h如下:
    #include <stdio.h>
    #include "cocos2d.h"
    USING_NS_CC;
    using namespace ui;
    //枚举定义蛇运动方向
    enum class SNAKE_DIR
    {
        DIR_UP,
        DIR_LEFT,
        DIR_RIGHT,
        DIR_DOWN
    };
    class SNode:public Node
    {
    public:
        LayerColor * sprite;//纹理
        int px,py;//坐标
        SNAKE_DIR dir;//方向
        CREATE_FUNC(SNode);
        bool init();//初始化
        void setColor(Color3B color);//颜色
        void moveTo(int x,int y);//位移到
    };
    
    class GameScene:public Layer
    {
    public:
        SNode * snakeHead;//蛇头
        SNode *food;//食物
        Vector<SNode *> allBody;//身体
        int mapx;
        int mapy;
        
        CREATE_FUNC(GameScene);
        bool init();
        static Scene * createScene();
        void gameLogc(float t);//逻辑
        virtual bool onTouchBegan(Touch *touch, Event *unused_event);//触摸
    };
    第二步:实现Game类的方法 Game.cpp如下:
    #include "Game.h"
    
    Scene * GameScene::createScene(){
        auto scene=Scene::create();
        auto layer=GameScene::create();
        scene->addChild(layer);
        return scene;
    }
    bool GameScene::init(){
        if(!Layer::init())
        {
            return false;
        }
        //绘制游戏背景
        auto bk=LayerColor::create(Color4B(149, 149, 149, 180));
        this->addChild(bk);
    
        
        //绘制游戏地图
        auto map=DrawNode::create();
        map->setTag(100);
        for (int i=0; i<=20; i++)
        {
            //绘制十1条横线
            map->drawSegment(Vec2(0,i*30),Vec2(600,i*30),0.5,Color4F::GRAY);
            map->drawSegment(Vec2(i*30,0),Vec2(i*30,600),0.5,Color4F::GRAY);
        }
        //map->setAnchorPoint(Vec2(0.5,0.5));
        map->setPosition(Vec2(280,20));
    
        this->addChild(map);
        //绘制蛇头
        this->snakeHead=SNode::create();
        this->snakeHead->setColor(Color3B::MAGENTA);
        map->addChild(snakeHead);
        int row=0;
        int col=0;
        row=rand()%10;
        col=rand()%10;
        this->snakeHead->moveTo(col*30, row*30);
        snakeHead->dir=SNAKE_DIR::DIR_DOWN;
        //绘制食物
        this->food=SNode::create();
        this->food->setColor(Color3B::YELLOW);
        map->addChild(food);
        mapx=map->getPositionX();
        mapy=map->getPositionY();
        row=rand()%20;
        col=rand()%20;
        this->food->moveTo(col*30, row*30);
        //让蛇头运动
        this->schedule(schedule_selector(GameScene::gameLogc), 0.2);
        //改变舌头方向
        auto listener=EventListenerTouchOneByOne::create();
        listener->onTouchBegan=CC_CALLBACK_2(GameScene::onTouchBegan, this);
        Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,this);
        return true;
    }
    
    //根据按下位置,判断舌头移动方向
    bool  GameScene::onTouchBegan(Touch *touch, Event *unused_event){
        int w=abs(touch->getLocation().x-mapx-snakeHead->px);
        int h=abs(touch->getLocation().y-mapy-snakeHead->py);
        if(w>h)//左右移动
        {
            if(touch->getLocation().x-mapx>snakeHead->px)//右
            {
                snakeHead->dir=SNAKE_DIR::DIR_RIGHT;
            }else
            {
                snakeHead->dir=SNAKE_DIR::DIR_LEFT;
            }
        }else
        {
            if(touch->getLocation().y-mapy>snakeHead->py)//上
            {
                snakeHead->dir=SNAKE_DIR::DIR_UP;
            }else
            {
                snakeHead->dir=SNAKE_DIR::DIR_DOWN;
            }
        }
        return true;
    }
    //根据舌头移动方向,位移距离
    void GameScene::gameLogc(float t){
        //移动蛇头
        switch (snakeHead->dir) {
            case SNAKE_DIR::DIR_DOWN:
                snakeHead->moveTo(snakeHead->px, snakeHead->py-30);
                break;
            case SNAKE_DIR::DIR_UP:
                snakeHead->moveTo(snakeHead->px, snakeHead->py+30);
                break;
            case SNAKE_DIR::DIR_LEFT:
                snakeHead->moveTo(snakeHead->px-30, snakeHead->py);
                break;
            case SNAKE_DIR::DIR_RIGHT:
                snakeHead->moveTo(snakeHead->px+30, snakeHead->py);
                break;
        }
        //移动身体
        for(int i=allBody.size()-1;i>=0;i--)
        {
            SNode * nowbody=allBody.at(i);
            switch (nowbody->dir) {
                case SNAKE_DIR::DIR_DOWN:
                    nowbody->moveTo(nowbody->px, nowbody->py-30);
                    break;
                case SNAKE_DIR::DIR_UP:
                    nowbody->moveTo(nowbody->px, nowbody->py+30);
                    break;
                case SNAKE_DIR::DIR_LEFT:
                    nowbody->moveTo(nowbody->px-30, nowbody->py);
                    break;
                case SNAKE_DIR::DIR_RIGHT:
                    nowbody->moveTo(nowbody->px+30, nowbody->py);
                    break;
            }
            
            if (i==0) {
                allBody.at(0)->dir=snakeHead->dir;
            }else
            {
                nowbody->dir=allBody.at(i-1)->dir;//等于上一个方向
            }
        }
        
        //碰撞判断
        if (snakeHead->px==food->px&&snakeHead->py==food->py)
        {
            int row=rand()%20;
            int col=rand()%20;
            this->food->moveTo(col*30, row*30);
            SNode * newBody=SNode::create();
            newBody->setColor(Color3B::BLUE);
            //新的身体在蛇的尾部
            if (allBody.size()==0)
            {
                switch (snakeHead->dir)
                {
                    case SNAKE_DIR::DIR_DOWN:
                        newBody->moveTo(snakeHead->px, snakeHead->py+30);
                        break;
                    case SNAKE_DIR::DIR_UP:
                        newBody->moveTo(snakeHead->px, snakeHead->py-30);
                        break;
                    case SNAKE_DIR::DIR_LEFT:
                        newBody->moveTo(snakeHead->px+30, snakeHead->py);
                        break;
                    case SNAKE_DIR::DIR_RIGHT:
                        newBody->moveTo(snakeHead->px-30, snakeHead->py);
                        break;
                }
                allBody.pushBack(newBody);
                newBody->dir=snakeHead->dir;
            }
            else
            {
                SNode * lastBody=allBody.at(allBody.size()-1);
                switch (lastBody->dir) {
                    case SNAKE_DIR::DIR_DOWN:
                        newBody->moveTo(lastBody->px, lastBody->py+30);
                        break;
                    case SNAKE_DIR::DIR_UP:
                        newBody->moveTo(lastBody->px, lastBody->py-30);
                        break;
                    case SNAKE_DIR::DIR_LEFT:
                        newBody->moveTo(lastBody->px+30, lastBody->py);
                        break;
                    case SNAKE_DIR::DIR_RIGHT:
                        newBody->moveTo(lastBody->px-30, lastBody->py);
                        break;
                }
                allBody.pushBack(newBody);
                newBody->dir=lastBody->dir;
            }
            this->getChildByTag(100)->addChild(newBody);
        }
    }
    bool SNode::init(){
        if(!Node::init())
        {
            return false;
        }
        this->sprite=LayerColor::create(Color4B::RED);
        this->sprite->setContentSize(Size(30,30));
        this->addChild(sprite);
        return true;
    }
    void SNode::setColor(Color3B color){
        this->sprite->setColor(color);
    }
    void SNode::moveTo(int x,int y){
        this->px=x;
        this->py=y;
        this->sprite->setPosition(Vec2(x,y));
    }

    总结:文中利用DrawNode绘制方格背景,利用schedule移动蛇头。



    展开全文
  • cocos creator 单机贪吃蛇之战

    千次阅读 2019-05-07 20:13:53
    cocos creator 单机贪吃蛇之战可以自行搭建项目。 演示地址:https://www.bilibili.com/video/av47915520/ 联系方式:QQ 1019429950

     cocos creator 单机贪吃蛇之战可以自行搭建项目。

    演示地址:https://www.bilibili.com/video/av47915520/

    联系方式:QQ 1019429950

    展开全文
  • cocos-creator贪吃蛇

    2018-08-07 15:39:03
    主要用cocos-creator进行开发的小游戏,希望对刚接触小游戏的朋友,有些帮助。
  • cocos creator 项目源码--类贪吃蛇贪吃蛇大作战这种,源码仅供参考
  • cocos2d-x lua 贪吃蛇完整项目

    万次阅读 2016-07-06 21:53:36
    cocos2d-x lua 贪吃蛇游戏 一步一步开发学习 本文地址: http://blog.csdn.net/qq_26437925/article/details/51842647开发环境,项目编译运行===windows 直接下载windows版本最新的,网址如下 ...

    cocos2d-x lua 贪吃蛇游戏 一步一步开发学习
    本文地址: http://blog.csdn.net/qq_26437925/article/details/51842647

    源代码:
    https://github.com/doctording/cocos2dx_lua_snake

    学习视频见慕课网

    开发环境,项目编译运行

    ===

    windows 直接下载windows版本最新的,网址如下(官网可能变化)
    http://www.cocos.com/download/cocos2d-lua/
    或者下载网盘里的:http://pan.baidu.com/s/1c2fwhKc

    解压到某个目录下就行了,setup.py设置环境
    这里写图片描述

    切换到如下的目录,既可以创建lua项目
    这里写图片描述

    shift +鼠标右键,在该目录下打开cmd,输入

    cocos.py new Snake -l lua -d d:\

    可以在D:\下建立一个Snake 项目,这些都是可选的,自己随便玩

    接着用vs2012 或者vs2013 打开如下的解决方案,并完成编译
    这里写图片描述

    最后可以运行出结果,可在如下的目录中运行,点击exe即可
    这里写图片描述

    这里写图片描述

    有模拟器 和 日志两个窗口,在实际编程中需要多打日志 和 查看日志 分析问题,并解决。


    Hello World

    ===

    类似cocos2d-x win32 C++ 功能
    程序从 E:\workspace\cocos_lua\Snake\runtime\win32\src\下的main.lua开发,包括了各种配置等(如config.lua的一些屏幕显示配置)

    接着 src/app 目录下有个 MyApp.lua,加载了 src/app/scenes/下的MainScene

    而Mainscene就是继承了Scene的一个场景了,可以看到有HelloWorld

    参考 cocos2d-x lua tolua++ 面向对象
    http://blog.csdn.net/qq_26437925/article/details/51842400

    因为继承了场景类,所以需要重写一些方法

    void onExitTransitionDidStart();//2,创建完layer的时候调用,也就是1调用完之后调用  
    void onEnter();//1,创建时调用  
    void onExit();//3,退出当前layer的时候调用,在这里都是做一些清除工作  
    void onEnterTransitionDidFinish();//在3完成之后,调用该成员方法  

    所以我们需要在 onEnter(); 中写各种东西,这样就可以显示Sprite, Button,Label等等了。


    坐标转换

    ===

    显示蛇,首先需要重定义坐标,认识屏幕的坐标系统

    这里写图片描述

    cGridSize 大小依据蛇身体图片的大小而定,自定义一个转换函数,方便用来设置Sprite的位置

    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end

    蛇身

    ===

    有了上面的基础,可以先显示蛇身了

    src/app 目录下定义一个Body类,表示蛇身 , 有坐标,是否头部,其父节点等信息,当然完全看自己如何弄

    local Body = class("Body")
    
    -- node为cocos2dx-父节点
    function Body:ctor(snake , x, y, node, isHead)
    
        self.snake = snake
        self.X = x
        self.Y = y
    
        if isHead then -- 根据是否是头部,用不同的图片创建 
            self.sp = cc.Sprite:create("head.png")
        else
            self.sp = cc.Sprite:create("body.png")
        end
    
        node:addChild(self.sp) -- 添加到父节点
    
        self:Update()
    
    end
    
    -- 更新自己的位置
    function Body:Update()
        local posx,posy = Grid2Pos(self.X , self.Y)
        self.sp:setPosition(posx,posy)
    end
    
    return Body

    MainScene.lua中

    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end
    
    -- require相应的类
    local Body = require("app.Body") 
    
    local MainScene = class("MainScene", function()
        return display.newScene("MainScene")
    end)
    
    function MainScene:onEnter() -- MainScene 加载执行
        -- 测试body
        self.body1 = Body.new(nil,0,0,self,true)
    
        self.body2 = Body.new(nil,2,2,self,false)
    end
    
    return MainScene

    显示如下,很预想的一样,就证明没有什么错误了

    这里写图片描述


    构造一条蛇

    ===

    有了蛇身体,构造一条蛇就容易了
    编写一个蛇Snake类,利用Body类

    Snake.lua

    
    local Snake = class("Snake")
    
    local Body = require("app.Body")
    
    local cInitLen = 3 -- 蛇初始长度
    
    -- 构造函数
    function Snake:ctor(node)
    
        self.BodyArray = {} -- Body对象数组
        self.node = node
        self.MoveDir = "left" -- 蛇的初始移动方向
    
        for i = 1,cInitLen do
            self:Grow(i == 1)
        end
    
    end
    
    
    --取出蛇尾
    function Snake:GetTailGrid()
        if #self.BodyArray == 0 then -- 设置蛇头的位置为(0,0)
            return 0,0
        end
    
        local tail = self.BodyArray[#self.BodyArray]
    
        return tail.X,tail.Y
    
    end
    
    -- 蛇变长
    function Snake:Grow(isHead)
    
        local tailX,tailY = self:GetTailGrid()
        local body = Body.new(self,tailX,tailY,self.node,isHead)
    
        table.insert(self.BodyArray,body)
    
    end
    
    -- 根据方向改变坐标
    local function OffsetGridByDir(x,y,dir)
        if dir == "left" then
            return x - 1, y
        elseif dir == "right" then
            return x + 1, y
        elseif dir == "up" then
            return x, y + 1
        elseif dir == "down" then
            return x, y - 1
        end
    
        print("Unkown dir", dir)
        return x, y
    end
    
    
    -- 根据蛇的移动方向 更新蛇,就是BodyArray一个一个往前移动
    function Snake:Update()
    
        if #self.BodyArray == 0 then
            return
        end
    
        for i = #self.BodyArray , 1 , -1 do
    
            local body = self.BodyArray[i]
    
            if i == 1 then -- 蛇头位置 与 方向,得到一个新的位置 存放蛇头
                body.X, body.Y = OffsetGridByDir(body.X, body.Y, self.MoveDir)
            else
                local front = self.BodyArray[i-1]
                body.X, body.Y = front.X, front.Y
            end
    
            body:Update()
    
        end
    
    end
    
    
    -- 取出蛇头
    function Snake:GetHeadGrid()
        if #self.BodyArray == 0 then
            return nil
        end
    
        local head = self.BodyArray[1]
    
        return head.X, head.Y
    
    end
    
    -- 设置方向
    function Snake:setDir(dir)
            self.MoveDir = dir
    end
    
    return Snake

    响应的MainScene.lua中也要调整下

    
    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end
    
    -- require相应的类
    -- local Body = require("app.Body")
    local Snake = require("app.Snake")
    
    local MainScene = class("MainScene", function()
        return display.newScene("MainScene")
    end)
    
    function MainScene:onEnter() -- MainScene 加载执行
            self.snake = Snake.new(self) -- 创建一条蛇
    end
    
    return MainScene

    最后显示如下,似乎看不到一条蛇,因为它们都重复了,我们需要是的蛇能够动起来

    这里写图片描述


    小蛇动起来

    ===

    只需要设个定时器,刷新屏幕就行了,所以在上面的基础上,只需要加个定时器,更新小蛇就行了
    修改MainScene.lua

    
    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end
    
    -- require相应的类
    -- local Body = require("app.Body")
    local Snake = require("app.Snake")
    
    local MainScene = class("MainScene", function()
        return display.newScene("MainScene")
    end)
    
    local cMoveSpeed = 0.3
    
    function MainScene:onEnter() -- MainScene 加载执行
            self.snake = Snake.new(self) -- 创建一条蛇
    
            local tick = function()
                self.snake:Update() -- 更新蛇
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end
    
    return MainScene

    定时器,就一句话
    schedulerID = cc.Director:getInstance():getScheduler():scheduleScriptFunc(调用的function, 定时时间(秒), 是否暂停(true, false))

    我们在更新函数中写了self.snake:Update()

    function Snake:Update()
    
        if #self.BodyArray == 0 then
            return
        end
    
        for i = #self.BodyArray , 1 , -1 do
    
            local body = self.BodyArray[i]
    
            if i == 1 then -- 蛇头位置 与 方向,得到一个新的位置 存放蛇头
                body.X, body.Y = OffsetGridByDir(body.X, body.Y, self.MoveDir)
            else
                local front = self.BodyArray[i-1]
                body.X, body.Y = front.X, front.Y
            end
    
            body:Update()
    
        end
    
    end

    逻辑: 根据一个移动方向,得到新的头部位置,然后从尾部到头部,每一个节点都更新成上一个节点,因为Snake中有个BodyArray ={} ,各个body都用这个table存储着

    显示如下:

    这里写图片描述


    控制小蛇

    ===

    我们希望蛇能够灵活点,我们用鼠标,键盘能够控制它(当然,智能机上没有按键的,我们在桌面开发),我们应该也知道各种事件,事件监听等概念。

    鼠标点击控制

    MainScene中添加成员方法

    local function vector2Dir(x, y)
    
        if math.abs(x) > math.abs(y) then
            if x < 0 then
                return "left"
            else
                return "right"
         end
    
        else
    
            if y > 0 then
                return "up"
            else
                return "down"
          end
    
        end
    
    end
    
    -- 鼠标点击事件处理
    function MainScene:ProcessInput()
    
        local function onTouchBegan(touch, event)
    
            local location = touch:getLocation() -- 得到触摸点坐标(cocos2d-x 坐标)
    
    
    -- 判断移动的方向
            local snakex , snakey = self.snake:GetHeadGrid()
            local snake_fx,snake_fy = Grid2Pos(snakex,snakey)
            local finalX = location.x - snake_fx
            local finalY = location.y - snake_fy
    
            local dir = vector2Dir(finalX, finalY)
            print("now dir",dir)
            self.snake:setDir(dir) -- 设置蛇的移动方向
    
        end
    
        local listener = cc.EventListenerTouchOneByOne:create()
        listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
        local eventDispatcher = self:getEventDispatcher()
        eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)
    
    end

    定义一个事件监听,并注册,分发事件等
    onTouchBegan
    onTouchMoved
    onTouchEnded;
    这三个是要重写的,跟cocos2d-x C++, Android中的事件处理都差不多的。

    那么我们重写了onTouchBegan() ,里面的逻辑就是 根据 鼠标点击点蛇头 相对位置,判断蛇该往哪个方向,更新蛇的移动方向就行了

    别忘了MainScene:onEnter() 调用此方法

    function MainScene:onEnter() -- MainScene 加载执行
            self:ProcessInput() -- 鼠标touch事件
    
            self.snake = Snake.new(self) -- 创建一条蛇
    
            local tick = function()
                self.snake:Update() -- 更新蛇
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    运行截图
    这里写图片描述

    键盘控制小蛇

    有了鼠标控制,自然键盘控制也不应该成为一个问题

    同样的MainScene中 添加一个事件处理 成员函数

    -- 按键事件处理
    function MainScene:ProcessKeyInput()
    
        local function keyboardPressed(keyCode,event)
    
            -- up
            if keyCode == 28 then
                    print("up")
                    self.snake:setDir("up") -- 设置蛇的移动方向
            -- down
            elseif keyCode == 29 then
                    print("down")
                    self.snake:setDir("down") -- 设置蛇的移动方向
            --left
            elseif keyCode == 26 then
                    print("left")
                    self.snake:setDir("left") -- 设置蛇的移动方向
            --right
            elseif keyCode == 27 then
                    print("right")
                    self.snake:setDir("right") -- 设置蛇的移动方向
            end
    
        end
    
        local listener = cc.EventListenerKeyboard:create()
        listener:registerScriptHandler(keyboardPressed, cc.Handler.EVENT_KEYBOARD_PRESSED)
        local eventDispatcher = self:getEventDispatcher()
        eventDispatcher:addEventListenerWithSceneGraphPriority(listener,self)
    
    end

    MainScene:onEnter() 调用此方法

    function MainScene:onEnter() -- MainScene 加载执行
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self.snake = Snake.new(self) -- 创建一条蛇
    
            local tick = function()
                self.snake:Update() -- 更新蛇
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    就这样就行了

    这里写图片描述

    按下方向键盘,试一下,可以多打打日志,调一调,应该没问题。


    围墙 和 死亡

    ===

    上例中,我们看到蛇是可以跑出屏幕的,我们需要给蛇弄个围墙,只能在围墙内任意走动,撞到了墙 就死了

    添加围墙

    编写src/app/Fence.lua

    • 需要一个矩形围墙

    • 能够给出一个撞墙的判断函数

    
    local Snake = require("app.Snake")
    local Fence = class("Fence")
    
    
    local function fenceGenerator(node, bound, callback)
    
        for i = -bound, bound do
            local sp = cc.Sprite:create("fence.png")
            local posx,posy = callback(i)
            sp:setPosition(posx,posy)
            node:addChild(sp)
        end
    end
    
    function Fence:ctor(rowBound, colBound, node)
    
        self.rowBound = rowBound -- 屏幕中心往上或下 有 几个格子
        self.colBound = colBound -- 屏幕中心往左或右 有 几个格子
    
        -- up
        fenceGenerator(node, colBound,function(i)
            return Grid2Pos(i, rowBound)
        end)
    
            -- down
        fenceGenerator(node, colBound,function(i)
            return Grid2Pos(i, -rowBound)
        end)
    
            -- left
        fenceGenerator(node, rowBound,function(i)
            return Grid2Pos(-colBound, i)
        end)
    
            -- right
        fenceGenerator(node, rowBound,function(i)
            return Grid2Pos(colBound, i)
        end)
    
    end
    
    -- 判断是否与围墙相撞
    function Fence:CheckCollide(x,y)
        return x == self.colBound or
                     x == -self.colBound or
                     y == self.rowBound or
                     y == -self.rowBound
    end
    
    return Fence
    

    程序需要传递 行,列(其实只是最终围墙的一半),父节点(MainScene控制的,就是MainScene类了)

    接着 MainScene.lua 添加显示
    在原来的基础上添加,部分代码如下

    local Fence = require("app.Fence")
    
    local MainScene = class("MainScene", function()
        return display.newScene("MainScene")
    end)
    
    local cMoveSpeed = 0.3
    local rowBound = 5
    local colBound = 8
    
    function MainScene:onEnter() -- MainScene 加载执行
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self.snake = Snake.new(self) -- 创建一条蛇
            self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
    
            local tick = function()
                self.snake:Update() -- 更新蛇
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    require, 所需变量的定义, onEnter()方法定义出fence, 这几点注意就行了

    运行图如下
    这里写图片描述

    蛇与围墙相撞

    MainScene.lua的onter方法中加几句话就行了

    local Snake = require("app.Snake")
    local Fence = require("app.Fence")
    
    local MainScene = class("MainScene", function()
        return display.newScene("MainScene")
    end)
    
    local cMoveSpeed = 0.3
    local rowBound = 5
    local colBound = 8
    
    function MainScene:onEnter() -- MainScene 加载执行
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self.snake = Snake.new(self) -- 创建一条蛇
            self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
    
            local tick = function()
                self.snake:Update() -- 更新蛇
    
                local headX , headY = self.snake:GetHeadGrid()
                if self.fence:CheckCollide(headX,headY) then -- 蛇与围墙相撞
                    print("collide fence")
                end
    
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    加了下面的几句

    local headX , headY = self.snake:GetHeadGrid()
                if self.fence:CheckCollide(headX,headY) then -- 蛇与围墙相撞
                    print("collide fence")
                end

    因为原来我们都写好了很多方法,比如我们是可以得到蛇的头部坐标的,有了坐标我们当然是可以判断是否撞墙的,这里我加了日志
    运行如下:

    这里写图片描述

    看到日志了,表明成功了,不过蛇并没有死亡,仍然跑出了边界,因为我们什么都没有做。

    死亡动画

    死亡的处理会比较复杂,我们要处理
    * 死亡后,直接重新开始就行了,可以来个死亡动画后再开始游戏
    * 死亡后,界面仍在刷新,我们需要设置一个程序运行状态
    * 死亡后,我们希望,蛇能够重新生成,开始(因为我们以后的蛇,如果边长了,我们重来,蛇将依然是初始长度)

    Fence.lua

    
    local Fence = class("Fence")
    
    
    function Fence:fenceGenerator(node, bound, callback)
    
        for i = -bound, bound do
            local sp = cc.Sprite:create("fence.png")
            local posx,posy = callback(i)
            sp:setPosition(posx,posy)
            node:addChild(sp)
    
            table.insert(self.fenceSpArray,sp)
        end
    end
    
    function Fence:ctor(rowBound, colBound, node)
    
        self.rowBound = rowBound -- 屏幕中心往上或下 有 几个格子
        self.colBound = colBound -- 屏幕中心往左或右 有 几个格子
        self.fenceSpArray = {}
        self.node = node
    
        -- up
        self:fenceGenerator(node, colBound,function(i)
            return Grid2Pos(i, rowBound)
        end)
    
        -- down
        self:fenceGenerator(node, colBound,function(i)
            return Grid2Pos(i, -rowBound)
        end)
    
        -- left
        self:fenceGenerator(node, rowBound,function(i)
            return Grid2Pos(-colBound, i)
        end)
    
        -- right
        self:fenceGenerator(node, rowBound,function(i)
            return Grid2Pos(colBound, i)
        end)
    
    end
    
    -- 判断是否与围墙相撞
    function Fence:CheckCollide(x,y)
        return x == self.colBound or
                     x == -self.colBound or
                     y == self.rowBound or
                     y == -self.rowBound
    end
    
    function Fence:Reset()
    
        for _,sp in ipairs(self.fenceSpArray) do
            self.node:removeChild(sp)
        end
    
    end
    
    return Fence
    

    改动:

    • 类似 Snake一样 , 用了table 存储了围墙Sprite

    • 添加了一个Rest() 成员方法,用来销毁Fence,注意要保存父节点node

    Snake.lua 添加两个成员方法

    -- 死亡之后的闪烁效果
    function Snake:Blink(callback)
    
        for index,body in ipairs (self.BodyArray) do
            local blink = cc.Blink:create(3,5)
    
            if index == 1 then -- 蛇头
                local a = cc.Sequence:create(blink, cc.CallFunc:create(callback))
                body.sp:runAction(a)
            else
                body.sp:runAction(blink) -- 蛇身
            end
    
        end -- for
    
    end
    
    -- 死亡销毁
    function Snake:Kill()
        for _,body in ipairs(self.BodyArray) do
            self.node:removeChild(body.sp)
        end
    end

    其中 Blink 函数中用到了cc.Blink 动画,让蛇闪烁

    MainScene.lua

    直接贴上到目前为止MainScene修改后的整个代码

    
    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end
    
    -- require相应的类
    -- local Body = require("app.Body")
    local Snake = require("app.Snake")
    local Fence = require("app.Fence")
    
    local MainScene = class("MainScene", function()
        return display.newScene("MainScene")
    end)
    
    local cMoveSpeed = 0.3
    local rowBound = 5
    local colBound = 8
    
    function MainScene:onEnter() -- MainScene 加载执行
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self:Reset()
    
            --self.snake = Snake.new(self) -- 创建一条蛇
            --self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
    
            local tick = function()
                if self.stage == "running" then
    
                    self.snake:Update() -- 更新蛇
    
                    local headX , headY = self.snake:GetHeadGrid()
                    if self.fence:CheckCollide(headX,headY) then -- 蛇与围墙相撞
                        self.stage = "dead"
                        self.snake:Blink(function()
    
                                    self:Reset()
    
    
                                    end)
                    end
                end
    
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end
    
    
    
    local function vector2Dir(x, y)
    
        if math.abs(x) > math.abs(y) then
            if x < 0 then
                return "left"
            else
                return "right"
         end
    
        else
    
            if y > 0 then
                return "up"
            else
                return "down"
          end
    
        end
    
    end
    
    -- 鼠标点击事件处理
    function MainScene:ProcessInput()
    
        local function onTouchBegan(touch, event)
    
            local location = touch:getLocation() -- 得到触摸点坐标(cocos2d-x 坐标)
    
    
    -- 判断移动的方向
            local snakex , snakey = self.snake:GetHeadGrid()
            local snake_fx,snake_fy = Grid2Pos(snakex,snakey)
            local finalX = location.x - snake_fx
            local finalY = location.y - snake_fy
    
            local dir = vector2Dir(finalX, finalY)
            print("now dir",dir)
            self.snake:setDir(dir) -- 设置蛇的移动方向
    
        end
    
        local listener = cc.EventListenerTouchOneByOne:create()
        listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
        local eventDispatcher = self:getEventDispatcher()
        eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)
    
    end
    
    
    -- 按键事件处理
    function MainScene:ProcessKeyInput()
    
        local function keyboardPressed(keyCode,event)
    
            -- up
            if keyCode == 28 then
                    print("up")
                    self.snake:setDir("up") -- 设置蛇的移动方向
            -- down
            elseif keyCode == 29 then
                    print("down")
                    self.snake:setDir("down") -- 设置蛇的移动方向
            --left
            elseif keyCode == 26 then
                    print("left")
                    self.snake:setDir("left") -- 设置蛇的移动方向
            --right
            elseif keyCode == 27 then
                    print("right")
                    self.snake:setDir("right") -- 设置蛇的移动方向
            end
    
        end
    
        local listener = cc.EventListenerKeyboard:create()
        listener:registerScriptHandler(keyboardPressed, cc.Handler.EVENT_KEYBOARD_PRESSED)
        local eventDispatcher = self:getEventDispatcher()
        eventDispatcher:addEventListenerWithSceneGraphPriority(listener,self)
    
    end
    
    
    -- 游戏结束操作
    function MainScene:Reset()
        if self.snake ~= nil then
            self.snake:Kill()
        end
    
        if self.fence ~= nil then
            self.fence:Reset()
        end
    
        self.snake = Snake.new(self) -- 创建一条蛇
        self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
        self.stage = "running"
    
    end
    
    return MainScene
    

    改动,

    我们定义了Reset()成员方法, 并且给程序运行时加了state状态

    在onEnter()中, 直接调用Reset()方法,就可以重新创建 蛇和围墙了

    tick() 函数中调用了 snake的死亡闪烁动画,并且设置了state = “dead”

    MainScene始终控制着整个程序的逻辑

    看下运行截图,小蛇会死,然后重新开始
    这里写图片描述

    围墙内随机产生苹果

    ===

    跟Fence, Snake差不多,编写AppleFactory.lua

    local AppleFactory = class("AppleFactory")
    
    function AppleFactory:ctor(rowBound, colBound, node)
    
        self.rowBound = rowBound
        self.colBound = colBound
        self.node = node
    
        math.randomseed(os.time())
    
        self:Generate()
    
    end
    
    
    local function getRandomPos(rowBound,colBound)
        local randomX = math.random(-colBound, colBound)
        local randomY = math.random(-rowBound, rowBound)
        return randomX, randomY
    end
    
    
    function AppleFactory:Generate()
    
        if self.appleSprite ~= nil then
            self.node:removeChild(self.appleSprite) -- 销毁对象
        end
    
    
        local sp = cc.Sprite:create("apple.png")
    
        local x, y = getRandomPos(self.rowBound - 1, self.colBound-1)
    
        local finalX, finalY = Grid2Pos(x,y)
        sp:setPosition(finalX, finalY)
    
        self.node:addChild(sp)
    
        self.appleX = x
        self.appleY = y
    
        self.appleSprite = sp
    
    end
    
    
    function AppleFactory:CheckCollide(x,y)
    
        if x == self.appleX and y == self.appleY then
            return true
        end
    
        return false
    end
    
    function AppleFactory:Reset()
    
        self.node:removeChild(self.appleSprite)
    
    end
    
    return AppleFactory
    

    围墙内 产生两个随机点用来显示苹果

    在上一步的MainScene.lua 的 Reset()成员方法 加上几句话就行了

    -- 游戏结束操作
    function MainScene:Reset()
        if self.apple ~= nil then
            self.apple:Reset()
        end
    
        if self.fence ~= nil then
            self.fence:Reset()
        end
    
        if self.snake ~= nil then
            self.snake:Kill()
        end
    
        self.snake = Snake.new(self) -- 创建一条蛇
        self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
        self.apple = AppleFactory.new(rowBound, colBound, self)  -- 创建apple
        self.stage = "running"
    
    end

    注意需要

    local AppleFactory = require("app.AppleFactory")

    显示如下
    这里写图片描述


    吃苹果

    ===

    在上面的基础上,有了苹果,当蛇头和苹果相撞时,表示蛇吃苹果,那么蛇需要变长,并且需要重新产生苹果

    修改MainScene.lua中的onEnter()方法, 添加吃苹果逻辑

    function MainScene:onEnter() -- MainScene 加载执行
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self:Reset()
    
            --self.snake = Snake.new(self) -- 创建一条蛇
            --self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
    
            local tick = function()
                if self.stage == "running" then
    
                    self.snake:Update() -- 更新蛇
    
                    local headX , headY = self.snake:GetHeadGrid()
                    if self.fence:CheckCollide(headX,headY) then -- 蛇与围墙相撞
                        self.stage = "dead"
                        self.snake:Blink(function()
    
                                    self:Reset()
    
    
                                    end)
                    elseif self.apple:CheckCollide(headX,headY) then
                            self.apple:Generate() -- 苹果重新产生
    
                            self.snake:Grow() -- 蛇变长
                    end
                end
    
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    分数版

    ===

    MainScene.lua中添加显示分数版的成员方法

    -- 创建显示分数
    function MainScene:CreateScoreBoard()
    
        display.newSprite("applesign.png")
        :pos(display.right - 200 , display.cy + 150)
        :addTo(self)
    
        local ttfConfig = {}
        ttfConfig.fontFilePath = "arial.ttf"
        ttfConfig.fontSize = 30
    
        local score = cc.Label:createWithTTF(ttfConfig, "0")
        self:addChild(score)
    
        score:setPosition(display.right - 200 , display.cy + 80)
    
        self.scoreLabel = score
    
    end
    
    -- 设置分数
    function MainScene:SetScore(s)
        self.scoreLabel:setString(string.format("%d",s))
    end

    修改MainScene.lua中的 onEnter()和Reset()方法

    function MainScene:Reset()
        if self.apple ~= nil then
            self.apple:Reset()
        end
    
        if self.fence ~= nil then
            self.fence:Reset()
        end
    
        if self.snake ~= nil then
            self.snake:Kill()
        end
    
        self.snake = Snake.new(self) -- 创建一条蛇
        self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
        self.apple = AppleFactory.new(rowBound, colBound, self)  -- 创建apple
    
        self.stage = "running"
        self.score = 0
        self:SetScore(self.score) -- 分数设置
    end
    function MainScene:onEnter() -- MainScene 加载执行
    
            self:CreateScoreBoard() -- 分数版创建
    
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self:Reset()
    
            --self.snake = Snake.new(self) -- 创建一条蛇
            --self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
    
            local tick = function()
                if self.stage == "running" then
    
                    self.snake:Update() -- 更新蛇
    
                    local headX , headY = self.snake:GetHeadGrid()
                    if self.fence:CheckCollide(headX,headY) then -- 蛇与围墙相撞
                        self.stage = "dead"
                        self.snake:Blink(function()
    
                                    self:Reset()
    
    
                                    end)
                    elseif self.apple:CheckCollide(headX,headY) then
                            self.apple:Generate() -- 苹果重新产生
    
                            self.snake:Grow() -- 蛇变长
    
                            self.score = self.score + 1 -- 分数加1
                            self:SetScore(self.score) --设置分数
                    end
                end
    
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    运行如下图
    这里写图片描述
    这里写图片描述


    到目前为止,游戏基本能玩了,不过会发现问题有很多,目前版本的源码下载地址如下
    http://download.csdn.net/detail/qq_26437925/9569936
    参考学习网址:慕课网 Cocos2d-x游戏开发之贪吃蛇
    我上面的程序,在老师讲的基础上做了修改。只要自己思考,并且不断的敲出来的,才是自己的。


    TODO solve bugs

    修复蛇移动bug

    ==

    如果设原来往左移动,那么设置方向的时候肯定不能往右移动,同理上下,所以是个互斥的

    只要修改下Snake.lua 的设置方向的成员方法,改成如下

    -- 设置方向
    function Snake:setDir(dir)
        local  hvTable = {
            ["left"] = "h",
            ["right"] = "h",
            ["up"] = "v",
            ["down"] = "v",
        }
        -- 水平 ,垂直的互斥
        if hvTable[dir] == hvTable[self.MoveDir] then
            return
        else
            self.MoveDir = dir
        end
    
    end

    修复蛇移动 图片显示的bug

    蛇头有个方向,所以移动的设置,需要改变项蛇头方向,可以旋转精灵,当然可以制作出不同的图片,用来设置
    也是只需改变 Snake.lua中的setDir()方法

    -- 设置方向
    function Snake:setDir(dir)
        local  hvTable = {
            ["left"] = "h",
            ["right"] = "h",
            ["up"] = "v",
            ["down"] = "v",
        }
    
        if hvTable[dir] == hvTable[self.MoveDir] then
            return
        else
            self.MoveDir = dir
            -- 取出蛇头
            local head = self.BodyArray[1]
    
            -- 顺时针旋转,初始方向为left
            local rotTable ={
                ["left"] = 0,
                ["up"] = 90,
                ["right"] = 180,
                ["down"] = -90,
            }
            -- 让精灵图片旋转,以改变显示
            head.sp:setRotation(rotTable[self.MoveDir])
        end
    
    end

    往下移动后显示如下,蛇头往下
    这里写图片描述


    蛇吃到自己bug

    蛇迟到自己就死亡了,就是判断蛇头是否与蛇身体有碰撞
    在Snake.lua中 添加一个成员方法即可

    function Snake:CheckCollideSelf()
        if #self.BodyArray < 2 then
            return false
        end
    
        local headX, headY = self.BodyArray[1].X , self.BodyArray[1].Y
    
        for i = 2, #self.BodyArray do
            local body = self.BodyArray[i]
    
            if body.X == headX and body.Y == headY then
                return true;
            end
        end
    
        return false
    end

    然后在 MainScene.lua 逻辑中添加吃掉自己的逻辑,修改onEnter()
    先在变成撞墙和自杀 两种死亡模式

    
    function MainScene:onEnter() -- MainScene 加载执行
    
            self:CreateScoreBoard() -- 分数版
    
            self:ProcessInput() -- 鼠标touch事件
            self:ProcessKeyInput() -- 键盘控制
    
            self:Reset()
    
            --self.snake = Snake.new(self) -- 创建一条蛇
            --self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
    
            local tick = function()
                if self.stage == "running" then
    
                    self.snake:Update() -- 更新蛇
    
                    local headX , headY = self.snake:GetHeadGrid()
                    if self.fence:CheckCollide(headX,headY)
                    or self.snake:CheckCollideSelf()
                    then -- 蛇与围墙相撞 或 自杀
                        self.stage = "dead"
                        self.snake:Blink(function()
    
                                    self:Reset()
    
    
                                    end)
                    elseif self.apple:CheckCollide(headX,headY) then
                            self.apple:Generate() -- 苹果重新产生
    
                            self.snake:Grow() -- 蛇变长
    
                            self.score = self.score + 1 -- 分数加1
                            self:SetScore(self.score)
                    end
                end
    
            end -- end tick
    
            cc.Director:getInstance():getScheduler():scheduleScriptFunc(
                 tick,cMoveSpeed,false)
    
    end

    如下如,吃到自己
    这里写图片描述

    ====

    让程序暂停,恢复

    参看API文档
    http://api.cocos.com/cn/d7/df3/classcocos2d_1_1_director.html

    本次暂停,直接调用了Director的pause(),resume()方法
    所以在MainScene的键盘事件处理中,用P键 和 R键 来控制正在运行游戏的暂停和恢复

    -- 按键事件处理
    function MainScene:ProcessKeyInput()
    
        local function keyboardPressed(keyCode,event)
    
            -- up or W
            if keyCode == 28 or keyCode == 146 then
                    print("up")
                    self.snake:setDir("up") -- 设置蛇的移动方向
            -- down or S
            elseif keyCode == 29 or keyCode == 142 then
                    print("down")
                    self.snake:setDir("down") -- 设置蛇的移动方向
            --left or A
            elseif keyCode == 26 or keyCode == 124 then
                    print("left")
                    self.snake:setDir("left") -- 设置蛇的移动方向
            --right or D
            elseif keyCode == 27 or keyCode == 127 then
                    print("right")
                    self.snake:setDir("right") -- 设置蛇的移动方向
            --end
    
            -- P
            elseif keyCode == 139 then
                print("P -- Pause")
                local director = cc.Director:getInstance()
                director:pause() -- 暂停
    
            --end
    
            -- R
            elseif keyCode == 141 then
                local director = cc.Director:getInstance()
                director:resume() -- 恢复
    
            end
        end
    
        local listener = cc.EventListenerKeyboard:create()
        listener:registerScriptHandler(keyboardPressed, cc.Handler.EVENT_KEYBOARD_PRESSED)
        local eventDispatcher = self:getEventDispatcher()
        eventDispatcher:addEventListenerWithSceneGraphPriority(listener,self)
    
    end

    添加障碍物

    采用跟添加苹果类似的简单程序,因为是可以添加很多障碍物的,所以写好障碍物Block类后,需要写个BlockFactory类,产生多个Block。

    在原来的基础上src/app下添加Block.lua,BlockFactory.lua,这里有些和别的文件重复的代码(需要优化,目前先不管)

    Block.lua

    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end
    
    local cMoveSpeed = 0.3
    local rowBound = 5
    local colBound = 8
    
    -------------------------------------------
    
    local Block = class("Block")
    
    function Block:ctor(node)
        self.node = node
    end
    
    -- 设置当前的障碍物 index = 1,2,3 ..依据自己的障碍物图片数目
    function Block:Set(index)
    
        if self.sp ~= nil then
            self.node:removeChild(self.sp)
        end 
    
        self.index = index 
        self.sp = display.newSprite(string.format("block%d.png",index)) --转换图片地址
        self.node:addChild(self.sp)
    
    end
    
    -- 设置位置
    function Block:SetPos(x, y)
        local rbound = rowBound -1 
        local cbound = colBound -1 
    
        local posx , posy = Grid2Pos(x, y)  
        self.sp:setPosition(posx, posy)
    
        self.x = x
        self.y = y
    
    end
    
    -- 清除对象
    function Block:Clear()
        self.node:removeChild(self.sp)
    end
    
    function Block:CheckCollide(x,y)
    
        if x == self.x and y == self.y then
            return true
        end
    
        return false
    end
    
    return Block

    BlockFactory.lua

    
    local BlockFactory = class("BlockFactory")
    
    local Block = require("app.Block")
    
    
    function BlockFactory:ctor(node)
        self.BlockArray = {} --障碍物集合
        self.node = node
    
    end
    
    -- 在x,y位置添加一个序号为index的障碍物
    function BlockFactory:Add(x, y, index)
        local b = Block.new(self.node)
    
        b:Set(index)
        b:SetPos(x, y)
        table.insert(self.BlockArray,b)
    
    end
    
    function BlockFactory:Remove(x,y)
        local b,index = self:Hit(x,y)
    
        if b ~= nil then
            b:Clear()
            table.remove(self.BlockArray, index)
        end
    
    end
    
    function BlockFactory:Hit(x,y)
        for index, b in ipairs(self.BlockArray) do
            if b:CheckCollide(x, y) then
                return b, index
            end
        end
    
        return nil,-1
    end
    
    function BlockFactory:Reset()
        for _,b in ipairs(self.BlockArray) do
            b:Clear()
        end
    end 
    
    return BlockFactory

    同样的可以在MainScene.lua中验证测试,require(“…”)不要忘记
    MainScene:Reset()中,添加障碍物

    -- 游戏结束操作
    function MainScene:Reset()
        if self.apple ~= nil then
            self.apple:Reset()
        end
    
        if self.fence ~= nil then
            self.fence:Reset()
        end
    
        if self.snake ~= nil then
            self.snake:Kill()
        end
        -- 添加障碍物
        if self.blocks ~= nil then
            self.blocks:Reset()
        end
        self.blocks = BlockFactory.new(self)
        self.blocks:Add(1,1,1)
        self.blocks:Add(-3,1,2)
    
        self.snake = Snake.new(self) -- 创建一条蛇
        self.fence = Fence.new(rowBound, colBound, self)  -- 创建围墙
        self.apple = AppleFactory.new(rowBound, colBound, self)  -- 创建apple
    
        self.stage = "running"
        self.score = 0
        self:SetScore(self.score)
    end

    还要在collide中可以自己要检测碰撞

    截个图
    这里写图片描述


    场景编辑器,文件保存

    场景编辑器的书写,可以利用光标移动,按键等来做设置。仿照MainScene.lua 写
    文件可以直接存储为.lua文件, 然后利用require dofile等加载出来

    EditorScene.lua

    local cGridSize = 33
    local scaleRate = 1 / display.contentScaleFactor
    
    -- 根据自定义的坐标得到实际应该显示的cocos2d-x坐标位置
    function Grid2Pos(x,y)
    
        local visibleSize = cc.Director:getInstance():getVisibleSize() -- 获取整个手机可视屏幕尺寸
    
        local origin = cc.Director:getInstance():getVisibleOrigin() -- 获取手机可视屏原点的坐标,屏幕的左上角
    
        local finalX = origin.x + visibleSize.width / 2 + x * cGridSize * scaleRate
        local finalY = origin.y + visibleSize.height / 2 + y * cGridSize * scaleRate
    
        return finalX,finalY
    
    end
    
    local cMoveSpeed = 0.3
    local rowBound = 5
    local colBound = 8
    
    -------------------------------------------
    
    
    local Fence = require("app.Fence")
    local Block = require("app.Block")
    local BlockFactory = require("app.BlockFactory")
    
    local EditorScene = class("EditorScene", function()
        return display.newScene("EditorScene")
    end)
    
    local cMaxBlock = 3 -- 障碍物数目 
    
    function EditorScene:onEnter()
        self.fence = Fence.new(rowBound, colBound,self) --围墙
    
        -- 鼠标光标位置 初始化为左上角
        self.curX = 0
        self.curY = 0
        self.curIndex = 0 -- 选择的物体序号
    
        self:SwitchCursor(1)
        self:ProcessInput()
        self.blockFactory = BlockFactory.new(self)
    
    end
    
    
    -- 按键事件处理
    function EditorScene:ProcessInput()
    
        local function keyboardPressed(keyCode,event)
    
            -- up
            if keyCode == 28 then
                self:MoveCursor(0,1)
            -- down
            elseif keyCode == 29 then
                self:MoveCursor(0,-1)
            --left
            elseif keyCode == 26 then
                self:MoveCursor(-1,0)
            --right
            elseif keyCode == 27 then
                self:MoveCursor(1,0)
            -- pageUp
            elseif keyCode == 38 then
                self:SwitchCursor(-1)
            -- pageDown
            elseif keyCode == 44 then
                self:SwitchCursor(1)
    
    
            -- enter
            elseif keyCode == 35 then
                self:Place()
    
            -- delete
            elseif keyCode == 23 then
                self:Delete()
    
            -- F3 加载文件
            elseif keyCode == 49 then 
                self:Load()
            -- F4 保存为文件
            elseif keyCode == 50 then
                self:Save()
    
            end
    
        end
    
        local listener = cc.EventListenerKeyboard:create()
        listener:registerScriptHandler(keyboardPressed, cc.Handler.EVENT_KEYBOARD_PRESSED)
        local eventDispatcher = self:getEventDispatcher()
        eventDispatcher:addEventListenerWithSceneGraphPriority(listener,self)
    
    end
    
    -- 上下左右 移动光标 
    function EditorScene:MoveCursor(deltaX,deltaY)
        self.cur:SetPos(self.curX + deltaX, self.curY+deltaY)
        self.curX = self.cur.x
        self.curY = self.cur.y
    
    end
    
    -- 可以切换障碍物 delta=1 为下一个物体 ,-1 为上一个障碍物
    function EditorScene:SwitchCursor(delta)
        if self.cur == nil then
            self.cur = Block.new(self)
        end
    
        local newIndex = self.curIndex + delta
        newIndex = math.max(newIndex, 1)
        newIndex = math.min(newIndex, cMaxBlock)
    
        self.curIndex = newIndex
    
        self.cur:Set(newIndex)
        self.cur:SetPos(self.curX, self.curY)
    
    end
    
    -- 放置一个物体
    function EditorScene:Place()
        if self.blockFactory:Hit(self.cur.x,self.cur.y) then
            return
        end
        self.blockFactory:Add(self.curX, self.curY, self.cur.index)
    end
    
    
    -- 删除物体
    function EditorScene:Delete()
        self.blockFactory:Remove(self.cur.x, self.cur.y)
    end
    
    
    -- lua文件存盘 F4
    function EditorScene:Save()
    
        local f = assert(io.open("scene.lua", "w"))
    
        f:write("return {\n")
        self.blockFactory:Save(f)
        f:write("}\n")
    
        f:close()
    
        print("saved")
    
    end
    
    -- F3
    function EditorScene:Load() 
        local f = assert(dofile("scene.lua"))
    
        self.blockFactory:Reset()
    
        for _,t in ipairs(f) do
            self.blockFactory:Add(t.x, t.y, t.index)
        end
    
        print("Loaded")
    end
    
    return EditorScene

    BlockFactory.lua 来设置写入文件的数据格式

    -- 写入文件
    function BlockFactory:Save(f)
    
        for _,b in ipairs(self.BlockArray) do
            f:write(string.format("{x=%d,y=%d,index=%d},\n",b.x,b.y,b.index ))
        end
    end

    这里写图片描述

    保存的scene.lua文件内容

    return {
    {x=-4,y=1,index=1},
    {x=-4,y=-1,index=1},
    {x=1,y=1,index=2},
    {x=2,y=-2,index=3},
    }

    测试 场景

    上一步中写好场景后,可以在MainScene中加载,这样就能利用自己的场景了

    写个加载文件的方法,其实就是让blocks添加障碍物

    -- 加载文件
    function MainScene:Load()
        local f = assert(dofile("scene.lua"))
    
        self.blocks:Reset()
    
        for _,t in ipairs(f) do
            self.blocks:Add(t.x, t.y, t.index)
        end
    
        print("main Loaded")
    end

    MainScene.lua中的reset方法对blocks的写法改一下

    -- 添加障碍物
        if self.blocks ~= nil then
            self.blocks:Reset()
        end
        self.blocks = BlockFactory.new(self)
        self:Load() -- 这利用文件加载

    TODO

    展开全文
  • Cocos2d-JS实现的贪吃蛇

    千次阅读 2016-09-05 16:12:16
    一、前言相信贪吃蛇大家都玩儿过,我对贪吃蛇的印象就是在电子词典上,一只像素蛇在屏幕游走,饥渴难耐,看着豆子就要去吃,吃到豆子就会长一节,当蛇的身体越来越长的时候,它才发现这个世界变了,每走一步,都是...

    一、前言

    相信贪吃蛇大家都玩儿过,我对贪吃蛇的印象就是在电子词典上,一只像素蛇在屏幕游走,饥渴难耐,看着豆子就要去吃,吃到豆子就会长一节,当蛇的身体越来越长的时候,它才发现这个世界变了,每走一步,都是寸步难行。当它的蛇头触碰到任意的物体,无论是屏幕边界还是自己的身体,游戏都将结束。这款游戏应该是比较经典的一个童年记忆。刚接触游戏开发的人可能比较喜欢以这款游戏入手,因为贪吃蛇包含了很多游戏开发中的原理,并且难度也不大,而我刚好在学习Cocos CVP的课程,学习在一个中间阶段,我也来拿这个练练手,以下就把我做贪吃蛇的过程分享出来。

    二、游戏分析

    1.身体关节

    贪吃蛇的实现可能有多种方法,但今天,我想用面向对象的思想来对游戏进行设计,到今天,任何的程序开发都离不开面向对象的思想,通过面向对象的思想我们能把很多抽象的问题具象化,方便我们解决很多问题。而在贪吃蛇中,面向对象的思想依然实用。
    在贪吃蛇中,我们可以把一条游走的蛇的每个关节当做是一个对象,而蛇本身是由多个关节组成的整体,当每个关节在移动时,我们就能看到整个蛇的移动,每个关节的位置以及移动方向都跟它的上一个关节息息相关,那么我们就可以把关节与它的上一个关节关联起来,实现如下结构:
    蛇关节关系图

    2.移动方向

    如上文所说,按照上面的关节关系来实现,那么蛇的移动方向就是与父节点的移动方向相关联,每一个关节应该有一个当前移动方向和下次移动方向,每一步的移动,都是跟着当前移动方向走的,而父关节的当前移动的方向即为子关节的下次移动方向,这样,只需要调整蛇头关节的下次移动方向,整条蛇就能顺着各自的父关节方向移动,蛇的移动方向图如下:
    蛇的移动方向图

    二、开发设计

    1.项目结构

    按照前面的设计,我们可以大致可以划分出游戏场景类和关节类,进入游戏场景类,再将一些全局的变量单独存在一个类中,项目结构可划分如下图:
    项目结构图

    2.全局变量类

    游戏中的全局变量,提炼为一个全局变量类,其中参数可以根据需求灵活变动配置

    var Constants = {
        frequency: 0.2,// 刷新频率
        speed: 31,// 每帧移动距离,身体节点大小+1像素间隔
        errDistance: 10,// 偏差举例
    }

    3.关节类

    按照以上设计的结构,每一个关节对象都应该包含蛇的当前方向、下次方向和蛇的父节点三个属性,代码如下:

    var SnakeBody = cc.Sprite.extend({
        frontBody: null,//上一个身体关节,没有则为头部
        nextDirection: 0,// 1-上,2-下,3-左,4-右
        direction: 0,// 1-上,2-下,3-左,4-右
        ctor: function (frontBody, direction) {
            this._super();
            this.frontBody = frontBody;
            this.direction = direction;
            this.nextDirection = direction;
            return true;
        }
    }

    上面说的,关节的位置跟它的福关节的位置是息息相关的,那么初始化的时候,我们就需要根据父关节的移动方向来进行次关节的位置设置,这部分代码,我们可以放在onEnter方法中,代码如下:

    onEnter: function () {
      this._super();
      if (this.frontBody == null) {
          // 蛇头部关节,设置头部纹理
          switch (this.direction) {
              case 1:
                  this.setTexture(res.head_up);
                  break;
              case 2:
                  this.setTexture(res.head_down);
                  break;
              case 3:
                  this.setTexture(res.head_left);
                  break;
              case 4:
                  this.setTexture(res.head_right);
                  break;
          }
      } else {
          // 蛇身体关节
          // 设置纹理
          this.setTexture(res.body);
          // 设置关节位置
          var frontX = this.frontBody.getPositionX();
          var frontY = this.frontBody.getPositionY();
          var frontWidth = this.frontBody.width;
          var frontHeight = this.frontBody.height;
          var width = this.width;
          var height = this.height;
          switch (this.frontBody.direction) {
              // 根据父关节的当前移动方向,决定此关节的位置
              case 1:// 上
                  this.setPosition(frontX, frontY - frontHeight / 2 - height / 2 - 1);
                  break;
              case 2:// 下
                  this.setPosition(frontX, frontY + frontHeight / 2 + height / 2 + 1);
                  break;
              case 3:// 左
                  this.setPosition(frontX + frontWidth / 2 + width / 2 + 1, frontY);
                  break;
              case 4:// 右
                  this.setPosition(frontX - frontWidth / 2 - width / 2 - 1, frontY);
                  break;
          }
      }
      return true;
    }

    有了这三个属性,每一个节点还应该有最重要的游戏逻辑——move方法,每一个关节分别调用move方法,从游戏场景中就能看到整条蛇按照预定方向进行移动,而整条蛇的运动方向就是跟着头部关节的方向走,头部关节的方向则通过点击屏幕区域控制。
    在move方法中,我们需要做以下事情:

    1.按照关节的下次移动方向移动本身长度的像素的距离
    2.如果是头部关节,需要改变关节纹理

    同时,如果是头部关节,我们还需要判断以下三个临界条件:

    1.头部关节是否触碰到屏幕边界
    2.头部关节是否吃到屏幕中的豆子
    3.头部关节是否触碰到自身关节

    其中1、3条件达成,则判定游戏结束,2条件达成,则能增加游戏分数,并且游戏继续。
    move方法代码如下:

    // 关节移动方法
    move: function (layer) {
        var star = layer.star;
        var direct;
        if (this.frontBody == null) {
            // 头部关节按照自身的下次方向行走
            direct = this.nextDirection;
        } else {
            // 身体关节按照父关节的当前方向行走,并将福关节的当前方向设置为自身的下次方向
            this.nextDirection = direct = this.frontBody.direction;
        }
        switch (direct) {
            case 1:// 上
                this.setPosition(this.getPositionX(), this.getPositionY() + Constants.speed);
                // this.runAction(cc.moveBy(Constants.frequency, cc.p(0, Constants.speed), 0))
                break;
            case 2:// 下
                this.setPosition(this.getPositionX(), this.getPositionY() - Constants.speed);
                // this.runAction(cc.moveBy(Constants.frequency, cc.p(0, -Constants.speed), 0))
                break;
            case 3:// 左
                this.setPosition(this.getPositionX() - Constants.speed, this.getPositionY());
                // this.runAction(cc.moveBy(Constants.frequency, cc.p(-Constants.speed, 0), 0))
                break;
            case 4:// 右
                this.setPosition(this.getPositionX() + Constants.speed, this.getPositionY());
                // this.runAction(cc.moveBy(Constants.frequency, cc.p(Constants.speed, 0), 0))
                break;
        }
        if (this.frontBody == null) {
            switch (this.nextDirection) {
                // 头部关节需要设置头部不同方向的纹理
                case 1:// 上
                    this.setTexture(res.head_up);
                    break;
                case 2:// 下
                    this.setTexture(res.head_down);
                    break;
                case 3:// 左
                    this.setTexture(res.head_left);
                    break;
                case 4:// 右
                    this.setTexture(res.head_right);
                    break;
            }
            // 头部关节判断是否触碰到边界
            var size = cc.winSize;
            if ((this.getPositionX() > size.width - this.width / 2)
                || (this.getPositionX() < this.width / 2)
                || (this.getPositionY() > size.height - this.height / 2)
                || (this.getPositionY() < this.height / 2)) {
                // 判断触碰边界
                cc.log("game over");
                return false;
            }
            // 判断是否触碰到自己身体关节
            for (var index in layer.bodys) {
                if (layer.bodys[index] != this && cc.rectIntersectsRect(this.getBoundingBox(), layer.bodys[index].getBoundingBox())) {
                    return false;
                }
            }
            // 判断是否吃到星星
            if (star != null) {
                if (cc.rectIntersectsRect(this.getBoundingBox(), star.getBoundingBox())) {
                    star.runAction(
                        cc.sequence(cc.spawn(
                            cc.scaleTo(0.2, 3),
                            cc.fadeOut(0.2)
                        ), cc.callFunc(function (star) {
                            star.removeFromParent();
                        }, star))
                    );
                    // 清除星星
                    layer.star = null;
                    // 添加身体
                    layer.canNewBody = 1;
                    // 改变分数
                    layer.score.setString("" + (Number(layer.score.getString()) + Math.round(Math.random() * 3 + 1)));
                    layer.score.runAction(cc.sequence(cc.scaleTo(0.1, 2), cc.scaleTo(0.1, 0.5), cc.scaleTo(0.1, 1)));
                }
            }
        }
        return true;
    }

    4.游戏场景类

    在游戏场景中我们需要以下几个变量:

    1. 贪吃蛇数组:用于存储贪吃蛇所有的关节节点
    2. 贪吃蛇尾部:每添加一个关节节点 ,都将此变量指向这个新加的节点,以便下次继续再尾部节点添加
    3. 吃的星星:屏幕中随机产生的星星,用于判断头部关节是否与它产生碰撞
    4. 是否添加节点:如果在定时任务中判断到吃到星星,那么可以次变量为1,代表可以添加一个节点
    5. 分数:存储游戏中累加的分数

    在cc.Layer的构造函数中对以上变量进行初始化,代码如下:

    var GameLayer = cc.Layer.extend({
      bodys: [],// snake body
      tail: null,// snake tail
      star: null,// star
      canNewBody: 0,// 0-无,1-有
      score: null,// 分数Label
      ctor: function () {
          // 初始化全局参数
          this._super();
          this.bodys = [];
          this.canNewBody = 0;
          this.star = null;
          this.tail = null;
          this.score = null;
          return true;
      }
    }

    之后,我们首先需要在场景中绘制出一条蛇,初始化定义为1个头部关节,5个身体关节,由于我们对关节类做了很好的封装,所以初始化一条蛇的代码很简单,我们在onEnter方法中进行初始化,如下所示:

    // 初始化一条蛇
    // 初始化头部
    var head = new SnakeBody(null, 4);
    head.setPosition(300, 300);
    this.addChild(head);
    this.bodys.push(head);
    head.setTag(1);
    this.tail = head;
    // 循环添加5个身体
    for (var i = 0; i < 5; i++) {
        var node = new SnakeBody(this.tail, this.tail.direction);
        this.addChild(node);
        this.bodys.push(node);
        this.tail = node;
    }

    初始化完了之后蛇是不会动的,如何让它动起来呢,我们就要用到在关节类中封装的move方法了,我们每隔一个时间,对所有的关节类执行一次move方法,就能实现蛇的移动,首先在onEnter中添加定时任务:

    // 蛇移动的定时任务
    this.schedule(this.snakeMove, Constants.frequency);

    在这个snakeMove定时调用的方法中,我们要写出所有关节移动的逻辑,在这个方法中,我们需要完成以下几件事:

    1. 遍历蛇的所有关节,每个关节执行一遍move方法,并在move完了之后,将下次移动方法变为本次移动方向
    2. 如果需要新增关节,在遍历完成之后,新增一个关节类,并将其父节点指向之前的蛇尾节点,并把蛇尾指向新加的这个关节

    代码如下:

    // 蛇关节移动方法
    snakeMove: function () {
        for (var index in this.bodys) {
            // 循环执行移动方法,并返回移动结果,false即视为游戏结束
            if (!this.bodys[index].move(this)) {
                // 执行移动方法,移动失败,游戏结束
                this.unschedule(this.snakeMove);
                this.unschedule(this.updateStar);
                var overScene = new OverScene(Number(this.score.getString()), false);
                cc.director.runScene(new cc.TransitionFade(1, overScene));
            }
        }
        for (var index in this.bodys) {
            // 本轮所有关节移动结束,所有节点的当前方向赋值为下一次的方向
            this.bodys[index].direction = this.bodys[index].nextDirection;
        }
        if (this.canNewBody == 1) {
            // 如果新增关节为1,增加关节
            var node = new SnakeBody(this.tail, this.tail.direction);
            this.addChild(node);
            this.bodys.push(node);
            this.tail = node;
            this.canNewBody = 0;
        }
    }

    目前为止这条蛇是只会按照我们初始化的方向一直走到碰壁,然后游戏结束的,如何改变蛇的运动轨迹呢?前面说到了,蛇头部节点的下次移动方向的改变,即可对整个蛇的移动轨迹进行改变,这里我们可以通过点击屏幕实现蛇头的下次移动方向的改变。
    首先在onEnter方法中添加触摸事件监听:

    // 添加屏幕触摸事件
    cc.eventManager.addListener({
        event: cc.EventListener.TOUCH_ONE_BY_ONE,
        swallowTouches: true,
        onTouchBegan: this.touchbegan,
        onTouchMoved: this.touchmoved,
        onTouchEnded: this.touchended
    }, this);

    然后在onTouchBegan方法中实现点击事件,我们可以允许点击有一个10像素的误差:

    // 点击转向
    touchbegan: function (touch, event) {
        var x = touch.getLocation().x;
        var y = touch.getLocation().y;
        var head = event.getCurrentTarget().getChildByTag(1);
        var headX = head.getPositionX();
        var headY = head.getPositionY();
        switch (head.direction) {
            case 1:// 上
            case 2:// 下
                if (x <= headX - Constants.errDistance) {// 转左
                    head.nextDirection = 3;
                } else if (x >= headX + Constants.errDistance) {// 转右
                    head.nextDirection = 4;
                }
                break;
            case 3:// 左
            case 4:// 右
                if (y <= headY - Constants.errDistance) {// 转下
                    head.nextDirection = 2;
                } else if (y >= headY + Constants.errDistance) {// 转上
                    head.nextDirection = 1;
                }
                break;
        }
        return true;
    }

    最后我们只差最后一步,就是蛇要吃的星星,我们可以在屏幕中任意位置随机产生一颗星星(又或者叫豆子,这都无所谓),只要这个星星满足以下条件,那么它就可以被绘制出来,否则我们需要重新随机这个星星的位置:

    1. 星星在游戏场景的屏幕范围内
    2. 星星不能与蛇的身体部分重叠

    代码如下:

    // 更新星星
    updateStar: function () {
        if (this.star == null) {
            this.star = new cc.Sprite(res.bean);
            var randomX = Math.random() * (cc.winSize.width - this.star.width) + this.star.width;
            var randomY = Math.random() * (cc.winSize.height - this.star.width) + this.star.height;
            this.star.setPosition(randomX, randomY);
            this.addChild(this.star);
            // 产生的星星只要在屏幕外,或与蛇的身体部分重叠,则本次任务不产生
            if ((randomX > cc.winSize.width - this.star.width / 2)
                || (randomX < this.star.width / 2)
                || (randomY > cc.winSize.height - this.star.height / 2)
                || (randomY < this.star.height / 2)) {
                cc.log("update star:out of screen");
                this.removeChild(this.star);
                this.star = null;
                return;
            }
            for (var index in this.bodys) {
                if (cc.rectIntersectsRect(this.bodys[index].getBoundingBox(), this.star.getBoundingBox())) {
                    cc.log("update star:intersect with self");
                    this.removeChild(this.star);
                    this.star = null;
                    return;
                }
            }
        }
    }

    至此,游戏的主要逻辑就大功告成了!贪吃蛇不仅能在屏幕中游走,还能吃星星,并且碰到自身或边缘都会GameOver!

    5.开始/结束场景类

    说到GameOver,那么就必须要有一个Over的场景类了,毕竟有了开始场景,游戏场景和结束场景,才算得上一个完成的游戏流程嘛,结束场景类的实现很简单,只需要把游戏场景中获得的分数传递进来,然后在Label中展示即可,代码如下:

    var OverLayer = cc.Layer.extend({
        sprite: null,
        score: 0,
        ctor: function (score) {
            this._super();
            this.score = score;
            return true;
        },
        onEnter: function () {
            this._super();
            var size = cc.winSize;
            var over = new cc.LabelTTF("Game Over,你的分数是:" + this.score, "Arial", 38);
            over.setPosition(size.width / 2, size.height / 2);
            this.addChild(over);
            over.runAction(cc.sequence(cc.scaleTo(0.2, 2), cc.scaleTo(0.2, 0.5), cc.scaleTo(0.2, 1)));
            var start = new cc.MenuItemFont("再来一次", function () {
                cc.director.runScene(new cc.TransitionFade(1, new HelloWorldScene()));
            }, this);
            start.setPosition(over.getPositionX(), over.getPositionY() - over.height / 2 - 50);
            var menu = new cc.Menu(start);
            this.addChild(menu);
            menu.setPosition(0, 0);
            return true;
        }
    });
    
    var OverScene = cc.Scene.extend({
        score: 0,
        ctor: function (score) {
            this._super();
            this.score = score;
            return true;
        },
        onEnter: function () {
            this._super();
            var layer = new OverLayer(this.score);
            this.addChild(layer);
        }
    });

    与结束场景一样,开始场景也只需一个Label一个Menu即可,代码如下:

    var HelloWorldLayer = cc.Layer.extend({
        sprite: null,
        ctor: function () {
            this._super();
            var size = cc.winSize;
            var helloLabel = new cc.LabelTTF("贪吃蛇", "Arial", 38);
            helloLabel.x = size.width / 2;
            helloLabel.y = size.height / 2 + 200;
            this.addChild(helloLabel, 5);
            var start = new cc.MenuItemFont("开始游戏", function () {
                cc.director.runScene(new cc.TransitionFade(1, new GameScene()));
            }, this);
            var menu = new cc.Menu(start);
            this.addChild(menu);
            return true;
        }
    });
    
    var HelloWorldScene = cc.Scene.extend({
        onEnter: function () {
            this._super();
            var layer = new HelloWorldLayer();
            this.addChild(layer);
        }
    });

    这样,我们就能形成一个完成游戏流程了,游戏加载进入游戏开始场景,点击开始游戏进行如主游戏场景,游戏结束后进入结束场景,结束场景点击“再来一次”又可以回到开始场景。

    四、运行效果

    最后的运行效果如下
    贪吃蛇效果图
    通过CVP平台的项目托管可看到实际运行效果,地址如下:
    http://www.cocoscvp.com/usercode/2e17b3cd9586a574140e0bb765bad21673fc7686/

    五、源代码

    所有源代码均上传到github,欢迎交流学习,地址:
    https://github.com/hjcenry/snake


    本人的简书博客与个人博客将同步更新
    原文来自个人博客http://hjcenry.github.io/2016/05/16/Cocos2d-JS实现的贪吃蛇/
    个人博客地址:http://hjcenry.github.io/

    展开全文
  • cocos creator开发微信小游戏(五)贪吃蛇大作战

    千次阅读 热门讨论 2018-11-13 22:37:50
    贪吃蛇小游戏:贪吃蛇试玩(首次加载比较慢),类似贪吃蛇大作战的小游戏。当玩家的蛇头碰到另一条蛇蛇身时死亡,游戏结束。 小游戏cocos creator场景图 小游戏的cocos creator节点结构如下图,Canvas上面的节点是...
  • 开发工具:Cocos Creator和VS Code 开发语言:TS 简化版贪吃蛇的实现主要涉及的功能就是在吃到场景中随机产生产生的物体后,物体会到蛇头的后面并且跟随移动路径,其原理主要是通过数组来存储相关的坐标数据。 ....
  • 贪吃蛇平滑移动 初始化蛇头和蛇身 调整蛇头方向 贪吃蛇移动 蛇头和蛇身的节点顺序 添加食物 添加碰撞逻辑代码 在本教程中我们重点来学习下如何让贪吃蛇能够平滑移动。 运行效果如下: Cocos Creator...
  • 贪吃蛇大战 cocoscreator

    2018-03-12 14:25:40
    贪吃蛇 大作战 cocos creator 仅供学习使用~~~~~~~学习
  • 这段时间开始学习cocos creator,小试牛刀,尝试怎么做一款经典小游戏贪吃蛇。折腾了几天,终于边看文档边代码做出相对完整的小游戏。 首先收集材料: 1、方块图片(自己弄几块不一样的图片就好了,大小是48x48)...
  • 下载链接 cocos 贪吃蛇大作战源码下载链接 cocos 贪吃蛇大作战源码下载链接 cocos 贪吃蛇大作战源码
  • 摇杆与蛇移动的结合!文章底部附完整代码!效果预览摇杆控制器的实现就不多说了,可以参考KUOKUO写的摇杆控制器!Cocos Creator !我稍微修改了下,改成typescript...
  • 贪吃蛇大作战demo实现和代码

    热门讨论 2016-11-23 23:25:07
    基于cocos2dx 和c++实现的类似贪吃蛇大作战的游戏demo
  • 非常有趣的创意玩法贪吃蛇一笔画玩法 里面将近有1000关游戏,分为三个难度 乡试 会试 殿试 每个难度通过200关可以解锁下一个难度 在这里插入图片描述 每得到一条蛇就会产生离线收益 兑换道具! 这款创意贪吃蛇小...
  • 贪吃蛇大作战 源码

    2018-05-14 11:16:12
    贪吃蛇大作战单机版源码,有AI的实现,win32开发,android打包!
  • 使用cocos2d-x3.2编写的贪吃蛇游戏,包含主界面,游戏界面,帮助界面,界面的切换与游戏的实现。适用新手学习cocos2d-x3.2引擎。
  • 贪吃蛇之战QQ轻游戏

    2018-11-19 22:27:42
    cocos creator v1.9.3 开发的微信小游戏,附带QQ轻游戏相关代码(使用的VScode的qqextension 0.5.5版本),此资源仅供学习参考用途。
  • cocos2dx贪吃蛇

    热门讨论 2014-01-24 16:40:39
    cocos2dx贪吃蛇 需要的童鞋赶快下载学习吧
  • 贪吃蛇大作战

    2018-06-11 12:01:26
    贪吃蛇贪吃蛇3D复仇》(Snake 3D Revenge)游戏的玩法和一般的贪吃蛇无二,都是控制小蛇前进的方向,尽可能吃更多的东西使自己变长,注意事项也和以前一样,首先不能咬到自己的尾巴。游戏共有三个难度可供选择,
1 2 3 4 5 ... 20
收藏数 495
精华内容 198
热门标签
关键字:

cocos贪吃蛇