cocos2d-x lua 贪吃蛇

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

开发环境,项目编译运行

===

windows 直接下载windows版本最新的,网址如下
http://www.cocos.com/download/cocos2d-lua/

解压到某个目录下就行了,setup.py设置环境
cocos2d-x lua 贪吃蛇_第1张图片

切换到如下的目录,既可以创建lua项目
cocos2d-x lua 贪吃蛇_第2张图片

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

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

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

接着用vs2012 或者vs2013 打开如下的解决方案,并完成编译
cocos2d-x lua 贪吃蛇_第3张图片

最后可以运行出结果,可在如下的目录中运行,点击exe即可
cocos2d-x lua 贪吃蛇_第4张图片

cocos2d-x lua 贪吃蛇_第5张图片

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

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

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

cocos2d-x lua 贪吃蛇_第6张图片

构造一条蛇

===

有了蛇身体,构造一条蛇就容易了
编写一个蛇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

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

cocos2d-x lua 贪吃蛇_第7张图片

小蛇动起来

===

只需要设个定时器,刷新屏幕就行了,所以在上面的基础上,只需要加个定时器,更新小蛇就行了
修改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存储着

显示如下:

cocos2d-x lua 贪吃蛇_第8张图片

控制小蛇

===

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

鼠标点击控制

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

运行截图
cocos2d-x lua 贪吃蛇_第9张图片

键盘控制小蛇

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

同样的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")

显示如下
cocos2d-x lua 贪吃蛇_第10张图片

吃苹果

===

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

修改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

运行如下图
cocos2d-x lua 贪吃蛇_第11张图片

到目前为止,游戏基本能玩了,不过会发现问题有很多,目前版本的源码下载地址如下
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

往下移动后显示如下,蛇头往下
cocos2d-x lua 贪吃蛇_第12张图片

蛇吃到自己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

如下如,吃到自己
cocos2d-x lua 贪吃蛇_第13张图片

====

让程序暂停,恢复

参看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

TODO logic

你可能感兴趣的:(cocos2d-x)