Cocos2dx 搓牌效果

先上效果圖: 輸入圖片說明 輸入圖片說明 輸入圖片說明 cocos2dx 3D搓牌效果類以下:mvc

-- date:2017/9/26
-- author:looyer
-- purpose:3D搓牌效果層, 

-- 頂點着色器
local strVertSource = 
[[
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

uniform float ratio; //牌初始狀態到搓牌最終位置的完成度比例
uniform float radius; //搓牌相似於繞圓柱滾起,其圓柱的半徑
uniform float width;
uniform float finish; //是否完成搓牌

uniform float offx;
uniform float offy;

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

void main()
{
	//注意OpenGL-ES中:1.attribute修飾的變量是常量。2.沒有自動轉類型float a = 1;或者5.0/3都是錯誤的
	//這兩個問題改了兩天,能夠經過修改CPP代碼,打印log來調試cocos2dx程序
	vec4 tmp_pos = a_position;

	//順時針旋轉90度
	tmp_pos = vec4(tmp_pos.y, -tmp_pos.x, tmp_pos.z, tmp_pos.w);

	if(finish > 0.5) {
		tmp_pos = vec4(tmp_pos.x, -width - tmp_pos.y, tmp_pos.z, tmp_pos.w);

	}else {
		//計算卡牌彎曲的位置,相似於卡牌繞圓柱捲起的原理
		float halfPeri = radius * 3.14159; //半周長
		float hr = halfPeri * ratio;
		if(tmp_pos.y < -width + hr) {
			float dy = -tmp_pos.y - (width - hr);
			float arc = dy/radius;
			tmp_pos.y = -width + hr - sin(arc)*radius;
			tmp_pos.z = radius * (1.0-cos(arc)); //注意以前這裏是1,是錯誤的,opengles不自動類型轉換
		}
	}
	
	

	tmp_pos += vec4(offx, offy, 0.0, 0.0);

	gl_Position = CC_MVPMatrix * tmp_pos;
	v_fragmentColor = a_color;
	v_texCoord = a_texCoord;
}
]]

-- 片斷着色器
local strFragSource = 
[[
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

void main()
{
	//TODO, 這裏能夠作些片斷着色特效

	gl_FragColor = texture2D(CC_Texture0, v_texCoord);
}
]]

-- 經過圖片取得紋理id,和該紋理在plist圖中的紋理座標範圍
local function getTextureAndRange(szImage)
	local FrameCache = cc.SpriteFrameCache:getInstance()
	local sprite = FrameCache:getSpriteFrameByName(szImage)

	local rect = sprite:getRect()
	local tex = sprite:getTexture()
	local id = tex:getName() --紋理ID
	local bigWide = tex:getPixelsWide() --plist圖集的寬度
	local bigHigh = tex:getPixelsHigh()

	-- 左右上下的紋理範圍
	local ll, rr, tt, bb = rect.x/bigWide, (rect.x + rect.width)/bigWide, rect.y/bigHigh, (rect.y + rect.height)/bigHigh
	return id, {ll, rr, tt, bb}, {rect.width, rect.height}
end

-- 建立3D牌面,所需的頂點和紋理數據, size:寬高, texRange:紋理範圍, bFront:是否正面
local function initCardVertex(size, texRange, bFront)
	local nDiv = 200 --將寬分紅100份
	
	local verts = {} --位置座標
	local texs = {} --紋理座標
	local dh = size.height
	local dw = size.width/nDiv

	--計算頂點位置
	for c = 1, nDiv do 
		local x, y = (c-1)*dw, 0
		local quad = {}
		if bFront then
			quad = {x, y, x+dw, y, x, y+dh, x+dw, y, x+dw, y+dh, x, y+dh}
		else
			quad = {x, y, x, y+dh, x+dw, y, x+dw, y, x, y+dh, x+dw, y+dh}
		end
		for _, v in ipairs(quad) do table.insert(verts, v) end
	end

	local bXTex = true --是否當前在計算橫座標紋理座標,
	for _, v in ipairs(verts) do 
		if bXTex then
			if bFront then
				table.insert(texs, v/size.width * (texRange[2] - texRange[1]) + texRange[1])
			else
				table.insert(texs, v/size.width * (texRange[1] - texRange[2]) + texRange[2])
			end
		else
			if bFront then
				table.insert(texs, (1-v/size.height) * (texRange[4] - texRange[3]) + texRange[3])
			else
				table.insert(texs, v/size.height * (texRange[3] - texRange[4]) + texRange[4])
			end
		end
		bXTex = not bXTex
	end

	local res = {}
	local tmp = {verts, texs}
	for _, v in ipairs(tmp) do 
		local buffid = gl.createBuffer()
		gl.bindBuffer(gl.ARRAY_BUFFER, buffid)
		gl.bufferData(gl.ARRAY_BUFFER, table.getn(v), v, gl.STATIC_DRAW)
		gl.bindBuffer(gl.ARRAY_BUFFER, 0)
		table.insert(res, buffid)
	end
	return res, #verts
end

