实现了一个模拟摇杆 --[[虚拟摇杆类 Layer几种组合方式:1.固定位置 不自动隐藏2.固定位置 自动隐藏3.非固定位置 自动隐藏Init 初始化Release 释放SetEnable 启用IsEnable 是否启用--]]local Navi
实现了一个模拟摇杆
--[[
虚拟摇杆类 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, event)
local self = NavigationLayer._instance
-- local touchPoint = touch:getLocation()
self:Update(nil, 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, angle, 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, touchArea, scene.onNaviCallback)
scene:addChild(naviLayer, 200)
--]]
3种组合方式,与cocos studio结合使用,需要自己修改也比较方便。
一种效果:
