【cocos2dx 3.3 lua】07 模拟摇杆

实现了一个模拟摇杆

--[[
虚拟摇杆类 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结合使用,需要自己修改也比较方便。

一种效果

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...