-- 建立搓牌效果層, pList:圖片合集.plist文件, szBack:牌背面圖片名, szFont:牌正面圖片名, 注意:默認傳入的牌在plist文件中是豎直的, scale縮放比
local function createRubCardEffectLayer(pList, szBack, szFont, scale)
	scale = scale or 1.0

	--預加載pList圖集(注重復添加不要緊,只會保存一份)
	local FrameCache = cc.SpriteFrameCache:getInstance()
	FrameCache:addSpriteFrames(pList)

	-- 取得屏幕寬高
	local Director = cc.Director:getInstance()
	local WinSize = Director:getWinSize()

	-- 建立廣角60度,視口寬高比是屏幕寬高比,近平面1.0,遠平面1000.0,的視景體
	local camera = cc.Camera:createPerspective(45, WinSize.width/WinSize.height, 1, 1000)
	camera:setCameraFlag(cc.CameraFlag.USER2)
	--設置攝像機的繪製順序,越大的深度越繪製的靠上,因此默認攝像機默認是0,其餘攝像機默認是1, 這句很重要!!
	camera:setDepth(1)
	camera:setPosition3D(cc.vec3(0, 0, 800))
	camera:lookAt(cc.vec3(0, 0, 0), cc.vec3(0, 1, 0))

	-- 建立用於OpenGL繪製的節點
	local glNode = gl.glNodeCreate()
	local glProgram = cc.GLProgram:createWithByteArrays(strVertSource, strFragSource)
	glProgram:retain()
	glProgram:updateUniforms()

	-- 建立搓牌圖層
	local layer = cc.Layer:create()
	layer:setCameraMask(cc.CameraFlag.USER2)
	layer:addChild(glNode)
	layer:addChild(camera)
	
	-- 退出時,釋放glProgram程序
	local function onNodeEvent(event)
		if "exit" == event then
			glProgram:release()
		end
	end
	layer:registerScriptHandler(onNodeEvent)

	local posNow = cc.p(0, 0)
 	--建立觸摸回調
	local function touchBegin(touch, event)
		posNow = touch:getLocation()
		return true
	end
	local function touchMove(touch, event)
		local location = touch:getLocation()
		local dy = location.y - posNow.y

		layer.ratioVal = cc.clampf(layer.ratioVal + dy/100, 0.0, 0.9) --最大程度默認0.9
		posNow = location
		return true
	end
	local function touchEnd(touch, event)
		layer.ratioVal = 0.0
		return true
	end
	local listener = cc.EventListenerTouchOneByOne:create()
	listener:registerScriptHandler(touchBegin, cc.Handler.EVENT_TOUCH_BEGAN )
	listener:registerScriptHandler(touchMove, cc.Handler.EVENT_TOUCH_MOVED )
	listener:registerScriptHandler(touchEnd, cc.Handler.EVENT_TOUCH_ENDED )
	local eventDispatcher = layer:getEventDispatcher()
	eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer)

	--建立牌的背面
	local id1, texRange1, sz1 = getTextureAndRange(szBack)
	local msh1, nVerts1 = initCardVertex(cc.size(sz1[1] * scale, sz1[2] * scale), texRange1, true)
	--建立牌的正面
	local id2, texRange2, sz2 = getTextureAndRange(szFont)
	local msh2, nVerts2 = initCardVertex(cc.size(sz2[1] * scale, sz2[2] * scale), texRange2, false)

	--搓牌的程度控制, 搓牌相似於經過一個圓柱滾動將牌粘着起來的效果。下面的參數就是滾動程度和圓柱半徑
	layer.ratioVal = 0.0
	layer.radiusVal = sz1[1]*scale/math.pi;

	--牌的渲染信息 
	local cardMesh = {{id1, msh1, nVerts1}, {id2, msh2, nVerts2}}
	-- OpenGL繪製函數
	local function draw(transform, transformUpdated)
		gl.enable(gl.CULL_FACE)
		glProgram:use()
		glProgram:setUniformsForBuiltins()

		for _, v in ipairs(cardMesh) do 
			gl.bindTexture(gl.TEXTURE_2D, v[1])

			-- 傳入搓牌程度到着色器中,進行位置計算
			local ratio = gl.getUniformLocation(glProgram:getProgram(), "ratio")
			glProgram:setUniformLocationF32(ratio, layer.ratioVal)
			local radius = gl.getUniformLocation(glProgram:getProgram(), "radius")
			glProgram:setUniformLocationF32(radius, layer.radiusVal)

			-- 偏移牌,使得居中
			local offx = gl.getUniformLocation(glProgram:getProgram(), "offx")
			glProgram:setUniformLocationF32(offx, WinSize.width/2 - sz1[2]/2*scale)
			local offy = gl.getUniformLocation(glProgram:getProgram(), "offy")
			glProgram:setUniformLocationF32(offy, WinSize.height/2 + sz1[1]/2*scale)

			local width = gl.getUniformLocation(glProgram:getProgram(), "width")
			glProgram:setUniformLocationF32(width, sz1[1]*scale)

			gl.glEnableVertexAttribs(bit._or(cc.VERTEX_ATTRIB_FLAG_TEX_COORDS, cc.VERTEX_ATTRIB_FLAG_POSITION))
			gl.bindBuffer(gl.ARRAY_BUFFER, v[2][1])
			gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION,2,gl.FLOAT,false,0,0)
			gl.bindBuffer(gl.ARRAY_BUFFER, v[2][2])
			gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORD,2,gl.FLOAT,false,0,0)
			gl.drawArrays(gl.TRIANGLES, 0, v[3])
		end
                gl.bindTexture(gl.TEXTURE_2D, 0)
                gl.bindBuffer(gl.ARRAY_BUFFER, 0)
	end
	glNode:registerScriptDrawHandler(draw)

	return layer
end

return createRubCardEffectLayer

演示類以下:app

-- date:2017/09/22
-- author:looyer
-- purpose:效果測試

local RubCardFunc = require("app.views.RubCardLayer")

local EffectTestNode = class("EffectTestNode", cc.load("mvc").ViewBase)

function EffectTestNode:onCreate()
end

function EffectTestNode:onEnter()
	--顯示搓牌效果
 	local layer = RubCardFunc("res/UI/images/card_cor.plist", "pkc_paimian.png", "pkc1_1.png", 2.0)
 	self:addChild(layer)
end

function EffectTestNode:onClickClose()
	self:getApp():removeView(self:getName())
end

return EffectTestNode
相關文章
相關標籤/搜索