这个需求来自于工作中的一个需求
两个城池之间的路径是美术事先画好的,武将需要沿着画好的曲线行进
经过查找资料,发现了这篇文章
用到了下面这个公式:
阶贝塞尔曲线可如下推断。给定点P0、P1、…、Pn,其贝塞尔曲线即
高阶曲线为建构高阶曲线,便需要相应更多的中介点。对于三次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2,和由二次曲线描述的点R0、R1所建构:
三次贝塞尔曲线![]()
![]()
对于四次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2、Q3,由二次贝塞尔曲线描述的点R0、R1、R2,和由三次贝塞尔曲线描述的点S0、S1所建构:
四次贝塞尔曲线![]()
![]()
更复杂的:
五次贝塞尔曲线
以下为lua的实现方式,参考了上文中的c++算法,并没有考虑在极为高阶的情况下的性能问题
代码基于quick-cocos2d-x 2.2.5编写(谁叫他快呢)
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102  | 
						-- -- Author: superyyl -- Date: 2014-08-22 17:31:48 -- tool = {} -- float to int function tool.toint(n)     local s = tostring(n)     local i, j = s:find('%.')     if i then         return tonumber(s:sub(1, i-1))     else         return n     end end -- 获取在ccnode屏幕的位置 function tool.getPositionInScreen(node)     if not node then         return 0,0     end     local x,y = node:getPosition()     local parent = node:getParent()     while parent do        local px,py = parent:getPosition()        if not parent:isIgnoreAnchorPointForPosition() then            local anchorPoint = parent:getAnchorPointInPoints()            px = px - anchorPoint.x            py = py - anchorPoint.y        end         x =x + px         y = y + py        parent = parent:getParent()     end     return x, y end -- 检查触摸x,y是否在摸个node上 function tool.checkIfTouch(sprite, x, y)     if not sprite then         return false     end     local px, py = tool.getPositionInScreen(sprite)     local anchorPoint = sprite:getAnchorPointInPoints()     local px = px - anchorPoint.x     local py = py - anchorPoint.y     local w = sprite:getContentSize().width     local h  = sprite:getContentSize().height     local boundingBox = CCRect:new(px,py,w,h)     if boundingBox:containsPoint(ccp(x,y)) then         return true     else         return false     end end --阶乘 function factorial(n)     local result = 1     for i=1,n,1 do         result = result * i     end     return result end --杨辉三角 function pascalTriangleRatio(n,i)     return factorial(n)/(factorial(i)*factorial(n-i)) end --x的y次幂 function powf(x,y)     if y == 0 then return 1 end     local result = 1     for i=1,y,1 do         result = result * x     end     return result end --贝塞尔 --[[     @param p 控制点table     @param t ]] function bezieerat(p,t)     local px,py = 0,0     local n = #p - 1     for i=0,n do         local ratio = pascalTriangleRatio(n,i)         px = px + ratio * p[i+1].x * powf(t,i) * powf(1-t, n-i)         py = py + ratio * p[i+1].y * powf(t,i) * powf(1-t, n-i)     end     return ccp(px,py) end  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61  | 
						-- -- Author: superyyl -- Date: 2014-08-27 11:44:08 -- local General = class("General",function() 	return display.newSprite("res/ui/circle.png") end) General.STATE = { 	IDLE = 0, 	MOVE = 1 } function General:ctor() 	self:registerScriptHandler(function(e) 		if e == "enter" then 			self:onEnter() 		elseif e == "exit" then 			self:onExit() 		end 	end) 	self.state = General.STATE.IDLE end function General:startAction(p,t) 	self.controlPos = {} 	self.totalTime = t 	self.state = General.STATE.MOVE 	for idx,pos in pairs(p) do 		local newPos = ccp(pos.x,pos.y) 		self.controlPos[#self.controlPos+1] = newPos 	end 	self.number = #self.controlPos 	self.t = 0 end function General:update(dt) 	if self.state == General.STATE.MOVE then 		self.t = self.t + dt 		local p = bezieerat(self.controlPos, self.t/self.totalTime) 		self:setPosition(p) 		if self.t > self.totalTime then 			self.state = General.STATE.IDLE 			self:startAction(self.controlPos, self.totalTime) 		end 	end end function General:onEnter() 	self.updateSchedulerEntry = CCDirector:sharedDirector():getScheduler():scheduleScriptFunc(handler(self,self.update), 0, false) end function General:onExit() 	if self.updateSchedulerEntry then 		CCDirector:sharedDirector():getScheduler():unscheduleScriptEntry(self.updateSchedulerEntry) 	end end return General  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159  | 
						-- -- Author: superyyl -- Date: 2014-08-27 10:20:02 -- local TestScene = class("TestScene", function() 	return display.newScene("TestScene") end) function TestScene:ctor() 	self.layer = CCLayerColor:create(ccc4(255, 255, 255, 255)) 	self:addChild(self.layer) 	self.pos = {} 	self.points = {} 	self.layer:registerScriptTouchHandler(handler(self,self.onTouch), false) 	self.layer:setTouchEnabled(true) 	local delete = CCControlButton:create( 		CCLabelTTF:create("删除最后一个节点", "Thonburi", 24), 		CCScale9Sprite:create("res/ui/btn_default_normal.png") 	) 	delete:setPreferredSize(CCSize(200, 80)) 	delete:setPosition(120,display.height-60) 	delete:addHandleOfControlEvent( 		handler(self,self.onDeleteClick), 		CCControlEventTouchUpInside 	) 	self:addChild(delete) end function TestScene:onDeleteClick() 	table.remove(self.pos,#self.pos) 	self:refresh() end function TestScene:onTouch(e,x,y) 	if e == "began" then 		self.touchLocation = ccp(x,y) 		self.dragPoint = nil 		self.dragIndex = nil 		for idx,point in pairs(self.points) do 			if tool.checkIfTouch(point,x,y) then 				self.dragPoint = point 				self.dragIndex = idx 				print("self.dragIndex",self.dragIndex) 			end 		end 		return true 	elseif e == "moved" then 		if self.dragPoint then 			local sprite = self.dragPoint 			local pos = ccp(sprite:getPositionX()+x-self.touchLocation.x,sprite:getPositionY()+y-self.touchLocation.y) 			self.dragPoint:setPosition(pos) 			self.touchLocation = ccp(x,y) 		end 	elseif e == "ended" then 		if self.dragPoint then 			self.pos[self.dragIndex] = ccp(x,y) 			self.dragPoint = nil 			self.dragIndex = nil 			self:refresh() 		else 			local pos = ccp(x,y) 			local distance = ccpDistance(self.touchLocation, pos) 			if distance < 25 then 				self.pos[#self.pos+1] = pos 			end 			self:refresh() 		end 	end end function TestScene:refresh() 	self.layer:removeAllChildrenWithCleanup(true) 	self.points = {} 	for i,pos in pairs(self.pos) do 		local point = CCSprite:create("res/ui/point.png") 		point:setPosition(pos) 		self.layer:addChild(point) 		local label = CCLabelTTF:create(tostring(i), "Thonburi", 20) 		label:setColor(ccc3(0, 0, 0)) 		label:setPosition(point:getContentSize().width/2,point:getContentSize().height+10) 		point:addChild(label) 		self.points[#self.points+1] = point 	end 	if #self.pos > 1 then 		self.general = require("app.models.General").new() 		self.general:setPosition(self.pos[1]) 		self.layer:addChild(self.general) 		self.general:startAction(self.pos, 4) 		local controlPos = {} 		for idx,pos in pairs(self.pos) do 			local newPos = ccp(pos.x,pos.y) 			controlPos[#controlPos+1] = newPos 		end 		local node = CCDrawNode:create() 		self.layer:addChild(node) 		local tracePoints = {} 		for t=0,1,0.001 do 			local point = bezieerat(controlPos, t) 			node:drawDot(point, 2, ccc4f(0, 255, 0, 128)) 			tracePoints[#tracePoints+1] = {} 			tracePoints[#tracePoints].point = point 			tracePoints[#tracePoints].percent = t 		end 		local distance = 0 		for i=1,#tracePoints-1,1 do 			local p1 = tracePoints[i] 			local p2 = tracePoints[i+1] 			local d = ccpDistance(p1.point, p2.point) 			distance = distance + d 		end 		local len = 30 		local pointInfo = {} 		local count = tool.toint(distance / len) 		local mod = distance % len 		len = len + mod / count 		pointInfo[1] = tracePoints[1] 		local remain = len 		for i=1,#tracePoints-1,1 do 			local p1 = tracePoints[i] 			local p2 = tracePoints[i+1] 			local d = ccpDistance(p1.point, p2.point) 			remain = remain - d 			if remain <= 0 then 				pointInfo[#pointInfo+1] = p2 				remain = remain + len 			end 		end 		for idx,p in pairs(pointInfo) do 			local point = CCSprite:create("res/ui/trace.png") 			point:setPosition(p.point) 			print("percent:"..p.percent) 			self.layer:addChild(point) 		end 		print(string.format("长度:%s#%s#%s#%s",distance,count,mod,len)) 	end end return TestScene  | 
					
最后放一张效果图

