cocos2dx lua 实现flappybird

效果

在这里插入图片描述

导语

本人初学cocos2dx-lua,代码可能存在bug,仅供参考。游戏只使用了一个场景完成了整个游戏,小鸟的重力使用更新定时器update实现,点击时速度设为0,每一帧速度+1

目录结构

在这里插入图片描述

  • frameworks:框架,cocos2d-x引擎框架库和各个平台工程。
  • obj,runtime,simulator:vs运行后所产生的文件夹(runtime:运行时的文件;simulator:模拟器运行的文件)。
  • res:资源目录,包含图片,音乐,音效,字体等。
  • src:lua代码目录,包含封装Cocos2d-x的lua库,自己开发的lua文件。
  • config.json:游戏配置文件。
  • lua.config,lua.luaproj:创建lua项目时产生的文件

函数入口

src/main.lua
调用MyApp的create创建主场景,run运行主场景,xpcall运行main函数

cc.FileUtils:getInstance():setPopupNotify(false)
require "config"
require "cocos.init"

local function main()
    require("app.MyApp"):create():run()
end

local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
    print(msg)
end

src/app/MyApp.lua
MyApp 继承 AppBase

local MyApp = class("MyApp", cc.load("mvc").AppBase)
function MyApp:onCreate()
    math.randomseed(os.time())
end
return MyApp

src/packages/mvc/AppBase.lua
main.lua调用create()时会调用ctor函数
可以看到run函数加载了MainScene场景

local AppBase = class("AppBase")

function AppBase:ctor(configs) 
    self.configs_ = {
        viewsRoot  = "app.views",
        modelsRoot = "app.models",
        defaultSceneName = "MainScene", 
    }

    for k, v in pairs(configs or {}) do
        self.configs_[k] = v
    end

    if type(self.configs_.viewsRoot) ~= "table" then
        self.configs_.viewsRoot = {self.configs_.viewsRoot}
    end
    if type(self.configs_.modelsRoot) ~= "table" then
        self.configs_.modelsRoot = {self.configs_.modelsRoot}
    end

    if DEBUG > 1 then
        dump(self.configs_, "AppBase configs")
    end

    if CC_SHOW_FPS then
        cc.Director:getInstance():setDisplayStats(true)
    end

    -- event
    self:onCreate()
end

function AppBase:run(initSceneName)
    initSceneName = initSceneName or self.configs_.defaultSceneName
    self:enterScene(initSceneName)
end

开始按钮,绑定点击事件

    -- 开始按钮
    local beginSprite =
        display.newSprite("button_play.png"):move(display.cx, display.cy):setName("beginSprite"):addTo(self, 9)
   -- 点击开始按钮事件
    local function onTouchBeganButton(touch, event)
        local target = event:getCurrentTarget()
        local size = target:getContentSize()
        local rect = cc.rect(0, 0, size.width, size.height)
        local locationInNode = target:convertTouchToNodeSpace(touch)
        -- 判断是否点中按钮
        if cc.rectContainsPoint(rect, locationInNode) then
            print("onTouchBeganButton")
            if gameStatus == GAME_INIT then
                self:removeChildByName("title")
                birdSprite:show()
                gameStart()
            end
            if gameStatus == GAME_OVER then
                gameRestart()
            end
            return true
        end
        return false
    end
        -- 绑定事件到按钮
    local touchListener = cc.EventListenerTouchOneByOne:create()
    touchListener:registerScriptHandler(onTouchBeganButton, cc.Handler.EVENT_TOUCH_BEGAN)
    cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(touchListener, beginSprite)

点击屏幕事件

    -- 点击场景事件
    local function onTouchScene(touch, event)
        print("onTouchScene")
        if gameStatus == GAME_START then
            -- 每次点击都初始化速度
            downSpeed = 0
            upSpeed = 10

            -- 小鸟抬头动画
            local rotateUp = cc.RotateTo:create(0.1, -40)
            local stop = cc.RotateTo:create(0.4, -40)
            local rotateDown = cc.RotateTo:create(0.1, 40.)
            local touchActionSeq = cc.Sequence:create(rotateUp, stop, rotateDown)
            birdSprite:runAction(touchActionSeq)

            -- 向上加速度的定时器
            local scheduler = cc.Director:getInstance():getScheduler()
            local schedulerID = nil
            schedulerID =
                scheduler:scheduleScriptFunc(
                function()
                    upSpeed = upSpeed - 1
                    if upSpeed <= 0 then
                        cc.Director:getInstance():getScheduler():unscheduleScriptEntry(schedulerID)
                    end
                    birdSprite:setPositionY(birdSprite:getPositionY() + upSpeed)
                end,
                0,
                false
            )
        end
    end

    -- 绑定事件到场景
    local touchListener = cc.EventListenerTouchOneByOne:create()
    touchListener:registerScriptHandler(onTouchScene, cc.Handler.EVENT_TOUCH_BEGAN)
    cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(touchListener, self)

