天天看點

quick-cocos2d-x從零開始遊戲開發筆記(四):參照Flappy Bird制作第一個遊戲②

主角有了,下面應該參照原作讓它上下微微的起伏。

在MenuScene:ctor()方法裡面繼續添加代碼:

local function titleBirdMove(dt)
local sequence = transition.sequence({
	CCMoveTo:create(0.5, CCPoint(display.cx, display.cy + 55)),
	-- CCDelayTime:create(0.5),
	CCMoveTo:create(0.5, CCPoint(display.cx, display.cy + 45)),
})
sprite:runAction(sequence)
end
local scheduler = require(cc.PACKAGE_NAME .. ".scheduler")
local birdUpDown = scheduler.scheduleGlobal(titleBirdMove, 0.5)
           

其中需要注意的是scheduler這個對象的使用是需要添加引用的,也就是local scheduler = require(cc.PACKAGE_NAME .. ".scheduler")這行代碼。

這裡很想吐槽一下quick-x的wiki文檔是在是太簡陋了。好多具體用法都不是太詳細或者幹脆沒有,好多時候都需要看源碼或者上百度查。

F5運作一下player,發現bird是上下移動了,但是沒有向前的感覺。

為什麼呢?地闆沒有移動!沒有參照物!該添加地闆,并且讓地闆不停的向左移動了。

我最開始的做法是設定一個循環方法,添加兩個地闆的對象,第二個地闆對象排列在第一個地闆對象的後面,然後兩個地闆圖檔同時向左移動,移動一個圖檔的距離之後,充值兩個圖檔的初始坐标重新開始向左移動,後來運作的時候覺得有點卡頓的感覺。上網查了些資料改成了根據逐幀的方法來改變圖檔的位置。這樣看起來效果比較平滑,圖檔切的好的話是無縫的,看不出來圖檔的切換。

local floor = display.newSprite("#move_bottom.png", display.cx, display.bottom + 30)
local floor2 = display.newSprite("#move_bottom.png", display.cx + 336, display.bottom + 30)

local function floorMove(dt)			-- 地闆循環移動造成前進的感覺
	if floor:getPositionX() <= -floor:getContentSize().width*0.5 then
			floor:setPosition(floor:getContentSize().width*1.5 - 2, display.bottom + 30)
	else floor:setPosition(floor:getPositionX() - 2, display.bottom + 30) end 

	if floor2:getPositionX() <= -floor2:getContentSize().width*0.5 then 
			floor2:setPosition(floor2:getContentSize().width*1.5 - 2, display.bottom + 30)
			else floor2:setPosition(floor2:getPositionX() - 2, display.bottom + 30)
	end	
end
	
local FLOOR_MOVE = scheduler.scheduleUpdateGlobal(floorMove)
self:addChild(floor)
self:addChild(floor2)
           

然後加入一個按鈕,用來開始遊戲,跳轉到MainScene界面。

local function onBtnStartClicked(tag)		
		app:EnterMainScene()
		scheduler.unscheduleGlobal(birdUpDown)
		scheduler.unscheduleGlobal(FLOOR_MOVE)
	end
 
	local btnStart = ui.newImageMenuItem({
		image = "#bird_start_btn.png",
		imageSelected = "#brid_start_btn_pressed.png",
		x = display.cx,
		y = display.cy - 20,
		listener = onBtnStartClicked
	})
	local menu = ui.newMenu({btnStart})
	self:addChild(menu)
           

app:EnterMainScene()這個方法是寫在MyApp.lua裡面的,還有一個EnterMenuScene()方法,是用來回到Menu頁面的,一起寫在這裡了:

quick-cocos2d-x從零開始遊戲開發筆記(四):參照Flappy Bird制作第一個遊戲②

最終的Menu頁面效果如圖,點選按鈕就可以開始遊戲了

quick-cocos2d-x從零開始遊戲開發筆記(四):參照Flappy Bird制作第一個遊戲②

終于來到主界面了。是不是有點小激動了呢?

加入背景圖檔,這次我們選夜晚那張,和白天那張差別開來

function MainScene:ctor()
      self.bg = display.newSprite("#bird_bg_night.png", display.cx, display.cy) 
      self:addChild(self.bg)
end
           

下面參照MenuScene,把界面畫好

self.getReady = display.newSprite("#get_ready.png", display.cx, display.top - 125)
	self:addChild(self.getReady)

	local frames = display.newFrames("bird%01d.png", 1, 3)
	self.sprite = display.newSprite(frames[1])
	self.sprite:zorder(3)
	local animation = display.newAnimation(frames, 1 / 7)   --1秒播放3幀 
	self.HERO_ACTION = self.sprite:playAnimationForever(animation)    -- 循環播放動畫
	self.sprite:setPosition(display.cx - 55, display.cy)
	-- sprite:setScale(1)   -- 設定精靈大小

	local function titleBirdMove(dt)
	  local sequence = transition.sequence({
	      CCMoveTo:create(0.5, CCPoint(display.cx - 55, display.cy + 5)),
	      -- CCDelayTime:create(0.5),
	      CCMoveTo:create(0.5, CCPoint(display.cx - 55, display.cy - 5)),
	  })
	  self.sprite:runAction(sequence)
	end
	self.birdUpDown = scheduler.scheduleGlobal(titleBirdMove, 1)    -- 精靈上下微動

	self:addChild(self.sprite)

	self.tip = display.newSprite("#bird_tip.png", display.cx, display.cy - 10)
	self.tip:addTo(self)

	self.floor = display.newSprite("#move_bottom.png")
	self.floor:setPosition(self.floor:getContentSize().width* 0.5, display.bottom + 30)
	self.floor2 = display.newSprite("#move_bottom.png")
	self.floor2:setPosition(self.floor2:getContentSize().width* 1.5, display.bottom + 30)

	local function floorMove(dt)      -- 地闆循環移動造成前進的感覺
	  local sp1 = self.floor    
	  if sp1:getPositionX() <= -sp1:getContentSize().width*0.5 then
	    sp1:setPosition(sp1:getContentSize().width*1.5 - 2, display.bottom + 30)
	  else sp1:setPosition(sp1:getPositionX() - 2, display.bottom + 30) end 

	  local sp2 = self.floor2
	  if sp2:getPositionX() <= -sp2:getContentSize().width*0.5 then 
	    sp2:setPosition(sp2:getContentSize().width*1.5 - 2, display.bottom + 30)
	    else sp2:setPosition(sp2:getPositionX() - 2, display.bottom + 30)
	  end
	end

	self.FLOOR_MOVE = scheduler.scheduleUpdateGlobal(floorMove)
	self:addChild(self.floor, 2)
	self:addChild(self.floor2, 2)
           

F5看一下效果:

quick-cocos2d-x從零開始遊戲開發筆記(四):參照Flappy Bird制作第一個遊戲②

根據原作的邏輯,在螢幕上任意地方點選一下,遊戲就開始了。那麼我們首先要添加對螢幕的觸摸監聽事件:

在MainScene:ctor()裡面添加touchLayer

-- touchLayer 用于接收觸摸事件
	self.touchLayer = display.newLayer()
	self:addChild(self.touchLayer)
           

并且添加MainScene的onEnter事件和onTouch事件

function MainScene:onEnter()
	-- 注冊touch事件處理函數
	self.touchLayer:addTouchEventListener(function(event, x, y)
		return self:onTouch(event, x, y)
	end)
	self.touchLayer:setTouchEnabled(true)
end
           
function MainScene:onTouch(event, x, y)
	if self.FIRST_TAP then
		self:InitGame()
		self.CollisionDetection = scheduler.scheduleUpdateGlobal(function() self:UpdateGame() end)
	end
	
	if STATE_GAME_OVER == true then
		return false
	end


	GRAVITY = -2 								-- 重力恢複
	-- audio.playEffect(MUSIC.wing, false)			-- 播放揮動翅膀音效


	scheduler.unscheduleGlobal(self.birdUpDown)


	transition.stopTarget(self.sprite)
	transition.execute(self.sprite, CCRotateTo:create(0.2, -30), {
		-- delay = 0.1,
		easing = "backout",
		onComplete = function()
			local sequence = transition.sequence({
				CCDelayTime:create(0.4),
				CCRotateTo:create(0.6, 90)
			})
			self.sprite:runAction(sequence)
		end,
	})
	self.HERO_ACTION = self.sprite:playAnimationForever(display.newAnimation(display.newFrames("bird%01d.png", 1, 3), 1 / 7))


	if self.sprite:getPositionY() > display.height then
		self.sprite:setPositionY(display.height + 20)
	end
end
           

在onTouch方法裡面需要判斷是否是第一次點選螢幕,如果是的話,需要初始化一些遊戲的内容,開始添加障礙物,并且開始碰撞檢測。字型檔案是我在sample裡面随便找的,需要拷到res檔案夾裡面,一共兩個檔案,futura-48.fnt、futura-48.png。本來想用原版的字型,沒去找字型制作的軟體。虧我還把資源裡面的原版字型一個個摳出來了……

