实现了一个模拟摇杆
--[[ 虚拟摇杆类 Layer 几种组合方式: 1.固定位置 不自动隐藏 2.固定位置 自动隐藏 3.非固定位置 自动隐藏 Init 初始化 Release 释放 SetEnable 启用 IsEnable 是否启用 --]] local NavigationLayer = class("NavigationLayer",function() return cc.Layer:create() end) -- 8个方向 NavigationLayer.eDirection = { None = 0,U = 1,UR = 2,R = 3,DR = 4,D = 5,DL = 6,L = 7,UL = 8 } NavigationLayer._instance = nil NavigationLayer._touchArea = nil NavigationLayer._touchListener = nil NavigationLayer._downPos = nil NavigationLayer._isEnable = false NavigationLayer._naviCallback = nil NavigationLayer._rootNode = nil NavigationLayer._naviBallNode = nil NavigationLayer._radius = 0 NavigationLayer._naviPosition = nil -- nil时跟随点击位置 NavigationLayer._isAutoHide = true -- 非点击或移动状态自动隐藏 function NavigationLayer.GetInstance() if NavigationLayer._instance == nil then NavigationLayer._instance = NavigationLayer.new() end return NavigationLayer._instance end function NavigationLayer.ReleaseInstance() if NavigationLayer._instance ~= nil then NavigationLayer._instance:Release() NavigationLayer._instance = nil end end function NavigationLayer:ctor() end --[[参数表 isEnable 是否启用 isAutoHide 是否自动隐藏,如果没有固定位置,则此参数无效,按照自动隐藏处理 naviPosition 摇杆位置,nil则跟随点击位置,否则有固定位置 naviCsbName 摇杆csb路径,其中摇杆球需要在根节点下 ballKey 摇杆球的key,用于查找摇杆球 radius 摇杆半径 touchArea 有效触摸区域,在此区域内点击才会处理摇杆操作 naviCallback 方向更改回调,回传角度与8个反向,参考eDirection,角度以右为0,上为90,下为-90 ]] -- 初始化 naviPosition nil则根据点击位置变化 有值则固定位置 function NavigationLayer:Init(isEnable,isAutoHide,naviPosition,naviCsbName,ballKey,radius,touchArea,naviCallback) -- 没有固定位置的 只能是自动隐藏 if naviPosition == nil then isAutoHide = true end -- 加载ui self._rootNode = cc.CSLoader:createNode(naviCsbName) if self._rootNode == nil then print('error load csb!') return false end self._naviBallNode = self._rootNode:getChildByName(ballKey) self._rootNode:setVisible(false) self._naviBallNode:setVisible(true) self._radius = radius self:addChild(self._rootNode,1) self._naviCallback = naviCallback self:SetTouchArea(touchArea) self:SetNaviPosition(naviPosition) self:SetAutoHide(isAutoHide) self:SetEnable(isEnable) if not self:IsAutoHide() then self._rootNode:setVisible(true) end -- 监听触摸 self._touchListener = cc.EventListenerTouchOneByOne:create() self._touchListener:registerScriptHandler(self.onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN) self._touchListener:registerScriptHandler(self.onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED) self._touchListener:registerScriptHandler(self.onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED) self._touchListener:registerScriptHandler(self.onTouchCanceled,cc.Handler.EVENT_TOUCH_CANCELLED) local eventDispatcher = self:getEventDispatcher() eventDispatcher:addEventListenerWithSceneGraPHPriority(self._touchListener,self) return true end -- 释放 function NavigationLayer:Release() if self._touchListener ~= nil then cc.EventDispatcher:removeEventListener(self._touchListener) self._touchListener = nil end if self._naviCallback ~= nil then self._naviCallback = nil end end -- 启用 function NavigationLayer:SetEnable(isEnable) self._isEnable = isEnable end -- 是否启用 function NavigationLayer:IsEnable() return self._isEnable end -- 自动隐藏 function NavigationLayer:SetAutoHide(isAutoHide) self._isAutoHide = isAutoHide end -- 是否自动隐藏 function NavigationLayer:IsAutoHide() return self._isAutoHide end -- 设置位置 function NavigationLayer:SetNaviPosition(naviPosition) self._naviPosition = naviPosition if self._naviPosition ~= nil then self._rootNode:setPosition(self._naviPosition) end end -- 位置是否跟随初始点击位置变动 function NavigationLayer:IsPosCanChange() return (self._naviPosition == nil) end -- 设置触摸区域 function NavigationLayer:SetTouchArea(touchArea) if touchArea ~= nil then self._touchArea = {} self._touchArea.x = touchArea.x self._touchArea.y = touchArea.y self._touchArea.width = touchArea.width self._touchArea.height = touchArea.height else self._touchArea = nil end end -- 触摸操作回调 function NavigationLayer.onTouchBegan(touch,event) local self = NavigationLayer._instance local needNextProcess = false if not self:IsEnable() then return needNextProcess end if self._touchArea ~= nil then local touchPoint = touch:getLocation() if cc.rectContainsPoint(self._touchArea,touchPoint) then -- 需要使用listener的setSwallowTouches,直接使用layer的无效 self._touchListener:setSwallowTouches(true) self:Update(touchPoint,false) print("in area!!!") needNextProcess = true else self._touchListener:setSwallowTouches(false) -- 区域外 考虑不做任何处理 self:Update(nil,false) print("NOT IN AREA") needNextProcess = false end end return needNextProcess end function NavigationLayer.onTouchMoved(touch,event) local self = NavigationLayer._instance local touchPoint = touch:getLocation() self:Update(touchPoint,true) end function NavigationLayer.onTouchEnded(touch,event) local self = NavigationLayer._instance -- local touchPoint = touch:getLocation() self:Update(nil,false) end function NavigationLayer.onTouchCanceled(touch,false) end -- 更新 function NavigationLayer:Update(touchPos,isMove) local direction,angle = self:UpdateData(touchPos,isMove) local isShow = ((not self:IsAutoHide()) or (self._downPos ~= nil)) self:UpdateUI(direction,angle,isShow) -- 回调数据 if self._naviCallback ~= nil then self._naviCallback(direction,angle) end end -- UI更新 function NavigationLayer:UpdateUI(direction,isShow) local ballPos = {x=0,y=0} if isShow then -- 球位置更新 if direction ~= self.eDirection.None then local radians = math.rad(angle) ballPos.x = math.cos(radians)*self._radius ballPos.y = math.sin(radians)*self._radius end self._naviBallNode:setPosition(ballPos) -- 显示更新 if self:IsPosCanChange() then self._rootNode:setPosition(self._downPos) end end self._rootNode:setVisible(isShow) end -- 数据更新 function NavigationLayer:UpdateData(touchPos,isMove) local direction = self.eDirection.None local angle = 0 local isNeedUpdate = false -- 按下 或 弹起 记录触摸点 if not isMove then self._downPos = touchPos -- 如果是非自动隐藏的 点击时也要进行一次位置判定 if not self:IsAutoHide() then isNeedUpdate = true end else -- 移动 更新角度 isNeedUpdate = true end if isNeedUpdate then if self._downPos ~= nil and touchPos ~= nil then local centerPos = self._downPos -- 如果有指定位置 则从根据指定位置算 if not self:IsPosCanChange() then centerPos = self._naviPosition end -- 弧度 然后转 角度 local radians = cc.pToAngleSelf(cc.pSub(touchPos,centerPos)) -- angle = radians*57.29577951 -- ((__ANGLE__) * 57.29577951f) // PI * 180 CC_RADIANS_TO_DEGREES angle = math.deg(radians) direction = self:AngleToDirection(angle) print("angle:"..tostring(angle)) print("direction:"..tostring(direction)) else print("downPos or touchPos is nil!") end end return direction,angle end -- 角度转方向 function NavigationLayer:AngleToDirection(angle) local direction = self.eDirection.None -- -22.5 22.5 67.5 112.5 157.5 -157.5 -112.5 -67.5 -22.5 -- R DR D DL L UL U UR if angle > -22.5 and angle <= 22.5 then direction = self.eDirection.R elseif angle > 22.5 and angle <= 67.5 then direction = self.eDirection.DR elseif angle > 67.5 and angle <= 112.5 then direction = self.eDirection.D elseif angle > 112.5 and angle <= 157.5 then direction = self.eDirection.DL elseif angle > 157.5 or angle <= -157.5 then -- 特殊 direction = self.eDirection.L elseif angle > -157.5 and angle <= -112.5 then direction = self.eDirection.UL elseif angle > -112.5 and angle <= -67.5 then direction = self.eDirection.U elseif angle > -67.5 and angle <= -22.5 then direction = self.eDirection.UR end return direction end return NavigationLayer
使用示例:
lcc.NavigationLayer = require("NavigationLayer")
---[[测试虚拟摇杆 local rootLayer = scene.rootLayer:getChildByName('TestLayer') local touchArea = {x = 0,y = 427,width = 480,height = 427} local naviLayer = lcc.NavigationLayer:GetInstance() naviLayer:Init(true,false,cc.p(240,427),"NavigationNode.csb","NV_BALL",120,scene.onNaviCallback) scene:addChild(naviLayer,200) --]]
3种组合方式,与cocos studio结合使用,需要自己修改也比较方便。
一种效果: