quick-cocos2d-x learning series 13 touch

quick-cocos2d-x learning series 13 touch

quick-cocos2d-x learning series 13 touch

 

Nowadays, smartphones are basically touch screens, except for keyboard enthusiasts. We need to control the entire game logic through this small touch screen, and we need to control this big palm area in detail.

 

1. Single touch test

Create sprite function

function createTouchableSprite (p)

    local sprite = display .newScale9Sprite(p.image)

    sprite:setContentSize(p.size)

 

    local cs = sprite:getContentSize()

    local label = cc .ui.UILabel.new({

            UILabelType = 2,

            text = p.label,

            color = p.labelColor})

    label:align( display .CENTER)

    label:setPosition(cs.width/2, label:getContentSize().height)

    sprite:addChild(label)

    sprite.label = label

 

    return sprite

end

BOX box

function drawBoundingBox (parent, target, color)

    local cbb = target:getCascadeBoundingBox()

    local left, bottom, width, height = cbb.origin.x, cbb.origin.y, cbb.size.width, cbb.size.height

    local points = {

        {left, bottom},

        {left + width, bottom},

        {left + width, bottom + height},

        {left, bottom + height},

        {left, bottom},

    }

    local box = display .newPolygon(points, {borderColor = color})

    parent:addChild(box, 1000)

end

1.1 Responding to touch events

Call this function:

self.sprite = createTouchableSprite ({

            image = "WhiteButton.png",

            size = cc .size(500, 300),

            label = "TOUCH ME !",

            labelColor = cc .c3b(255, 0, 0)})

        :pos( display .cx, display .cy)

        :addTo(self)

    drawBoundingBox (self, self.sprite, cc .c4f(0, 1.0, 0, 1.0))

- Enable touch

self.sprite:setTouchEnabled( true )

self.sprite:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        - event.name is the status of the touch event: began, moved, ended, cancelled

        - event.x, event.y is the current position of the touch point

        - event.prevX, event.prevY is the position before the touch point

        local label = string .format("sprite: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.sprite.label:setString(label)

 

        - Return true to indicate that you want to respond to the touch event and continue to receive the state change of the touch event

        return true

    end )

Single-point touch is the most direct way to use it.

 

1.2 Event penetration and event capture

-Create the bottom touch layer

self.parentButton = createTouchableSprite ({

            image = "WhiteButton.png",

            size = cc .size(600, 500),

            label = "TOUCH ME !",

            labelColor = cc .c3b(255, 0, 0)})

        :pos( display .cx, display .cy)

        :addTo(self)

    self.parentButton.name = "parentButton"

    drawBoundingBox (self, self.parentButton, cc .c4f(0, 1.0, 0, 1.0))

self.parentButton:setTouchEnabled( true )

--Add monitoring to the touch layer

self.parentButton:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("parentButton: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.parentButton.label:setString(label)

        return true

    end )

 

 

--Create button1 at the bottom, button1 will swallow touch events after responding to touch

    self.button1 = createTouchableSprite ({

            image = "GreenButton.png",

            size = cc .size(400, 160),

            label = "TOUCH ME !"})

        :pos(300, 400)

        :addTo(self.parentButton)

 

    cc .ui.UILabel.new({text = "SWALLOW = YES\nThe event is swallowed after the current object is processed", size = 24})

        :align( display .CENTER, 200, 90)

        :addTo(self.button1)

    drawBoundingBox (self, self.button1, cc .c4f(1.0, 0, 0, 1.0))

 

self.button1:setTouchEnabled( true )

self.button1:setTouchSwallowEnabled( true )-- Whether to swallow the event, the default value is true

    self.button1:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("button1: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.button1.label:setString(label)

        return true

    end )

 

 

 

- Create button2 at the bottom, after responding to touch, it won't swallow up touch events

    self.button2 = createTouchableSprite ({

            image = "PinkButton.png",

            size = cc .size(400, 160),

            label = "TOUCH ME !"})

        :pos(300, 200)

        :addTo(self.parentButton)

    cc .ui.UILabel.new({text = "SWALLOW = NO\nThe event will be passed to the underlying object", size = 24})

        :align( display .CENTER, 200, 90)

        :addTo(self.button2)

    drawBoundingBox (self, self.button2, cc .c4f(0, 0, 1.0, 1.0))

 

    self.button2:setTouchEnabled( true )

    self.button2:setTouchSwallowEnabled( false ) - When the event is not swallowed, the touch event will be passed from the upper object to the lower object, which is called "penetration"

    self.button2:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("button1: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.button2.label:setString(label)

        return true

    end )

 