function MainScene:InitGame()
	if self.FIRST_TAP ~= true then return false end

	self.tip:zorder(-1)
	self.getReady:zorder(-1)

	self.passNum = ui.newBMFontLabel({
		text = "0",
		font = "futura-48.fnt",
		x = display.cx,
		y = display.top - 60,
	})
	self:addChild(self.passNum, 4)

	function Gravity()	-- HERO持續受到的重力
		GRAVITY = GRAVITY - 0.15
		self.sprite:setPositionY(self.sprite:getPositionY() + GRAVITY)
		if self.sprite:getPositionY() <= 105 then 		-- 如果飛的過低,遊戲結束			
			STATE_GAME_OVER = true
		end
	end

	self.heroGravity = scheduler.scheduleUpdateGlobal(Gravity)

	local function addPipes()	-- 每1.5秒在一定範圍内随機加入一對水管,但水管之間的距離不變
		self.pipes_up = display.newSprite("#obstacle_up.png", display.right + 50, display.cy + 140 +  math.random(0, 200))
		self.pipes_down = display.newSprite("#obstacle_down.png", display.right + 50, self.pipes_up:getPositionY() - 400)
		self.pipes_up:addTo(self)
		self.top_pipes[#self.top_pipes + 1] = self.pipes_up
		self.pipes_down:addTo(self, 1)
		self.bottom_pipes[#self.bottom_pipes + 1] = self.pipes_down
	end

	self.ADD_PIPES = scheduler.scheduleGlobal(addPipes, 1.5)
	self.FIRST_TAP = false
end
           

UpdateGame()方法裡面東西比較多,在裡面有很重要的方法getBoundingBox(),這個方法傳回的對象通過intersectsRect(target)方法就能實作碰撞檢測了。

實作小鳥的點選跳躍沒有使用MoveTo,而是在UpdateGame()裡面給小鳥一個持續向下的重力

GRAVITY = GRAVITY - 0.15

同時讓小鳥有一個 持續向上的移動速度,這個速度保持不變

self.sprite:setPositionY(self.sprite:getPositionY() + 5.1)

由于這個方法是逐幀實行的,是以很快向下的速度就會超過向上的速度。每次點選螢幕的時候,就初始化小鳥的重力,始向下的速度小于向上的速度,小鳥就會猛的向上飛一段距離,然後快速向下墜。

之是以沒有使用MoveTo動畫是因為這樣看起來動畫效果更平滑,而且現實中重力是遞增的,符合實體規律。

function MainScene:UpdateGame()
	GRAVITY = GRAVITY - 0.15 					-- HERO持續受到的重力
	self.sprite:setPositionY(self.sprite:getPositionY() + GRAVITY)
	if self.sprite:getPositionY() <= 105 then 		-- 如果飛的過低,遊戲結束			
		STATE_GAME_OVER = true
	end
	self.sprite:setPositionY(self.sprite:getPositionY() + 5.1)

	local rHero = self.sprite:getBoundingBox()

	for k,v in pairs(self.top_pipes) do
		v:setPositionX(v:getPositionX() - 2)
		local rtop_Pipes = v:getBoundingBox()
		local bump = rHero:intersectsRect(rtop_Pipes)
		if bump then
			STATE_GAME_OVER = true
		end
		if v:getPositionX() < -30 then
			v:removeSelf()
			table.remove(self.top_pipes, k)
		end			
	end	

	for k,v in pairs(self.bottom_pipes) do
		v:setPositionX(v:getPositionX() - 2)
		local rbottom_Pipes = v:getBoundingBox()
		local bump = rHero:intersectsRect(rbottom_Pipes)
		if v:getPositionX() + 20 < self.sprite:getPositionX() then
			if PASS_NUMBER < 10 then
				self.passNum:setString(string.format("%01d", PASS_NUMBER))
			elseif PASS_NUMBER < 100 then
				self.passNum:setString(string.format("%02d", PASS_NUMBER))
			else
				self.passNum:setString(string.format("%03d", PASS_NUMBER))
			end
		end
		if bump then
			STATE_GAME_OVER = true
		end
		if v:getPositionX() < -30 then
			v:removeSelf()
			table.remove(self.bottom_pipes, k)
			PASS_NUMBER = PASS_NUMBER + 1
			-- audio.playEffect(MUSIC.point, false)			-- 播放得分音效
		end	
	end	

	if STATE_GAME_OVER then
		echoInfo("-----GAME OVER-----")
		-- audio.playEffect(MUSIC.hit, false)					-- 播放撞擊音效
		transition.removeAction(self.HERO_ACTION)		-- 停止小鳥揮動翅膀
		transition.pauseTarget(self.floor)					-- 停止地表移動
		transition.pauseTarget(self.floor2)
		transition.removeAction(self.FLAPPY)				-- 停止小鳥向上飛的動畫
		scheduler.unscheduleGlobal(self.ADD_PIPES)		-- 停止添加水管
		transition.pauseTarget(self.pipes_up)				-- 停止上水管移動的動畫
		transition.pauseTarget(self.pipes_down)			-- 停止下水管移動的動畫
		transition.pauseTarget(self.top_pipes[#self.top_pipes -1])
		transition.pauseTarget(self.bottom_pipes[#self.bottom_pipes -1])
		transition.moveTo(self.sprite,{y = 100, time = self.sprite:getPositionY() / 270})
		scheduler.unscheduleGlobal(self.CollisionDetection)
		scheduler.unscheduleGlobal(self.FLOOR_MOVE)
		PASS_NUMBER = 1
		function restart( )
			STATE_GAME_OVER = false
			app:EnterMainScene()
		end
		scheduler.performWithDelayGlobal(restart, 2)
	end
end
           

其中有些動畫細節,比如每次點選螢幕,小鳥的向上旋轉,等一小段時間如果不繼續點選螢幕,小鳥向下旋轉90°什麼的也在代碼中做了處理。看代碼應該能夠明白,就不一一解釋了。

另外一些效果音播放,還有如何打包成手機上可用的apk程式下次再說吧。

quick-cocos2d-x從零開始遊戲開發筆記(四):參照Flappy Bird制作第一個遊戲②

遊戲難度還是挺高的……你們能得多少分呢?我最多10幾分:)