Sign in to follow this  
clifftm

FPS drops on mousemove

Recommended Posts

Hello.

If u paste this code (just a source code with a little modifications)  here https://pixijs.io/examples/#/plugin-dragonbones/robot.js

const app = new PIXI.Application({ antialias: true });
document.body.appendChild(app.view);

//That will help with fps, but all children become not interactive
//app.stage.interactiveChildren = false; 

app.stop();

// load spine data
PIXI.Loader.shared
    .add('skeleton', 'examples/assets/pixi-dragonbones/robot/mecha_1002_101d_show_ske.json')
    .add('texture_json', 'examples/assets/pixi-dragonbones/robot/mecha_1002_101d_show_tex.json')
    .add('texture_png', 'examples/assets/pixi-dragonbones/robot/mecha_1002_101d_show_tex.png')
    .load(onAssetsLoaded);

function onAssetsLoaded(loader, res) {
    const factory = dragonBones.PixiFactory.factory;

    factory.parseDragonBonesData(res.skeleton.data);
    factory.parseTextureAtlasData(res.texture_json.data, res.texture_png.texture);
    let scale = 0.1;
    for (let i = 0; i < 10; i++)
      for (let j = 0; j < 10; j++) {
  
    const armatureDisplay = factory.buildArmatureDisplay('mecha_1002_101d', 'mecha_1002_101d_show');

    let lb = armatureDisplay.getLocalBounds();

    //That makes better fps if uncomment
  //armatureDisplay.hitArea = new PIXI.Rectangle(lb.x, lb.y, lb.width, lb.height);
    armatureDisplay.pivot.set(lb.x, lb.y);
    armatureDisplay.scale.set(scale, scale);
    armatureDisplay.position.set(
       lb.width*i*armatureDisplay.scale.x, 
       lb.height*j*armatureDisplay.scale.y);
    armatureDisplay.interactive = true;

    armatureDisplay.on('click',()=>{console.log(i*10+j)});
        
    armatureDisplay.animation.play('idle');
    app.stage.addChild(armatureDisplay);
    }
    app.start();
}

and then move a mouse over a canvas FPS drops from  ~20-25 frames to 1 frame. How can i avoid that dropping? As i understand ParticleContainer not working with DB or Spine.  I'v tried disable mousemove events

app.view.addEventListener('mousemove',(e)=> {e.stopPropagation()})

but that not helped.

if i set app.stage.interactiveChildren = false; that helps with FPS a little, but in that case armatureDisplay.interactive = true will discarded and can't register any on('click') events , like  armatureDisplay.on('click',()=>{console.log(i*31+j)});

I can agree that 900 dragonbones objects reduce total FPS from 60 to 30, but don't understand why it falls down on mouse move events.

Probably the main problem is in hitArea, because setting it to the rounding box of the robot makes fps drops much smaller. If that true, how can i setup to check hitArea only on click, not on mouseMove?

Thanks

Edited by clifftm

Share this post


Link to post
Share on other sites

Found a solution but it seems very rude

        window.document.removeEventListener('mousemove', app.renderer.plugins.interaction.onPointerMove, true);
        window.document.removeEventListener('pointermove', app.renderer.plugins.interaction.onPointerMove, true);

is there any way to do this more softly?

Share this post


Link to post
Share on other sites

The interaction plugin tries to find target it moves over on and if you have lots of interactive elements to search from with complex hitareas then movement will slow things down as it has to iterate lot of stuff and see if they are under the pointer.

You could try setting the armatures interactiveChildren to false to prevent them from going deeper into the armature.

Share this post


Link to post
Share on other sites

If you want to make sure it's the interacitonmanagers search that is at fault you should take a profile when moving the mouse to see if that part of the code has issues.

Share this post


Link to post
Share on other sites

The reason the hitArea helps is because it uses that instead of searching through children to find the hittable areas. So if you require interactivity, hitAreas are the way to go. I'm not sure why this wasn't acceptable in your opening post? What issue were you having with them? They work on all events.

In any case, for really complex scenes, what you could do is have an extra layer, where you put either transparent sprites, graphics or containers with their own hitarea  - which are purely for hit areas for the scene below?

So instead of 

stage --> armatureDisplay

you have

stage --> interactivity layer --> armatureDisplay

Nothing below interactivity layer is actually interactive... just items in the layer above, which consist of very simple objects which mirror the size and shape of what is below.

 

Share this post


Link to post
Share on other sites
14 minutes ago, Exca said:

The interaction plugin tries to find target it moves over on

Yes, but how to disable that? Let IM found target only when mousedown, not on every pointer move.

Setting interactiveChildren to armature makes it not responsible for clicks at all, even if armature.interactive = true;

Again. You have 900 robots from pixi example, that have different animations (idle, run, jump, death). You need determine what robot was clicked. They can overlap if their position is near. So creating a container around each robot like this:

