Posted September 02, 2024 by Sir Unknown
#input handling
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:
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.