0
\$\begingroup\$

I am diving into libgdx and used their sample game as a starting point.

I also added a touchpad to move the bucket. That was kind of easy and worked flawless. My additional idea was to make the touchpad disappear when not touching/clicking the screen and be shown again once you touch it. Basically to give the player the freedom to decide where he wants to have the touchpad located on the screen.

Watching this recording it works pretty good but obviously I don't want to make it follow the mouse constantly. So I tried to make the touchpad (which is an actor on my stage) invisible. As soon as I do that, I am losing the touch event being forwarded to the touchpad. See this recording.

My initial idea was to not have the pad constantly follow the mouse pointer (as I want to support touch screens at some point too). So the ideal approach I tried but failed to implement was to add the touchpad on any "down" event and and "move" event afterwards is dealt by the touchpad but that does not work. The stage keeps the primary target of the input event and the touchpad is not informed about anything. I tried it with an InputMultiplexer but failed with it as well...

Any ideas?

My main class:

import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.InputListener
import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.ui.Touchpad
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable
import com.badlogic.gdx.utils.ScreenUtils


class MyGame : ApplicationAdapter() {
    private lateinit var pad: Touchpad
    private lateinit var stage: Stage
    private lateinit var camera: OrthographicCamera

    override fun create() {
        // create the camera and the SpriteBatch
        camera = OrthographicCamera().apply {
            setToOrtho(false, 800f, 480f)
        }

        stage = Stage()
        stage.addListener(object : InputListener() {
            override fun touchDown(event: InputEvent?, x: Float, y: Float, pointer: Int, button: Int): Boolean {
                println("tdown")
                pad.isVisible = true
                return super.touchDown(event, x, y, pointer, button)
            }

            override fun mouseMoved(event: InputEvent?, x: Float, y: Float): Boolean {
                println("mmoved")
                if (!pad.isVisible) {
                    pad.x = x - pad.width / 2f
                    pad.y = y - pad.height / 2f
                }
                return super.mouseMoved(event, x, y)
            }

            override fun touchUp(event: InputEvent?, x: Float, y: Float, pointer: Int, button: Int) {
                println("tup")
                pad.isVisible = false
                super.touchUp(event, x, y, pointer, button)
            }
        })
        Gdx.input.inputProcessor = stage;

        val padBackground = TextureRegionDrawable(Texture(Gdx.files.internal("pad_circle.png")))
        val padThumb = TextureRegionDrawable(Texture(Gdx.files.internal("pad_thumb.png")))
        pad = Touchpad(10f, Touchpad.TouchpadStyle(padBackground, padThumb))
        pad.addListener(object : InputListener() {
            override fun enter(event: InputEvent?, x: Float, y: Float, pointer: Int, fromActor: Actor?) {
                println("pad enter")
                pad.isVisible = true
                super.enter(event, x, y, pointer, fromActor)
            }

            override fun exit(event: InputEvent?, x: Float, y: Float, pointer: Int, toActor: Actor?) {
                pad.isVisible = false
                println("pad exit")
                super.exit(event, x, y, pointer, toActor)
            }
        })
        pad.isVisible = false
        stage.addActor(pad)
    }

    override fun render() {
        ScreenUtils.clear(0f, 0f, 0.2f, 1f)
        camera.update()

        stage.act(Gdx.graphics.deltaTime)
        stage.draw()
    }

    override fun dispose() {
    }
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

I found the solution to my problem: InputMultiplexer (but used correctly :D )

My create() method in my main class now looks like this:

override fun create() {
    // create the camera and the SpriteBatch
    camera = OrthographicCamera().apply {
        setToOrtho(false, 800f, 480f)
    }

    stage = Stage()

    val padBackground = TextureRegionDrawable(Texture(Gdx.files.internal("pad_circle.png")))
    val padThumb = TextureRegionDrawable(Texture(Gdx.files.internal("pad_thumb.png")))
    pad = EnhancedTouchPad(10f, Touchpad.TouchpadStyle(padBackground, padThumb))
    stage.addActor(pad)

    val inputMultiplexer = InputMultiplexer()
    inputMultiplexer.addProcessor(0, pad.inputProcessor)
    inputMultiplexer.addProcessor(1, stage)
    Gdx.input.inputProcessor = inputMultiplexer
}

I also created my own Touchpad child class (mainly to organize my code better).

Anyway, with the multiplexer in place, it will basically call the InputProcessor correctly but (and I do not know the reason for that) it will not call any InputListener that is internally added inside the Touchpad. So what I had to do was to "forward" the touchDragged event manually from within the InputProcessor, see the fireDrag(...) method

class EnhancedTouchPad : Touchpad {
    constructor(deadzoneRadius: Float, skin: Skin) : super(deadzoneRadius, skin)
    constructor(deadzoneRadius: Float, skin: Skin, styleName: String) : super(deadzoneRadius, skin, styleName)
    constructor(deadzoneRadius: Float, style: TouchpadStyle) : super(deadzoneRadius, style)

    init {
        isVisible = false
    }

    val inputProcessor = object : InputProcessor {
        override fun keyDown(keycode: Int) = false

        override fun keyUp(keycode: Int) = false

        override fun keyTyped(character: Char) = false

        override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
            if (debug) println("touchDown")
            isVisible = true
            val touchPos = Vector3(screenX.toFloat(), screenY.toFloat(), 0f)
            stage.camera.unproject(touchPos)
            setPosition(touchPos.x - width / 2, touchPos.y - height / 2)
            fireDrag(touchPos.x, touchPos.y)
            return true
        }

        override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
            if (debug) println("touchUp")
            isVisible = false
            return true
        }

        override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
            if (debug) println("touchDragged")
            val touchPos = Vector3(screenX.toFloat(), screenY.toFloat(), 0f)
            stage.camera.unproject(touchPos)
            fireDrag(touchPos.x, touchPos.y)
            return true
        }

        override fun mouseMoved(screenX: Int, screenY: Int) = false

        override fun scrolled(amountX: Float, amountY: Float) = false
    }

    fun fireDrag(eventX: Float, eventY: Float) {
        val inputEvent = InputEvent()
        inputEvent.type = InputEvent.Type.touchDragged
        inputEvent.stageX = eventX
        inputEvent.stageY = eventY
        this.fire(inputEvent)
    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .