cocos2dx lua 实现flappybird

    技术2022-07-21  89

    cocos2dx lua 实现flappybird

    效果导语目录结构函数入口开始按钮,绑定点击事件点击屏幕事件更新定时器update的实现完整代码项目地址

    效果

    导语

    本人初学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

    Processed: 0.009, SQL: 10