Event penetration is achieved through the function setTouchSwallowEnabled.

The following function sets whether to capture touch

self.parentButton:setTouchCaptureEnabled(button:isButtonSelected())

 

1.3 Decide whether to accept the event in the event capture phase

- This flag variable is used to decide whether to accept the event in the touch event capture phase

    self.isTouchCaptureEnabled_ = true

 

    --parentButton is the parent node of button1

    self.parentButton = createTouchableSprite ({

            image = "WhiteButton.png",

            size = cc .size(600, 500),

            label = "TOUCH ME !",

            labelColor = cc .c3b(255, 0, 0)})

        :pos( display .cx, display .cy)

        :addTo(self)

    drawBoundingBox (self, self.parentButton, cc .c4f(0, 1.0, 0, 1.0))

 

    self.parentButton.label2 = cc .ui.UILabel.new({text = "", size = 24, color = cc .c3b(0, 0, 255)})

        :align( display .CENTER, 300, 60)

        :addTo(self.parentButton)

 

    self.parentButton:setTouchEnabled( true )

    self.parentButton:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("parentButton: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.parentButton.label:setString(label)

        printf ("%s %s [TARGETING]", "parentButton", event.name)

        if event.name == "ended" or event.name == "cancelled" then

            print ("-----------------------------")

        else

            print ("")

        end

        return true

    end )

 

    - Can dynamically capture touch events, and decide whether to accept the event when the capture of the touch event starts

    self.parentButton:addNodeEventListener( cc .NODE_TOUCH_CAPTURE_EVENT, function (event)

        if event.name == "began" then

            print ("-----------------------------")

        end

 

        local label = string .format("parentButton CAPTURE: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.parentButton.label2:setString(label)

        printf ("%s %s [CAPTURING]", "parentButton", event.name)

        if event.name == "began" or event.name == "moved" then

            return self.isTouchCaptureEnabled_

        end

    end )

 

    - button1 will swallow the touch event after responding to the touch

    self.button1 = createTouchableSprite ({

            image = "GreenButton.png",

            size = cc .size(400, 160),

            label = "TOUCH ME !"})

        :pos(300, 400)

        :addTo(self.parentButton)

    cc .ui.UILabel.new({text = "SWALLOW = YES\nThe event is swallowed after the current object is processed", size = 24})

        :align( display .CENTER, 200, 90)

        :addTo(self.button1)

    drawBoundingBox (self, self.button1, cc .c4f(1.0, 0, 0, 1.0))

 

    self.button1:setTouchEnabled( true )

    self.button1:setTouchSwallowEnabled( true ) - Whether to swallow the event, the default value is true

    self.button1:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("button1: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.button1.label:setString(label)

        printf ("%s %s [TARGETING]", "button1", event.name)

        if event.name == "ended" or event.name == "cancelled" then

            print ("-----------------------------")

        else

            print ("")

        end

        return true

    end )

 

    - button2 will not swallow up touch events after responding to touch

    self.button2 = createTouchableSprite ({

            image = "PinkButton.png",

            size = cc .size(400, 160),

            label = "TOUCH ME !"})

        :pos(300, 200)

        :addTo(self.parentButton)

    cc .ui.UILabel.new({text = "SWALLOW = NO\nThe event will be passed to the underlying object", size = 24})

        :align( display .CENTER, 200, 90)

        :addTo(self.button2)

    drawBoundingBox (self, self.button2, cc .c4f(0, 0, 1.0, 1.0))

 

    self.button2:setTouchEnabled( true )

    self.button2:setTouchSwallowEnabled( false ) - When the event is not swallowed, the touch event will be passed from the upper object to the lower object, which is called "penetration"

    self.button2:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("button1: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.button2.label:setString(label)

        printf ("%s %s [TARGETING]", "button2", event.name)

        return true

    end )

 

    - Even if the parent object prevents the response to the event during the capture phase, the child object can still capture the event, but the event will not be triggered

    self.button2:addNodeEventListener( cc .NODE_TOUCH_CAPTURE_EVENT, function (event)

        printf ("%s %s [CAPTURING]", "button2", event.name)

        return true

    end )

 

    - Place a switch button on the screen

    local labels = {}

    labels[ true ] = "The parent object [can] capture touch events"

    labels[ false ] = "The parent object [cannot] capture touch events"

    local images = {on = "CheckBoxButton2On.png", off = "CheckBoxButton2Off.png"}

    self.captureEnabledButton = cc .ui.UICheckBoxButton.new(images)

        :setButtonLabel( cc .ui.UILabel.new({text = labels[ true ], size = 24}))

        :setButtonLabelOffset(40, 0)

        :setButtonSelected( true )

        :onButtonStateChanged( function (event)

            local button = event.target

            button:setButtonLabelString(labels[button:isButtonSelected()])

        end )

        :onButtonClicked( function (event)

            local button = event.target

            self.isTouchCaptureEnabled_ = button:isButtonSelected()

        end )

        :pos( display .cx-160, display .top- 80)

        :addTo(self)

 

    cc .ui.UILabel.new({

        text = "Event processing flow:\n1. [Capture] Phase: From parent to child\n2. [Target] Phase\n3. [Transfer] Phase: Try to pass to the lower object",

        size= 24})

        : align = left ( the display .CENTER_TOP, the display .CX, the display .top - 120)

        :addTo(self)

 

 

Among them, NODE_TOUCH_EVENT and NODE_TOUCH_CAPTURE_EVENT represent two events.

Return true or false in the processing function of NODE_TOUCH_CAPTURE_EVENT, and then decide whether to call the processing function of NODE_TOUCH_EVENT.

1.4 The touch area of the container is determined by the child object

Create a node, add several sprites to the node, and the touch range is determined by the sprite's local area.

 

- touchableNode is the touch-enabled Node

    self.touchableNode = display .newNode()

    self.touchableNode:setPosition( display .cx, display .cy)

    self:addChild(self.touchableNode)

 

    - Add some sprites to touchableNode

    local count = math .random(3, 8)

    local images = {"WhiteButton.png", "BlueButton.png", "GreenButton.png", "PinkButton.png"}

    for i = 1, count do

        local sprite = display .newScale9Sprite(images[ math .random(1, 4)])

        sprite:setContentSize( cc .size( math .random(100, 200), math .random(100, 200)))

        sprite:setPosition( math .random(-200, 200), math .random(-200, 200))

        self.touchableNode:addChild(sprite)

    end

 

    self.stateLabel = cc .ui.UILabel.new({text = ""})

    self.stateLabel:align( display .CENTER, display .cx, display .top -100)

    self:addChild(self.stateLabel)

 

    - Enable touch

    self.touchableNode:setTouchEnabled( true )

    - Add touch event handler

    self.touchableNode:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("touchableNode: %sx,y: %0.2f, %0.2f", event.name, event.x, event.y)

        self.stateLabel:setString(label)

        return true

    end )

    drawBoundingBox (self, self.touchableNode, cc .c4f(0, 1.0, 0, 1.0))

 

 

2. Multi-touch

 

2.1 Responding to touch events

 

 

 

- createTouchableSprite() is defined in includes/functions.lua

    self.sprite = createTouchableSprite ({

            image = "WhiteButton.png",

            size = cc .size(500, 600),

            label = "TOUCH ME !",

            labelColor = cc .c3b(255, 0, 0)})

        :pos( display .cx, display .cy)

        :addTo(self)

    drawBoundingBox (self, self.sprite, cc .c4f(0, 1.0, 0, 1.0))

 

    local labelPoints = cc .ui.UILabel.new({text = "", size = 24})

        : align = left ( the display .CENTER_TOP, the display .CX, the display .top - 120)

        :addTo(self)

 

    - Enable touch

    self.sprite:setTouchEnabled( true )

    - Set touch mode

    self.sprite:setTouchMode( cc .TOUCH_MODE_ALL_AT_ONCE)-- Multipoint

    --self.sprite:setTouchMode(cc.TOUCH_MODE_ONE_BY_ONE) - Single point (default mode)

    - Add touch event handler

    self.sprite:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        - event.name is the status of the touch event: began, moved, ended, cancelled, added (multi-touch only), removed (multi-touch only)

        - event.points contains all touch points, organized according to the structure of events.point[id] = {x = ?, y = ?}

        local str = {}

        for id, point in pairs (event.points) do

            str[#str + 1] = string .format("id: %s, x: %0.2f, y: %0.2f", point.id, point.x, point.y)

        end

        local pointsCount = #str

        table .sort(str)

        labelPoints:setString( table .concat(str, "\n"))

 

        if event.name == "began" or event.name == "added" then

            self.touchIndex = self.touchIndex + 1

            for id, point in pairs (event.points) do

                local cursor = display .newSprite("Cursor.png")

                    :pos(point.x, point.y)

                    :scale(1.2)

                    :addTo(self)

                self.cursors[id] = cursor

            end

        elseif event.name == "moved" then

            for id, point in pairs (event.points) do

                local cursor = self.cursors[id]

                local rect = self.sprite:getBoundingBox()

                if cc .rectContainsPoint(rect, cc .p(point.x, point.y)) then

                    - Check whether the position of the touch point is within the rectangle

                    cursor:setPosition(point.x, point.y)

                    cursor:setVisible( true )

                else

                    cursor:setVisible( false )

                end

            end

        elseif event.name == "removed" then

            for id, point in pairs (event.points) do

                self.cursors[id]:removeSelf()

                self.cursors[id] = nil

            end

        else

            for _, cursor in pairs (self.cursors) do

                cursor:removeSelf()

            end

            self.cursors = {}

        end

 

        local label = string .format("sprite: %s, count = %d, index = %d", event.name, pointsCount, self.touchIndex)

        self.sprite.label:setString(label)

 

        if event.name == "ended" or event.name == "cancelled" then

           self.sprite.label:setString("")

            labelPoints:setString("")

        end

 

        - Return true to indicate that you want to respond to the touch event and continue to receive the state change of the touch event

        return true

    end )

 

    cc .ui.UILabel.new({

        text = "After registering multi-touch, the target will receive the data of all touch points\nadded and removed indicating the addition and removal of touch points",

        size= 24})

        : align = left ( Run the display .CENTER, Run the display .CX, Run the display .Top - 80)

        :addTo(self)

 

2.2 Decide whether to accept the event in the event capture phase

- This flag variable is used to decide whether to accept the event in the touch event capture phase

    self.isTouchCaptureEnabled_ = true

 

    --parentButton is the parent node of button1

    self.parentButton = createTouchableSprite ({

            image = "WhiteButton.png",

            size = cc .size(600, 500),

            label = "TOUCH ME !",

            labelColor = cc .c3b(255, 0, 0)})

        :pos( display .cx, display .cy)

        :addTo(self)

    drawBoundingBox (self, self.parentButton, cc .c4f(0, 1.0, 0, 1.0))

 

    self.parentButton.label2 = cc .ui.UILabel.new({text = "", size = 24, color = cc .c3b(0, 0, 255)})

        :align( display .CENTER, 300, 60)

        :addTo(self.parentButton)

 

    self.parentButton:setTouchEnabled( true )

    self.parentButton:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("parentButton: %s", event.name)

        self.parentButton.label:setString(label)

        printf ("%s %s [TARGETING]", "parentButton", event.name)

        if event.name == "ended" or event.name == "cancelled" then

            print ("-----------------------------")

        else

            print ("")

        end

        return true

    end )

 

    - Can dynamically capture touch events, and decide whether to accept the event when the capture of the touch event starts

    self.parentButton:addNodeEventListener( cc .NODE_TOUCH_CAPTURE_EVENT, function (event)

        if event.name == "began" then

            print ("-----------------------------")

        end

 

        local label = string .format("parentButton CAPTURE: %s", event.name)

        self.parentButton.label2:setString(label)

        printf ("%s %s [CAPTURING]", "parentButton", event.name)

        if event.name == "began" or event.name == "moved" then

            return self.isTouchCaptureEnabled_

        end

    end )

 

    - button1 will swallow the touch event after responding to the touch

    self.button1 = createTouchableSprite ({

            image = "GreenButton.png",

            size = cc .size(400, 160),

            label = "TOUCH ME !"})

        :pos(300, 400)

        :addTo(self.parentButton)

    cc .ui.UILabel.new({text = "SWALLOW = YES\nThe event is swallowed after the current object is processed", size = 24})

        :align( display .CENTER, 200, 90)

        :addTo(self.button1)

    drawBoundingBox (self, self.button1, cc .c4f(1.0, 0, 0, 1.0))

 

    self.button1:setTouchEnabled( true )

    self.button1:setTouchMode( cc .TOUCH_MODE_ALL_AT_ONCE)-- Multipoint

    self.button1:setTouchSwallowEnabled( true ) - Whether to swallow the event, the default value is true

    self.button1:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("button1: %s count: %d", event.name, table .nums(event.points))

        self.button1.label:setString(label)

        printf ("%s %s [TARGETING]", "button1", event.name)

        if event.name == "ended" or event.name == "cancelled" then

            print ("-----------------------------")

        else

            print ("")

        end

        return true

    end )

 

    - button2 will not swallow up touch events after responding to touch

    self.button2 = createTouchableSprite ({

            image = "PinkButton.png",

            size = cc .size(400, 160),

            label = "TOUCH ME !"})

        :pos(300, 200)

        :addTo(self.parentButton)

    cc .ui.UILabel.new({text = "SWALLOW = NO\nThe event will be passed to the underlying object", size = 24})

        :align( display .CENTER, 200, 90)

        :addTo(self.button2)

    drawBoundingBox (self, self.button2, cc .c4f(0, 0, 1.0, 1.0))

 

    self.button2:setTouchEnabled( true )

    self.button2:setTouchMode( cc .TOUCH_MODE_ALL_AT_ONCE)-- Multipoint

    self.button2:setTouchSwallowEnabled( false ) - When the event is not swallowed, the touch event will be passed from the upper object to the lower object, which is called "penetration"

    self.button2:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local label = string .format("button1: %s count: %d", event.name, table .nums(event.points))

        self.button2.label:setString(label)

        printf ("%s %s [TARGETING]", "button2", event.name)

        return true

    end )

 

    - Even if the parent object prevents the response to the event during the capture phase, the child object can still capture the event, but the event will not be triggered

    self.button2:addNodeEventListener( cc .NODE_TOUCH_CAPTURE_EVENT, function (event)

        printf ("%s %s [CAPTURING]", "button2", event.name)

        return true

    end )

 

    - Place a switch button on the screen

    local labels = {}

    labels[ true ] = "The parent object [can] capture touch events"

    labels[ false ] = "The parent object [cannot] capture touch events"

    local images = {on = "CheckBoxButton2On.png", off = "CheckBoxButton2Off.png"}

    self.captureEnabledButton = cc .ui.UICheckBoxButton.new(images)

        :setButtonLabel( cc .ui.UILabel.new({text = labels[ true ], size = 24}))

        :setButtonLabelOffset(40, 0)

        :setButtonSelected( true )

        :onButtonStateChanged( function (event)

            local button = event.target

            button:setButtonLabelString(labels[button:isButtonSelected()])

        end )

        :onButtonClicked( function (event)

            local button = event.target

            self.isTouchCaptureEnabled_ = button:isButtonSelected()

        end )

        :pos( display .cx-160, display .top- 80)

        :addTo(self)

 

    cc .ui.UILabel.new({

        text = "Event processing flow:\n1. [Capture] Phase: From parent to child\n2. [Target] Phase\n3. [Transfer] Phase: Try to pass to the lower object",

        size= 24})

        : align = left ( the display .CENTER_TOP, the display .CX, the display .top - 120)

        :addTo(self)

 

 

 

 

