1. 程式人生 > >cocos2d-lua消除遊戲實戰(二)水果生成演算法和消除演算法

cocos2d-lua消除遊戲實戰(二)水果生成演算法和消除演算法

效果是這樣的:


程式碼如下:

FruitItem = import("app.scenes.FruitItem")

local PlayScene = class("PlayScene", function()
	return display.newScene("PlayScene")
end)

function PlayScene:ctor()
	-- init value
	self.highSorce = 0 -- 最高分數
	self.stage = 1 -- 當前關卡
	self.target = 123 -- 通關分數
	self.curSorce = 0 -- 當前分數
	self.xCount = 8 -- 水平方向水果數
	self.yCount = 8 -- 垂直方向水果數
	self.fruitGap = 0 -- 水果間距
	self.scoreStart = 5 -- 水果基分
	self.scoreStep = 10 -- 加成分數
	self.activeScore = 0 -- 當前高亮的水果得分

	self:initUI()

	-- 初始化隨機數
	math.newrandomseed()

	--  計算水果矩陣左下角的x、y座標:以矩陣中點對齊螢幕中點來計算,然後再做Y軸修正。
	self.matrixLBX = (display.width - FruitItem.getWidth() * self.xCount - (self.yCount - 1) * self.fruitGap) / 2
	self.matrixLBY = (display.height - FruitItem.getWidth() * self.yCount - (self.xCount - 1) * self.fruitGap) / 2 - 30

	-- 等待轉場特效結束後再載入矩陣
	self:addNodeEventListener(cc.NODE_EVENT, function(event)
		if event.name == "enterTransitionFinish" then
			self:initMartix()
		end
	end)
end

function PlayScene:initUI()
	-- 背景圖片
	display.newSprite("playBG.png")
		:pos(display.cx, display.cy)
		:addTo(self)

	-- high sorce
	display.newSprite("#high_score.png")
		:align(display.LEFT_CENTER, display.left + 15, display.top - 30)
		:addTo(self)

	display.newSprite("#highscore_part.png")
		:align(display.LEFT_CENTER, display.cx + 10, display.top - 26)
		:addTo(self)

	self.highSorceLabel = display.newTTFLabel({text = tostring(self.highSorce), font = "font/earth38"})
		:align(display.CENTER, display.cx + 105, display.top - 24)
		:addTo(self)
	
	-- 聲音
	display.newSprite("#sound.png")
		:align(display.CENTER, display.right - 60, display.top - 30)
		:addTo(self)
		-- stage
	display.newSprite("#stage.png")
		:align(display.LEFT_CENTER, display.left + 15, display.top - 80)
		:addTo(self)

	display.newSprite("#stage_part.png")
		:align(display.LEFT_CENTER, display.left + 170, display.top - 80)
		:addTo(self)

	self.highStageLabel = display.newTTFLabel({text = tostring(self.stage), font = "font/earth32"})
		:align(display.CENTER, display.left + 214, display.top - 78)
        :addTo(self)
	
	-- target
	display.newSprite("#tarcet.png")
		:align(display.LEFT_CENTER, display.cx - 50, display.top - 80)
		:addTo(self)

	display.newSprite("#tarcet_part.png")
		:align(display.LEFT_CENTER, display.cx + 130, display.top - 78)
		:addTo(self)

	self.highTargetLabel = display.newTTFLabel({text = tostring(self.target), font = "font/earth32"})
		:align(display.CENTER, display.cx + 195, display.top - 76)
        :addTo(self)

	-- current sorce
	display.newSprite("#score_now.png")
		:align(display.CENTER, display.cx, display.top - 150)
		:addTo(self)

	self.curSorceLabel = display.newTTFLabel({Utext = tostring(self.curSorce), font = "font/earth48"})
		:align(display.CENTER, display.cx, display.top - 150)
        :addTo(self)
	
	-- 選中水果分數
	self.activeScoreLabel = display.newTTFLabel({text = "", size = 30})
		:pos(display.width / 2, 120)
		:addTo(self)
	self.activeScoreLabel:setColor(display.COLOR_WHITE)
end


function PlayScene:initMartix()
	-- 建立空矩陣
	self.matrix = {}
	-- 高亮水果
	self.actives = {}
	for y = 1, self.yCount do
		for x = 1, self.xCount do
			if 1 == y and 2 == x then
                -- 確保有可消除的水果
                self:createAndDropFruit(x, y, self.matrix[1].fruitIndex)
            else 
                self:createAndDropFruit(x, y)
			end
		end
	end
end

function PlayScene:createAndDropFruit(x, y, fruitIndex)
    local newFruit = FruitItem.new(x, y, fruitIndex)
    local endPosition = self:positionOfFruit(x, y)
    local startPosition = cc.p(endPosition.x, endPosition.y + display.height / 2)
    newFruit:setPosition(startPosition)
    local speed = startPosition.y / (2 * display.height)
    newFruit:runAction(cc.MoveTo:create(speed, endPosition))
    self.matrix[(y - 1) * self.xCount + x] = newFruit
    self:addChild(newFruit)

	-- 繫結觸控事件,此處3.7框架與3.6不太一樣了 需要先繫結,後啟用節點
	
	newFruit:addNodeEventListener(cc.NODE_TOUCH_EVENT, function(event)
		print( event.name)
		if event.name == "ended" then

			if newFruit.isActive then
				print("remove")
				self:removeActivedFruits()
				self:dropFruits()
			else
				self:inactive()
				self:activeNeighbor(newFruit)
				self:showActivesScore()
			end
		end

		if event.name == "began" then
			if newFruit.isActive then
				print("remove")
				self:removeActivedFruits()
				self:dropFruits()
			else
				self:inactive()
				self:activeNeighbor(newFruit)
				self:showActivesScore()
			end
			return true
		end
	end)
	newFruit:setTouchEnabled(true)
end


function PlayScene:removeActivedFruits()
	local fruitScore = self.scoreStart
	for _, fruit in pairs(self.actives) do
		if (fruit) then
			-- 從矩陣中移除
			self.matrix[(fruit.y - 1) * self.xCount + fruit.x] = nil
			-- 分數特效
			self:scorePopupEffect(fruitScore, fruit:getPosition())
			fruitScore = fruitScore + self.scoreStep
			fruit:removeFromParent()
		end
	end

	-- 清空高亮陣列
	self.actives = {}

	-- 更新當前得分
	self.curSorce = self.curSorce + self.activeScore
	self.curSorceLabel:setString(tostring(self.curSorce))

	-- 清空高亮水果分數統計
	self.activeScoreLabel:setString("")
	self.activeScore = 0
end
function PlayScene:scorePopupEffect(score, px, py)
	print(scorePopupEffect)
	local labelScore = display.newTTFLabel({ text = tostring(score), font = "font/earth32"})

	local move = cc.MoveBy:create(0.8, cc.p(0, 80))
	local fadeOut = cc.FadeOut:create(0.8)
	local action = cc.Sequence:create(
		cc.Spawn:create(move,fadeOut),
		-- 動畫結束移除 Label
		cc.CallFunc:create(function() labelScore:removeFromParent() end) 
	)

	labelScore:pos(px, py)
		:addTo(self)
		:runAction(action)
