首先声明我用的是corona引擎,不过思路都是一样的,看懂了就可以改成给其他引擎使用
先上代码:
local M = {}
local rectH = 80
M.tRect = {}
--根据数组长度分段类等腰三角形一次函数/\
local function linearFunction( sum,k,min,max )
local y
if sum==1 then
return min
end
if k>= 1+(sum-1)/2 then
y=max/(1-sum)*k+min-(max*sum/(1-sum))
else
y=max/(sum-1)*k+min-(max/(sum-1))
end
return y
end
--抛物线
local function parabola( x )
local y = math.pow((x),2)*0.3
return y
end
--抛物线
local function getScale( distanceXY )
return (1+distanceXY/6000)
end
--设置矩形y坐标
local function setRectY( ... )
local height = 0
for k,imgRect in pairs(M.tRect) do
--矩形之间间隙
local spaceY = linearFunction(#M.tRect,k,4,20)
height = height+imgRect.height+spaceY
imgRect.y = height
end
end
--移除所有矩形
local function removeAllRect( ... )
for i,imgRect in pairs(M.tRect) do
display.remove(imgRect)
imgRect = nil
end
M.tRect = {}
end
--计算矩形高度
local function computerRectHeight( sum,k )
--图片高度
local height = linearFunction(sum,k,30,80)
return height
end
--计算distanceXY应该创建多少个矩形
local function getAllCount( distanceXY )
if distanceXY<=rectH then
return 0
end
--架设矩形个数在1-20之间反复检测,满足条件就返回
--i为创建个数
for i=1,20 do
local height = 0
for j=1,i do
--矩形之间间隙
local spaceY = linearFunction(i,j,4,20)
height = height+getScale(distanceXY)*computerRectHeight(i,j)+spaceY
if height>=distanceXY-rectH then
return i
end
end
end
return 20
end
--创建矩形
local function createRect( distanceXY )
local count = getAllCount(distanceXY)-#M.tRect
if count>0 then
for i=1,count do
local imgRect = display.newImage( M.gRect, "arrow/rect.png")
imgRect.anchorY = 1
table.insert(M.tRect,imgRect)
end
elseif count<0 then
for i=1,-count do
local imgRect = table.remove(M.tRect)
display.remove(imgRect)
imgRect = nil
end
end
end
--设置角度,高度,图形的变形
local function setCoordinate( startX,startY,endX,endY,distanceX,distanceY,distanceXY )
M.imgCenter.x,M.imgCenter.y = endX,endY
M.imgArrow.x,M.imgArrow.y = endX,endY
M.imgArrow.rotation = math.atan2(distanceY,distanceX)*180/math.pi+90
M.gRect.x,M.gRect.y = startX,startY
M.gRect.rotation = math.atan2(distanceY,distanceX)*180/math.pi+90
M.gRect.xScale,M.gRect.yScale = getScale(distanceXY),getScale(distanceXY)
local len = #M.tRect
local centerLen = math.ceil(#M.tRect/2)
for k,imgRect in pairs(M.tRect) do
local offsetLen = math.abs(centerLen-k)
if centerLen-k<0 then
imgRect.path.x1 = parabola(offsetLen-1)
imgRect.path.x4 = -parabola(offsetLen-1)
imgRect.path.x2 = parabola(offsetLen)
imgRect.path.x3 = -parabola(offsetLen)
else
imgRect.path.x1 = parabola(offsetLen)
imgRect.path.x4 = -parabola(offsetLen)
imgRect.path.x2 = parabola(offsetLen-1)
imgRect.path.x3 = -parabola(offsetLen-1)
end
if #M.tRect%2~=0 and k==centerLen then
imgRect.path.x1 = 0
imgRect.path.x4 = 0
imgRect.path.x2 = 0
imgRect.path.x3 = 0
end
if #M.tRect%2==0 and (k==centerLen or k==centerLen+1 ) then
if k==centerLen then
imgRect.path.x1 = 0
imgRect.path.x4 = 0
end
if k==centerLen+1 then
imgRect.path.x2 = 0
imgRect.path.x3 = 0
end
end
imgRect.height = computerRectHeight(#M.tRect,k)
end
end
--touch事件监听
local function touchListener( event )
local phase = event.phase
local self = event.target
local endX,endY = event.x-M.gTouch.x,event.y-M.gTouch.y
local startX,startY = event.xStart-M.gTouch.x,event.yStart-M.gTouch.y
local distanceX,distanceY = endX-startX,endY-startY
local distanceXY = math.sqrt(math.pow((distanceY),2)+math.pow((distanceX),2))
-- print("phase",phase,startX,startY)
if phase == "began" then
display.getCurrentStage():setFocus( self )
self.isFocus = true
M.imgCenter.isVisible = true
M.imgArrow.isVisible = true
M.gRect.isVisible = distanceXY>=rectH
setCoordinate(startX,startY,endX,endY,distanceX,distanceY,distanceXY)
elseif ( self.isFocus ) then
if phase == "moved" then
M.gRect.isVisible = distanceXY>=rectH
createRect(distanceXY)
setCoordinate(startX,startY,endX,endY,distanceX,distanceY,distanceXY)
-- setRectY()
elseif phase == "ended" or phase == "cancelled" then
display.getCurrentStage():setFocus( nil )
self.isFocus = nil
M.imgCenter.isVisible = false
M.imgArrow.isVisible = false
M.gRect.isVisible = false
removeAllRect()
end
end
return true
end
function M.addListener( sceneGroup )
M.gTouch = display.newGroup()
M.gTouch.x,M.gTouch.y = display.contentWidth/2,display.contentHeight/2
-- 底图
local imgBg = display.newRect( M.gTouch, 0,0, display.contentWidth,display.contentHeight )
--背景
local imgBg = display.newImage( M.gTouch, "bg.png")
--靶心
M.imgCenter = display.newImage( M.gTouch, "arrow/center.png")
M.imgCenter.isVisible = false
--矩形条组
M.gRect = display.newGroup()
M.gTouch:insert(M.gRect)
M.gRect.anchorY = 1
M.gRect.anchorChildren = true
M.gRect.isVisible = false
--箭头
M.imgArrow = display.newImage( M.gTouch, "arrow/arrow.png")
M.imgArrow.anchorY = -3
M.imgArrow.isVisible = false
M.gTouch:addEventListener( "touch", touchListener )
Runtime:addEventListener( "enterFrame", setRectY )
return M.gTouch
end
return M
设计前的分析思路:
1.我们应该先分析这一整个箭头的功能是由哪几块组成的,如何分块较容易实现该功能。
2.分析这个功能的难点有哪些?角度,怎么表现的有立体感,一块块矩形组成起来怎么表现出有弧度。
设计思路:
1.我们将整个箭头分成三块,矩形块的组(a),箭头(b),靶心(c)。a里面存放的一块块小矩形。
2.当鼠标移动的时候,我们只要记录touch事件点击起始的x,y,和现在touch事件的现在的x,y。便可以求出旋转的角度。
相关公式:math.atan2(distanceY,distanceX)*180/math.pi+90
distanceX,distanceY为起始x,y和现在x,y的差值
求出的角度用来设置箭头(b)和矩形块组(a)的rotation,并且设矩形块组(a)锚点anchorY=1
并且将箭头(b)和靶心(c)的坐标设成touch事件的坐标,保证(b)和(c)一直跟着touch的位置移动。
然后将矩形块的组(a)的位置设成touch事件点击的起始位置。
3.当然,我们监听的touch事件,只需要touch为移动的时候去创建。放开的时候移除矩形就可以了。
4.那么如何创建矩形,刚开始的思路是根据distanceX和distanceY勾股定理,得出鼠标起始位置到终点位置的距离(l),并且根据距离(l),得出需要创建几个矩形。刚开始还没将立体感考虑进去,所以当每个矩形的rHeight为一个固定值,所以只要l/rHeight就得出了这个距离所要创建的矩形个数。并且touch移动的时候,不要反复的全部移除和创建,而是根据距离(l),l长了就在原来矩形块组(a)的基础上继续创建所需的个数,如果l短了,就移除多了的个数。
5.在每一帧去设置这些矩形的y,y就是根据上一个矩形的y加上矩形的height,再加上一个间隙。
6.当上面的步骤做好后,其实已经有一个可以看的箭头效果了,只是不立体而已。接下来我们就是做立体的效果,有种抛物线的感觉
第一步,如果需要抛物线的效果,我们的视角是在箭头的正上方,所以整个矩形块组(a)表现出的效果就应该是两边小,中间大。所以我们要对每一个矩形做形变,两边的矩形,要形变成梯形,并且越靠矩形块组(a)的中部,形变越小,最中间的不形变。这时我只考虑矩形的width,先不考虑rHeight,因为rHeight涉及到距离(l)所要创建的矩形个数。代码中设置的x1,x2,x3,x4规则是如下图这样的。并且刚开始我用的是一次函数,后来为了更加立体,我设置的函数改成了二次抛物线函数,并且抛物线的x和数组的索引有关。
相关公式:y = math.pow((x),2)*0.3
7.设置完每个矩形的形变,你会发现图片变得胖胖的。因为我们只改变了矩形的width。所以现在我们要把矩形height考虑进去。
我们矩形的height按线性方程来设置,根据矩形块组(a)里面矩形的个数,和矩形所在的索引为条件,一样也是越两边矩形的height越小。
并且线性函数linearFunction其实是一个分段的线性函数,画出来是呈一个等腰梯形的。并且代码设的最大值为30-80之间,这就是矩形的height计算。
并且矩形和矩形之间的间隙也是一样,越中间越大,越靠边越窄。所以我们一样也是用了线性函数,只是最大值我们设置为4-20。
相关公式:左边一段y=max/(sum-1)*k+min-(max/(sum-1))
右边一段y=max/(1-sum)*k+min-(max*sum/(1-sum))
sum为当前矩形个数,max,min为函数最大值最小值
8.这时其实整个矩形块组(a)已经有立体感了。可是由于之前创建的个数是按每个矩形高度相同,都为rHeight均分计算的,所以创建出来的个数明显和距离(l)对不上,连接不起来。因为现在每个矩形的高度都不同了。
刚开始想根据距离(l)想出一个公式来计算出应该创建矩形的个数,可是发现这样行不通,因为设置矩形的高度和坐标还有间隙,都是根据当前有几个矩形决定的,而我现在要知道的就是应该创建几个矩形,这就陷入了一个死循环,计算的结果就是计算过程所需要的数据。
后来想了一下,用了假设法,假设矩形个数为1,2,3,4……一直到20,20个矩形应该是可以跨越整个屏幕了,才设了20,所以最多只可以创建20个矩形。并且根据一个个假设去计算出20种情况中哪一种满足现状距离(l)的情况,这样就得出了所需要创建的矩形个数。
9.做到以上那步已经差不多了,最后一步就是设置放大比例,根据距离(l),移的越远放的越大,也不能太大。
相关公式:y = (1+l/6000)
当然这个放大的比例也应该放入上面步骤的假设中去一起计算。
效果图: