Orientation based device input


Since this is a relatively simple game, I wanted it to be accessible across all devices, which means enabling smartphone inputs. A smartphone has no buttons, and I didn't want to put simulated buttons on the screen to obscure the gameplay, so I decided to receive input from the myriad sensors that a modern smartphone has. However, receiving orientation data from the web browser in a smartphone is another beast entirely.

To simplify, I will only target an updated Chrome browser on an Android. The attempt is to implement as much of the input logic in Javascript as possible so that the main game loop does not need to adapt to different input paradigms. This will enable the same code to be built for different platforms, and a conditional check will ensure that the orientation input is only considered if built for HTML5. The strategy is as follows:

  1. Record the initial orientation of the device, and set it as 0.
  2. Set up an Event Listener that listens to changes in orientation.
  3. Whenver an orientation change event is triggered, calculate the distance from the initial orientation, and accelerate the Player accordingly.

Step 2 and 3 are easy, and can be set up as follows:


var Javascript = JavaScriptBridge
func _ready():
    if !OS.has_feature('web'): return
    Javascript.eval("""
        var initOrient = {x: 0., y: 0.}
        var orient = {x: 0., y: 0.}
        function registerMotionListener() {
            window.ondeviceorientation = function(event) {
                orient.x = event.gamma - initOrient.x
                orient.y = 2.5*(event.beta - initOrient.y)
            }
        }
        if (typeof DeviceOrientationEvent.requestPermission === 'function') {
            DeviceOrientationEvent.requestPermission().then(function(state) {
                if (state === 'granted') registerMotionListener()
            })
        }
        else {
            registerMotionListener()
        }
    """, true)
func get_orientation() -> Vector2:
    if !OS.has_feature('web'): return Vector2.ZERO
    
    return Vector2(Javascript.eval('orient.x'), Javascript.eval('orient.y'))

The event.gamma and event.beta variables return the tilt along the x and y axes respectively, so the Vector2 returned by get_orientation() can be added to the usual Input axis to encapsulate all possible inputs that the Player object can receive. Rest of the code can use the overall Input axis as usual.

However, setting the initial orientation values is not so simple. Since JS does not allow you to synchronously pause the execution of a program until an event is received, my solution (inspired by this StackOverflow question) was to add a Boolean to the initOrient dictionary called freeze set to false, and set it to true the first time an event is received. Further executions will simply check this freeze variable and calculate the distance from initOrient if it is set to true. The registerMotionListener function then changes to

function registerMotionListener() {
    window.ondeviceorientation = function(event) {
        if(!initOrient.freeze) {
            initOrient.x = event.gamma
            initOrient.y = event.beta
            initOrient.freeze = true
        }
        orient.x = event.gamma - initOrient.x
        orient.y = 2.5*(event.beta - initOrient.y)
    }
}

This seems to work.

Get Size Matters in Space

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.