nipplejs: Joystick gets stuck
I’m using Nipple.js on two separate transparent elements, overlaid on top of an HTML canvas - a ‘left’ and ‘right’ element. This allows me to use two joysticks for a ‘twin stick shooter’ style game.
I’m having a lot of trouble determining when this actually happens, but it is happening quite frequently: the right joystick “locks up”, frozen at the max extent of its bounds, and cannot be released without a page refresh. The left joystick never seems to lock up (it is defined first in my code).
Part of the difficulty in debugging this is that it only happens when using a phone to visit the website, and as such my debugging tools (namely access to the browser console) are practically non-existent. In the browser, I cannot reproduce the bug.
It also seems to happen only when there are 2 joysticks active at once. If only 1 joystick is used (despite both having been instantiated), the problem doesn’t seem to surface.
Any ideas? I’m digging through your code to see if I can spot where this bug might come from, but figured I would bring it up in case you are able to spot the issue more easily.
Might not be relevant, but here’s my usage:
function initializeMovementJoystick(){
movementJoystick = NippleJS.create({zone: leftDiv});
var currentOctant = null;
var octants = new Array(8);
octants[0] = function(state){InputHandler.setInputState("strafeRight", state);};
octants[1] = function(state){InputHandler.setInputState("strafeRight", state);InputHandler.setInputState("forward", state);};
octants[2] = function(state){InputHandler.setInputState("forward", state);};
octants[3] = function(state){InputHandler.setInputState("strafeLeft", state);InputHandler.setInputState("forward", state);};
octants[4] = function(state){InputHandler.setInputState("strafeLeft", state);};
octants[5] = function(state){InputHandler.setInputState("strafeLeft", state);InputHandler.setInputState("backward", state);};
octants[6] = function(state){InputHandler.setInputState("backward", state);};
octants[7] = function(state){InputHandler.setInputState("strafeRight", state);InputHandler.setInputState("backward", state);};
movementJoystick.on('end move', function(event, data){
if (event.type === 'move'){
if(data.distance < 15){
if (currentOctant !== null){
//un-fire current
octants[currentOctant](false);
currentOctant = null;
}
} else {
let angle = data.angle.degree;
let octant = Math.floor(((angle + 22.5) % 360) / 45.0);
if(octant !== currentOctant){
if(currentOctant !== null){
//changed from one to another, un-fire last
octants[currentOctant](false);
}
//fire current
octants[octant](true);
currentOctant = octant;
}
}
} else if (event.type === 'end'){
if (currentOctant !== null){
octants[currentOctant](false);
currentOctant = null;
}
}
//console.log(event);
//console.log(data);
});
}
function initializeRotationJoystick(){
rotationJoystick = NippleJS.create({zone: rightDiv});
var currentOctant = null;
var octants = new Array(8);
octants[0] = function(state){InputHandler.setInputState("turnRight", state);};
octants[1] = function(state){InputHandler.setInputState("turnRight", state);InputHandler.setInputState("boost", state);};
octants[2] = function(state){InputHandler.setInputState("boost", state);};
octants[3] = function(state){InputHandler.setInputState("turnLeft", state);InputHandler.setInputState("boost", state);};
octants[4] = function(state){InputHandler.setInputState("turnLeft", state);};
octants[5] = function(state){InputHandler.setInputState("turnLeft", state);InputHandler.setInputState("shoot", state);};
octants[6] = function(state){InputHandler.setInputState("shoot", state);};
octants[7] = function(state){InputHandler.setInputState("turnRight", state);InputHandler.setInputState("shoot", state);};
rotationJoystick.on('end move', function(event, data){
if (event.type === 'move'){
if (useRelativeOrientation){
InputHandler.setInputState("targetAngle", data.angle.degree);
} else if(data.distance < 15){
if (currentOctant !== null){
//un-fire current
octants[currentOctant](false);
currentOctant = null;
}
} else {
let angle = data.angle.degree;
let octant = Math.floor(((angle + 22.5) % 360) / 45.0);
if(octant !== currentOctant){
if(currentOctant !== null){
//changed from one to another, un-fire last
octants[currentOctant](false);
}
//fire current
octants[octant](true);
currentOctant = octant;
}
}
} else if (event.type === 'end'){
if (currentOctant !== null){
octants[currentOctant](false);
currentOctant = null;
}
}
//console.log(event);
//console.log(data);
});
}
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 15 (7 by maintainers)
Commits related to this issue
- fix: add touchcancel handling as touchend for events coming from outside Fix #61 #57 #33 #31 #30 — committed to yoannmoinet/nipplejs by yoannmoinet 7 years ago
I ended up getting stuck on this. It probably has something to do with
event.preventDefault()
or returning false from an event handler, but I couldn’t figure out what handler was causing the issue. Most of the occurrences of this issue can be solved by settingfadeTime: 0
so I’m going to stick with that for now.I’ve done some more research today. It looks like the second
touchend
is not fired on mobile Safari or Mobile Firefox on Android when you do a slow double tap. This causes the second nipple element to get stuck.I’m seeing this as well and can replicate only on iOS devices, Android seems to work fine.
I’ve added a console log to Super.trigger and I’m seeing the following output (you’re going to want to paste this into an editor to make any sense of it 😅):
It looks as though the
nipple_0_0
element is added, end is called,nipple_0_1
is added,nipple_0_0
is removed, and thennipple_0_1
has it’s shown event fired and is stuck.@yoannmoinet couldn’t reproduce bug with dataOnly param set to true. Could it be dom manipulation related issue?