Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Joystick gets stuck #31

Closed
bfishman opened this issue May 29, 2016 · 15 comments
Closed

Joystick gets stuck #31

bfishman opened this issue May 29, 2016 · 15 comments

Comments

@bfishman
Copy link

bfishman commented May 29, 2016

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);

    });
}
@yoannmoinet
Copy link
Owner

yoannmoinet commented May 29, 2016

Thanks for reporting it, I'll look into this.
Do you have it hosted somewhere I can test it?

@bfishman
Copy link
Author

I will message you privately.

@yoannmoinet
Copy link
Owner

Ok, so after testing it, I might have found a way to reproduce it.

I was able to reproduce the phenomenon by activating the right joystick, while playing with it, rotate the phone.
Then release your finger, and the joystick will stay activated.
After that, if you active the left joystick, both will tracked the same finger.
Release, and only the right one will de-activate, and the right one will stay stuck for ever.

Is it the behavior you were having?

It could be related to #30. This looks like an edge case, but definitely a bug.
I'll have to think what the joystick should do in this situation.
Kind of a tough one...

@bfishman
Copy link
Author

It is not the same behavior, but I have seen that happen as well. Perhaps
they are related and one solution will fix both.

I will try to come up with a better repro..

On Mon, May 30, 2016 at 2:35 PM, Yoann Moinet notifications@github.com
wrote:

Ok, so after testing it, I might have found a way to reproduce it.

I was able to reproduce the phenomenon by activating the right joystick,
while playing with it, rotate the phone.
Then release your finger, and the joystick will stay activated.
After that, if you active the left joystick, both will tracked the same
finger.
Release, and only the right one will de-activate, and the right one will
stay stuck for ever.

Is it the behavior you were having?

It could be related to #30
#30. This looks like an
edge case, but definitely a bug.
I'll have to think what the joystick should do in this situation.
Kind of a tough one...


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#31 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/ADXbO21V1g_GS1-NeGJ0FRGJ-adjHZLfks5qG0oogaJpZM4IpZVm
.

@mlknz
Copy link
Contributor

mlknz commented Aug 22, 2016

Having same issue. Reproduced by creating one static and one dynamic nipples on different dom elements.
addedNipples = 0;
removedNipples = 0;
lookNippleManager.on('added', (evt, nipple) => {
addedNipples++;
}).on('removed', (e, nipple) => {
removedNipples++;
});

When bug happens having addedNipples === removedNipples + 1 (after releasing touch). Spamming touches help with reproducing, but is not necessary.
Platforms: desktop Chrome on Ubuntu 14.04. Mobile Safari on multiple ios devices.

@yoannmoinet
Copy link
Owner

Thx @mlknz to add more details to this.
I'm still having a hard time pinpointing this (ANNOYING) bug.

@mlknz
Copy link
Contributor

mlknz commented Aug 23, 2016

@yoannmoinet couldn't reproduce bug with dataOnly param set to true. Could it be dom manipulation related issue?

@yoannmoinet
Copy link
Owner

@mlknz it could be indeed.

I don't have much time on this right now, so feel free to investigate.
I'm fixing the issue you have with the dev workflow and I'm almost there.

@mlknz
Copy link
Contributor

mlknz commented Aug 24, 2016

Found easy way to reproduce bug:
start a new touch on old joystick container before it gets removed from options.zone. This way Collection.processOnEnd is not called after releasing touch.

Basically slow double tap works.

@SpaceRangerX
Copy link

SpaceRangerX commented Mar 3, 2017

hi, I have fixed a similar issue (joystick gets stuck in iOS devices) by adding this line "|| self.binded" in this function in this file dist/nipplejs.js

Manager.prototype.unbindDocument = function (force) { var self = this; // If there are no touch left // unbind the document. if (!Object.keys(self.ids).length || force === true || self.binded) { self.unbindEvt(document, 'move') .unbindEvt(document, 'end'); self.binded = false; } };

@kevzettler
Copy link

kevzettler commented Mar 31, 2018

I am seeing this happen with nipplejs@0.6.8

I think this only happens when dataOnly: false and the DOM elements are rendered.

There are no error warnings thrown. The behaveior that happens is the joy stick UI DOM elements just freeze or lockup, The joystic UI remains visible on screen and cannot be manipulated.

@robertlong
Copy link

robertlong commented Apr 12, 2018

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 😅):

[Log] added 0:added – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] added 0:added – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] start – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] start 0:start – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] rested – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] rested 0:rested – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] rested 0:rested – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] move – {identifier: 0, position: {x: 268, y: 525}, force: 0, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 268, y: 525}, force: 0, pressure: 0, distance: 0, …}Object
[Log] move 0:move – {identifier: 0, position: {x: 268, y: 525}, force: 0, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 268, y: 525}, force: 0, pressure: 0, distance: 0, …}Object
[Log] move – {identifier: 0, position: {x: 269, y: 523}, force: 0.044721359549995794, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 269, y: 523}, force: 0.044721359549995794, pressure: 0, distance: 2.23606797749979, …}Object
[Log] move 0:move – {identifier: 0, position: {x: 269, y: 523}, force: 0.044721359549995794, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 269, y: 523}, force: 0.044721359549995794, pressure: 0, distance: 2.23606797749979, …}Object
[Log] move – {identifier: 0, position: {x: 270, y: 523}, force: 0.0565685424949238, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 270, y: 523}, force: 0.0565685424949238, pressure: 0, distance: 2.8284271247461903, …}Object
[Log] move 0:move – {identifier: 0, position: {x: 270, y: 523}, force: 0.0565685424949238, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 270, y: 523}, force: 0.0565685424949238, pressure: 0, distance: 2.8284271247461903, …}Object
[Log] move – {identifier: 0, position: {x: 269, y: 524}, force: 0.0282842712474619, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 269, y: 524}, force: 0.0282842712474619, pressure: 0, distance: 1.4142135623730951, …}Object
[Log] move 0:move – {identifier: 0, position: {x: 269, y: 524}, force: 0.0282842712474619, …} (nipplejs.js, line 251)
{identifier: 0, position: {x: 269, y: 524}, force: 0.0282842712474619, pressure: 0, distance: 1.4142135623730951, …}Object
[Log] end – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] end 0:end – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] added 1:added – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] added 1:added – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] start – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] start 1:start – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] rested – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] rested 1:rested – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] rested 1:rested – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] move – {identifier: 1, position: {x: 275, y: 524}, force: 0, …} (nipplejs.js, line 251)
{identifier: 1, position: {x: 275, y: 524}, force: 0, pressure: 0, distance: 0, …}Object
[Log] move 1:move – {identifier: 1, position: {x: 275, y: 524}, force: 0, …} (nipplejs.js, line 251)
{identifier: 1, position: {x: 275, y: 524}, force: 0, pressure: 0, distance: 0, …}Object
[Log] removed – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] removed 0:removed – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] removed 0:removed – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] destroyed – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] hidden – {el: <div id="nipple_0_0">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_0">, on: function, off: function, show: function, hide: function, …}Object
[Log] shown – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] shown 1:shown – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object
[Log] shown 1:shown – {el: <div id="nipple_0_1">, on: function, off: function, …} (nipplejs.js, line 251)
{el: <div id="nipple_0_1">, on: function, off: function, show: function, hide: function, …}Object

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 then nipple_0_1 has it's shown event fired and is stuck.

@kevzettler
Copy link

great find @robertlong

@robertlong
Copy link

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.

@robertlong
Copy link

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 setting fadeTime: 0 so I'm going to stick with that for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants