cocos2d-x lua 贪吃蛇完整项目


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:\
  • 1

可以在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完成之后,调用该成员方法  
  • 1
  • 2
  • 3
  • 4

所以我们需要在 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

蛇身

===

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

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

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

这里写图片描述


构造一条蛇

===

有了蛇身体,构造一条蛇就容易了 
编写一个蛇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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

响应的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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

这里写图片描述


小蛇动起来

===

只需要设个定时器,刷新屏幕就行了,所以在上面的基础上,只需要加个定时器,更新小蛇就行了 
修改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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

定时器,就一句话 
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

逻辑: 根据一个移动方向,得到新的头部位置,然后从尾部到头部,每一个节点都更新成上一个节点,因为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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

定义一个事件监听,并注册,分发事件等 
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行截图 
这里写图片描述

键盘控制小蛇

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

同样的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

就这样就行了

这里写图片描述

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


围墙 和 死亡

===

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

添加围墙

编写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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

程序需要传递 行,列(其实只是最终围墙的一半),父节点(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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

加了下面的几句

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

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

这里写图片描述

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

死亡动画

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

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

改动:

  • 类似 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

其中 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166

改动,

我们定义了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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

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

在上一步的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

注意需要

local AppleFactory = require("app.AppleFactory")
  • 1

显示如下 
这里写图片描述


吃苹果

===

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

修改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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

分数版

===

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

修改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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

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


到目前为止,游戏基本能玩了,不过会发现问题有很多,目前版本的源码下载地址如下 
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

修复蛇移动 图片显示的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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


蛇吃到自己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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

然后在 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

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

====

让程序暂停,恢复

参看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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

添加障碍物

采用跟添加苹果类似的简单程序,因为是可以添加很多障碍物的,所以写好障碍物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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

同样的可以在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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

还要在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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里写图片描述

保存的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},
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

测试 场景

上一步中写好场景后,可以在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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

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

TODO


你可能感兴趣的:(cocos2d-x lua 贪吃蛇完整项目)