end
function PlayScene:dropFruits()
	local emptyInfo = {}

	-- 1. 掉落已存在的水果
	-- 一列一列的處理
	for x = 1, self.xCount do
		local removedFruits = 0
		local newY = 0
		-- 從下往上處理
		for y = 1, self.yCount do
			local temp = self.matrix[(y - 1) * self.xCount + x]
			if temp == nil then
				-- 水果已被移除
				removedFruits = removedFruits + 1
			else
				-- 如果水果下有空缺,向下移動空缺個位置
				if removedFruits > 0 then
					newY = y - removedFruits
					self.matrix[(newY - 1) * self.xCount + x] = temp
					temp.y = newY
					self.matrix[(y - 1) * self.xCount + x] = nil

					local endPosition = self:positionOfFruit(x, newY)
					local speed = (temp:getPositionY() - endPosition.y) / display.height
					temp:stopAllActions() --停止之前的動畫
					temp:runAction(cc.MoveTo:create(speed, endPosition))
				end
			end
		end

		-- 紀錄本列最終空缺數
		emptyInfo[x] = removedFruits
	end

	-- 2. 掉落新水果補齊空缺
	for x = 1, self.xCount do
		for y = self.yCount - emptyInfo[x] + 1, self.yCount do
			self:createAndDropFruit(x, y)
		end
	end
end

function PlayScene:positionOfFruit(x, y)
    local px = self.matrixLBX + (FruitItem.getWidth() + self.fruitGap) * (x - 1) + FruitItem.getWidth() / 2
    local py = self.matrixLBY + (FruitItem.getWidth() + self.fruitGap) * (y - 1) + FruitItem.getWidth() / 2
    return cc.p(px, py)
end

function PlayScene:activeNeighbor(fruit)
	-- 高亮fruit
	if false == fruit.isActive then
		fruit:setActive(true)
		table.insert(self.actives, fruit)
	end

	-- 檢查fruit左邊的水果
	if (fruit.x - 1) >= 1 then
		local leftNeighbor = self.matrix[(fruit.y - 1) * self.xCount + fruit.x - 1]
		if (leftNeighbor.isActive == false) and (leftNeighbor.fruitIndex == fruit.fruitIndex) then
			leftNeighbor:setActive(true)
			table.insert(self.actives, leftNeighbor)
			self:activeNeighbor(leftNeighbor)
		end
	end

	-- 檢查fruit右邊的水果
	if (fruit.x + 1) <= self.xCount then
		local rightNeighbor = self.matrix[(fruit.y - 1) * self.xCount + fruit.x + 1]
		if (rightNeighbor.isActive == false) and (rightNeighbor.fruitIndex == fruit.fruitIndex) then
			rightNeighbor:setActive(true)
			table.insert(self.actives, rightNeighbor)
			self:activeNeighbor(rightNeighbor)
		end
	end

	-- 檢查fruit上邊的水果
	if (fruit.y + 1) <= self.yCount then
		local upNeighbor = self.matrix[fruit.y * self.xCount + fruit.x]
		if (upNeighbor.isActive == false) and (upNeighbor.fruitIndex == fruit.fruitIndex) then
			upNeighbor:setActive(true)
			table.insert(self.actives, upNeighbor)
			self:activeNeighbor(upNeighbor)
		end
	end

	-- 檢查fruit下邊的水果
	if (fruit.y - 1) >= 1 then
		local downNeighbor = self.matrix[(fruit.y - 2) * self.xCount + fruit.x]
		if (downNeighbor.isActive == false) and (downNeighbor.fruitIndex == fruit.fruitIndex) then
			downNeighbor:setActive(true)
			table.insert(self.actives, downNeighbor)
			self:activeNeighbor(downNeighbor)
		end
	end
end

function PlayScene:inactive()
    for _, fruit in pairs(self.actives) do
        if (fruit) then
            fruit:setActive(false)
        end
    end
	self.actives = {}
end

function PlayScene:showActivesScore()
	-- 只有一個高亮,取消高亮並返回
	if 1 == #self.actives then
		self:inactive()
		self.activeScoreLabel:setString("")
		self.activeScore = 0
		return
	end

	-- 水果分數依次為5、15、25、35... ,求它們的和
	self.activeScore = (self.scoreStart * 2 + self.scoreStep * (#self.actives - 1)) * #self.actives / 2
	self.activeScoreLabel:setString(string.format("%d 連消,得分 %d", #self.actives, self.activeScore))
end

function PlayScene:onEnter()
end

function PlayScene:onExit()
end

return PlayScene