更新定时器update的实现


  local function update()
     -- .....
  end
self:onUpdate(update)

完整代码

src/app/views/MainScene.lua

require "config"
local MainScene = class("MainScene", cc.load("mvc").ViewBase)

function MainScene:onCreate()
    -- 初始化游戏参数
    local pipes = {}
    local gameStatus = GAME_INIT
    local downSpeed = 0
    local upSpeed = 0
    local score = 0
    local medals = {}
    -- 背景
    display.newSprite("bg_day.png"):move(display.center):addTo(self)

    -- 地板
    local land1 = display.newSprite("land.png"):move(display.cx, 0):setName("land1"):addTo(self)
    local land2 = display.newSprite("land.png"):move(display.cx, 0):setName("land2"):addTo(self)

    -- 标题
    local title = display.newSprite("title.png"):move(display.cx, display.cy + 100):setName("title"):addTo(self)

    -- 小鸟
    local birdSprite = display.newSprite("bird1.png"):move(display.center):setName("birdSprite"):addTo(self, 10):hide()

    -- 小鸟飞帧动画
    local animFrames = {}
    table.insert(animFrames, cc.SpriteFrame:create("bird1.png", cc.rect(0, 0, 38, 27)))
    table.insert(animFrames, cc.SpriteFrame:create("bird2.png", cc.rect(0, 0, 38, 27)))
    table.insert(animFrames, cc.SpriteFrame:create("bird3.png", cc.rect(0, 0, 38, 27)))
    local animation = cc.Animation:createWithSpriteFrames(animFrames, 0.1)
    local animate = cc.Animate:create(animation)
    local swingAnimate = cc.RepeatForever:create(animate):setTag(1)
    birdSprite:runAction(swingAnimate)

    -- 分数面板
    local scoreSprite =
        display.newSprite("score.png"):move(display.center):setName("scoreSprite"):hide():addTo(self, 10)
    local textGameOver =
        display.newSprite("text_game_over.png"):move(display.cx, display.cy + 100):setName("textGameOver"):hide():addTo(
        self,
        10
    )
    local scoreLabel =
        cc.Label:createWithSystemFont("0", "黑体", 20):move(display.cx, display.cy + 10):setName("scoreLabel"):hide():addTo(
        self,
        10
    )
    local maxScoreLabel =
        cc.Label:createWithSystemFont("0", "黑体", 20):move(display.cx, display.cy - 30):setName("maxScoreLabel"):hide():addTo(
        self,
        10
    )
    local nowScoreLabel =
        cc.Label:createWithSystemFont("0", "黑体", 50):move(display.cx, display.cy + 200):setName("nowScoreLabel"):hide():addTo(
        self,
        10
    )
    -- 奖牌
    local medal1 =
        display.newSprite("medals.png"):setName("newMedal"):move(
        display.cx + math.random(200) + 350,
        display.cy + math.random(200) - 100
    ):hide():addTo(self, 8)
    local medal2 =
        display.newSprite("medals.png"):setName("newMedal"):move(
        display.cx + math.random(200) + 350,
        display.cy + math.random(200) - 100
    ):hide():addTo(self, 8)

    -- 开始按钮
    local beginSprite =
        display.newSprite("button_play.png"):move(display.cx, display.cy):setName("beginSprite"):addTo(self, 9)

    -- 游戏开始
    local function gameStart()
        print("gameStart")
        medal1:show()
        medal2:show()
        nowScoreLabel:show()
        beginSprite:hide()
        cc.Director:getInstance():getEventDispatcher():pauseEventListenersForTarget(beginSprite)
        gameStatus = GAME_START
        pipes = {}
        for i = 0, 1, 1 do
            local r = math.random(PIPE_VARIATION_RANGE)
            local pipeUp =
                display.newSprite("pipe_up.png"):move(
                PIPE_START_WIDTH + i * PIPE_BETWEEN_WIDTH,
                CC_DESIGN_RESOLUTION.height - PIPE_SPACE + r
            ):setName("newPipe"):addTo(self)
            local pipeDown =
                display.newSprite("pipe_down.png"):move(PIPE_START_WIDTH + i * PIPE_BETWEEN_WIDTH, r):setName("newPipe"):addTo(
                self
            )

            table.insert(pipes, pipeUp)
            table.insert(pipes, pipeDown)
        end
    end

    -- 游戏重新开始
    local function gameRestart()
        local animFrames = {}
        table.insert(animFrames, cc.SpriteFrame:create("bird1.png", cc.rect(0, 0, 38, 27)))
        table.insert(animFrames, cc.SpriteFrame:create("bird2.png", cc.rect(0, 0, 38, 27)))
        table.insert(animFrames, cc.SpriteFrame:create("bird3.png", cc.rect(0, 0, 38, 27)))
        local animation = cc.Animation:createWithSpriteFrames(animFrames, 0.1)
        local animate = cc.Animate:create(animation)
        local swingAnimate = cc.RepeatForever:create(animate):setTag(1)
        birdSprite:runAction(swingAnimate)
        birdSprite:move(display.center)
        beginSprite:hide()
        for k, v in pairs(pipes) do
            self:removeChild(v)
        end
        scoreLabel:hide()
        maxScoreLabel:hide()
        scoreSprite:hide()
        textGameOver:hide()
        score = 0
        nowScoreLabel:setString(score)

        medal1:move(display.cx + math.random(200) + 350, display.cy + math.random(200) - 100)
        medal2:move(display.cx + math.random(200) + 350, display.cy + math.random(200) - 100)
        gameStart()
    end

    -- 游戏结束
    local function gameOver()
        print("gameOver")
        gameStatus = GAME_OVER
        birdSprite:stopAction(birdSprite:getActionByTag(1))
        beginSprite:show():move(display.cx, display.cy - 100)
        cc.Director:getInstance():getEventDispatcher():resumeEventListenersForTarget(beginSprite)
        scoreLabel:show()
        maxScoreLabel:show()
        scoreSprite:show()
        textGameOver:show()
        nowScoreLabel:hide()
        scoreLabel:setString(tostring(score))
        if cc.UserDefault:getInstance():getIntegerForKey("maxScore") < score then
            maxScoreLabel:setString(tostring(score))
            cc.UserDefault:getInstance():setIntegerForKey("maxScore", score)
        else
            maxScoreLabel:setString(cc.UserDefault:getInstance():getIntegerForKey("maxScore"))
        end
    end

    -- 点击开始按钮事件
    local function onTouchBeganButton(touch, event)
        local target = event:getCurrentTarget()
        local size = target:getContentSize()
        local rect = cc.rect(0, 0, size.width, size.height)
        local locationInNode = target:convertTouchToNodeSpace(touch)
        -- 判断是否点中按钮
        if cc.rectContainsPoint(rect, locationInNode) then
            print("onTouchBeganButton")
            if gameStatus == GAME_INIT then
                self:removeChildByName("title")
                birdSprite:show()
                gameStart()
            end
            if gameStatus == GAME_OVER then
                gameRestart()
            end
            return true
        end
        return false
    end

    -- 点击场景事件
    local function onTouchScene(touch, event)
        print("onTouchScene")
        if gameStatus == GAME_START then
            -- 每次点击都初始化速度
            downSpeed = 0
            upSpeed = 10

            -- 小鸟抬头动画
            local rotateUp = cc.RotateTo:create(0.1, -40)
            local stop = cc.RotateTo:create(0.4, -40)
            local rotateDown = cc.RotateTo:create(0.1, 40.)
            local touchActionSeq = cc.Sequence:create(rotateUp, stop, rotateDown)
            birdSprite:runAction(touchActionSeq)

            -- 向上加速度的定时器
            local scheduler = cc.Director:getInstance():getScheduler()
            local schedulerID = nil
            schedulerID =
                scheduler:scheduleScriptFunc(
                function()
                    upSpeed = upSpeed - 1
                    if upSpeed <= 0 then
                        cc.Director:getInstance():getScheduler():unscheduleScriptEntry(schedulerID)
                    end
                    birdSprite:setPositionY(birdSprite:getPositionY() + upSpeed)
                end,
                0,
                false
            )
        end
    end

    -- 绑定事件到按钮
    local touchListener = cc.EventListenerTouchOneByOne:create()
    touchListener:registerScriptHandler(onTouchBeganButton, cc.Handler.EVENT_TOUCH_BEGAN)
    cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(touchListener, beginSprite)

    -- 绑定事件到场景
    local touchListener = cc.EventListenerTouchOneByOne:create()
    touchListener:registerScriptHandler(onTouchScene, cc.Handler.EVENT_TOUCH_BEGAN)
    cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(touchListener, self)

    --  更新定时器
    local function update()
        if gameStatus == GAME_START then
            -- 奖牌的移动
            medal1:setPositionX(medal1:getPositionX() - 1)
            medal2:setPositionX(medal2:getPositionX() - 1)
            if medal1:getPositionX() <= -medal1:getContentSize().width / 2 then
                medal1:setPosition(
                    CC_DESIGN_RESOLUTION.width + medal1:getContentSize().width + math.random(100),
                    display.cy - 100 + math.random(200)
                )
            end
            if medal2:getPositionX() <= -medal2:getContentSize().width / 2 then
                medal2:setPosition(
                    CC_DESIGN_RESOLUTION.width + medal2:getContentSize().width + math.random(100),
                    display.cy - 100 + math.random(200)
                )
            end

            if cc.rectIntersectsRect(birdSprite:getBoundingBox(), medal1:getBoundingBox()) then
                print("get medal")
                score = score + 1
                nowScoreLabel:setString(tostring(score))
                medal1:setPosition(
                    CC_DESIGN_RESOLUTION.width + medal1:getContentSize().width + math.random(100),
                    display.cy - 100 + math.random(200)
                )
            end
            if cc.rectIntersectsRect(birdSprite:getBoundingBox(), medal2:getBoundingBox()) then
                print("get medal")
                score = score + 1
                nowScoreLabel:setString(tostring(score))
                medal2:setPosition(
                    CC_DESIGN_RESOLUTION.width + medal2:getContentSize().width + math.random(100),
                    display.cy - 100 + math.random(200)
                )
            end

            -- 小鸟的重力
            downSpeed = downSpeed + 1
            birdSprite:setPositionY(birdSprite:getPositionY() - downSpeed / 10)
            -- 地板的移动
            land1:setPositionX(land1:getPositionX() - 1)
            land2:setPositionX(land1:getPositionX() + land1:getContentSize().width - 2)
            if land2:getPositionX() <= land2:getContentSize().width / 2 then
                land1:setPosition(0, 0)
            end
            -- 管道的移动
            local r = 100
            for k, v in pairs(pipes) do
                v:setPositionX(v:getPositionX() - 1)
                -- 得分判断
                if v:getName() == "newPipe" then
                    if birdSprite:getPositionX() > v:getPositionX() then
                        score = score + 1
                        nowScoreLabel:setString(tostring(score))
                        v:setName("passed")
                    end
                end
                if v:getPositionX() < -PIPE_WIDTH / 2 then
                    v:setPositionX(CC_DESIGN_RESOLUTION.width + PIPE_WIDTH / 2)
                    v:setName("newPipe")
                    if k % 2 == 1 then
                        r = math.random(PIPE_VARIATION_RANGE)
                        v:setPositionY(CC_DESIGN_RESOLUTION.height - PIPE_SPACE + r)
                    else
                        v:setPositionY(r)
                    end
                end
            end

            -- 碰撞检测
            -- 地板
            if
                cc.rectIntersectsRect(birdSprite:getBoundingBox(), land1:getBoundingBox()) or
                    cc.rectIntersectsRect(birdSprite:getBoundingBox(), land2:getBoundingBox())
             then
                print("boundingBox")
                gameOver()
            end
            -- 管道
            for k, v in pairs(pipes) do
                if cc.rectIntersectsRect(birdSprite:getBoundingBox(), v:getBoundingBox()) then
                    print("boundingBox")
                    gameOver()
                end
            end
            -- 天
            if birdSprite:getPositionY() > CC_DESIGN_RESOLUTION.height then
                gameOver()
            end
        end
        -- 游戏结束后掉落到地板
        if gameStatus == GAME_OVER then
            if
                not (cc.rectIntersectsRect(birdSprite:getBoundingBox(), land1:getBoundingBox()) or
                    cc.rectIntersectsRect(birdSprite:getBoundingBox(), land2:getBoundingBox()))
             then
                downSpeed = downSpeed + 1
                birdSprite:setPositionY(birdSprite:getPositionY() - downSpeed)
            end
        end
    end
    self:onUpdate(update)
end

return MainScene

项目地址

https://github.com/ouwenfeng/flappybirdlua.git

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值