2.3 The touch area of the container is determined by the child object

- touchableNode is the touch-enabled Node

    self.touchableNode = display .newNode()

    self.touchableNode:setPosition( display .cx, display .cy)

    self:addChild(self.touchableNode)

 

    - Add some sprites to touchableNode

    local count = math .random(3, 8)

    local images = {"WhiteButton.png", "BlueButton.png", "GreenButton.png", "PinkButton.png"}

    for i = 1, count do

        local sprite = display .newScale9Sprite(images[ math .random(1, 4)])

        sprite:setContentSize( cc .size( math .random(100, 200), math .random(100, 200)))

        sprite:setPosition( math .random(-200, 200), math .random(-200, 200))

        self.touchableNode:addChild(sprite)

    end

 

    self.stateLabel = cc .ui.UILabel.new({text = ""})

    self.stateLabel:align( display .CENTER, display .cx, display .top -100)

    self:addChild(self.stateLabel)

 

    - Enable touch

    self.touchableNode:setTouchEnabled( true )

    self.touchableNode:setTouchMode( cc .TOUCH_MODE_ALL_AT_ONCE)-- Multipoint

   - Add touch event handler

    self.touchableNode:addNodeEventListener( cc .NODE_TOUCH_EVENT, function (event)

        local str = {}

        for id, point in pairs (event.points) do

            str[#str + 1] = string .format("id: %s, x: %0.2f, y: %0.2f", point.id, point.x, point.y)

        end

        self.stateLabel:setString( table .concat(str, "\n"))

        return true

    end )

    drawBoundingBox (self, self.touchableNode, cc .c4f(0, 1.0, 0, 1.0))

 

    -

    app :createNextButton(self)

    app :createTitle(self, "Multi-touch test-the touch area of the container is determined by the child")