let boundContainer = new PIXI.Container();
boundContainer.interactive = true;
boundContainer.interactiveChildren = false;
boundContainer.addChild(armatureDisplay);
boundContainer.position.set(
   lb.width*i*armatureDisplay.scale.x*0.5, 
   lb.height*j*armatureDisplay.scale.y*0.5);  

boundContainer.hitArea = boundContainer.getLocalBounds();
boundContainer.on('click',()=>{console.log(i*10+j)});
app.stage.addChild(boundContainer);

You cant determine which robot was actually clicked left or right or upper or you just clicked in black space:

image.png.71217308dab84c9f2b836e62bd29fe51.png 

That will appear even with "interactivity layer". The animations can be complex, for example like death animation, and that bound box container should be recalculated every armature animation frame.

I have nothing against how IM determine object on click. Let him check all objects. But figuratively i'm interacting with robots only with mouseclick, and IM forcibly checks hitArea on every mousemove.

I can give you another analogy of what's happening. You have an antivirus with a button => onClick(check all computer for viruses). When pressing it, the process is starting. At the same time that process will also starting on every mouse move. And you just recommend me to exclude all folders from checking or just to check parent forlders on the disk without subfolders on each mousemove. And I just want to check computer only by pressing a button

Thanks

 

Share this post


Link to post
Share on other sites

If you dont have a need for pointer/touchmovement at all you could either fork the interactionmanager and use your own interactionplugin, remove the listeners or make the handling function do nothing. (game.renderer.plugins.interaction.processPointerMove = () => {} in v4, dont have a v5 project at hand to check how it would go there).

Currently the interactionplugin does not have properties for ignoring certain events.

Share this post


Link to post
Share on other sites

Its easy enough to override treeSearch in latest pixi-v5.

There are so many ways to optimize that thing, but each one also has side-effects or deoptimizations in other cases, so we really cant just change default, that's why we prettied up the code for everyone to override for their game.

If you happen to know how to optimize it for most of cases and not make code difficult - please share it, we are waiting for a hero.

Edited by ivan.popelyshev

Share this post


Link to post
Share on other sites
2 minutes ago, Exca said:

game.renderer.plugins.interaction.processPointerMove = () => {}

Yes, that's another variant of disabling event. But that 2 solutions looks like: "Doctor i have a bruise on the leg, can you help me?" "Sure, lets cut off your leg"

Share this post


Link to post
Share on other sites

 

19 hours ago, clifftm said:

Found a solution but it seems very rude


        window.document.removeEventListener('mousemove', app.renderer.plugins.interaction.onPointerMove, true);
        window.document.removeEventListener('pointermove', app.renderer.plugins.interaction.onPointerMove, true);

is there any way to do this more softly?

From the comments... if you didn't want any move events in your game, then that is a reasonable way to do it. There's no simpler way either at a top level or on a per component level.

It's that or digging into overriding functions, like Ivan suggests above, so that if the event is a move event, it's ignored if the component contains a flag or something

Share this post


Link to post
Share on other sites
1 minute ago, clifftm said:

Yes, that's another variant of disabling event. But that 2 solutions looks like: "Doctor i have a bruise on the leg, can you help me?" "Sure, lets cut off your leg"

I'm sorry you don't like the solutions, but it's not something that's really come up before that people haven't been satisfied covering with the existing methods. There is nothing more we can give here.

Share this post


Link to post
Share on other sites

Correct way would be to create your own version with a flag that does the ignoring. Then make a pull request to make it part of pixi if others see that it adds value.

Share this post


Link to post
Share on other sites
On 6/24/2020 at 6:24 PM, ivan.popelyshev said:

If you happen to know how to optimize it for most of cases and not make code difficult - please share it, we are waiting for a hero.

For example, like this

    this.app.renderer.plugins.interaction.search.recursiveFindHit = function(interactionEventdisplayObjectfunchitTestinteractive) {
            let type = interactionEvent.data.originalEvent.type;
            if ((/.*(move|leave|out|over).*/.test(type) && displayObject.allowMove === false) || (/.*(up|down|click|cancel).*/.test(type) && displayObject.allowClick === false)) {
                return false;
            } else {
                return this.__proto__.recursiveFindHit.call(thisinteractionEventdisplayObjectfunchitTestinteractive);
            }
        };

and then in the code you just need to 

        this.instance.interactive = true;     // enable interactive
        this.instance.allowMove = false;   // ignore mouse moving
        this.instance.allowClick = true;      // or just make it undefined. allow clicks

Again. You have a dragonbone\spine robot with head, 2 hands, 2 legs, body and a gun = 7 mesh\sprites. You create 100x100 robots and just want to click on them and show alert(clicked robot #...)

Every mouse move you will make tree traversal of all robots * 7 children, so 70000 checks if dispayobject.contains(cursor)

Edited by clifftm

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.