BGE proposal: Better performance with Mouse Over (any) sensors

Issue:
As you might know each active mouse over (any) sensor forces the BGE send a ray across the scene.
A ray is very processor intensive.

When using a lot of mouse over sensors this has a very negative effect on the game speed. This is especially true on GUIs.

My suggestion:
Within one scene the ray of a mouse over (any) sensor is always the same if the sensor’s configuration is the same (e.g. X-Ray).
This enables us to cache the result of one sensor and re-use it when evaluated by another mouse over (any) sensor within the same scene with the same configuration.

Benefits:
The first sensor evaluation will still be heavy. But the subsequent sensor evaluations will be very light weighted.
Brings back an efficient object orientated approach on GUI development.

Drawbacks:
Currently I do not see any, except the development and testing effort.

Workaround:
Avoid lots of mouse over sensors by using a few mouse over any sensors.

Remarks:
I’m not sure how much work that is. I guess it is somewhere between middle and high afford.

What do you think?

Indeed,this shall really get inplemented! A developer must see this,I will try and ask Moguri,in case he didnt see this.

i never understand why 10 “mouse over” is more heavy than 1 “mouse over any” that can hit 10 obj

in both case is already a thing centralized . if i’m not wrong the ray start from active camera… (that can be only one = already centralized)

EDIT : i not see [ray-X] option …anyway without RAY-X should be reusable the same ray (that is only one) while with ray-X, required a new ray for each sensor

without x-ray, mouse over can be just a “dummy”:

that just check in the hitObject of the active_camera:
if active_camera.hitObject == owner : trigger = True

Another method for a GUI scan would be to completely skip the “mouse over” business, you would of course need to know python. I do not believe mouse-over should ever be used for a GUI considering on what it is and how many calculations it has to use. If your GUI doesn’t dynamically change with moving buttons you can just set up an array of RECT variables, then on mouse click see if the cursor lays in one of the RECTs and use that as selection. Personally I would think that most GUIs would work with this kind of system, The problems with this is for non-rectangular buttons. Though you can always use circles as well by using the distance formula, or for more complex ones you can use Point in Polygon algorithm. The Point in Polygon method would be more intensive but would still be faster than the Engine having to calculate it in 3 space.
Edit:
Just wrote an example piece of this code: (Not tested but the theory should work)


#each button holds
#[0] = type of button
#[1] = Button id
#   for rectangles [0]==0
#       [2] = top left x        [3] = top left y
#       [4] = bottom right x    [5] = bottom right y
#   for circles [0] == 1
#       [2] = center x          [3] = center y
#       [4] = radius
#
#Below is an example of how the button array should be set up.
buttons = [[0,"square_one",0,10,10,10],[0,"square_two",0,20,10,20],[1,"circle_one",30,30,10]]

def getButton(x,y):
    for i in buttons:
        if i[0] == 0:
            #it's a square button
            if x > i[2] and x < i[4] and y > i[3] and y < i[5]:
                return i[1]
        elif i[0] == 1:
            #it's a circular button
            tempX = x-i[2]
            tempY = y-i[3]
            if sqrt(tempX*tempX+tempY*tempY) < i[4]:
                return i[1]
        else:
            #it's an error
            return "NULL"
    return "NULL"

Note: The input would have to be centered around the center of the screen utilizing the below to get the mouse position reletive to the center of the screen.
width = bge.render.getWindowWidth()
height = bge.render.getWindowHeight()
controller.sensors[“MouseMovement”].position
and the buttons would have to be also setup on this kind of grid.
Some Errors that might occur:
You must line up the buttons up correctly with what you have set in the code. I would suggest having a function that reads in your button objects and adds where it will line up with the pixels on the screen. This kind of a function would also remove scaling error when resolution is changed.

It ends up being more work but this is a clean method that doesn’t rely on the engine to do work for you. At the end of the day your game will represent the work you put into it.

Monster, this is an important proposal. Mouse over sensors can very quickly make a game slow, and many users do not know this.

I agree with you. It would be better if the engine optimized this behind the scenes, instead of forcing us to use Python scripts to work around it.

I do not think mouseOver sensors should be used for making a GUI. I also don’t think this could be implemented, I don’t think it works how Monster has stated. I have nothing to back this up but I believe that the system works by creating a ray that follows from the camera origin in the direction of the cursor. Then it checks to see if that ray collides with any planes, if it does it returns the collisions. If it works like this, there would be no method to “save” the sensor reading because it directly relates to the mouse position during that scan. This also would explain (as MarcolT said) “why 10 “mouse over” is more heavy than 1 “mouse over any” that can hit 10 obj” Because to make ten mouse over sensors would in turn cause a ten rays to scan the scene every tick, while one mouse over any would make one ray. The difference being that a mouse over any returns anything that is hit by the ray and a mouse over returns only if the object exists in that list. Still I honestly don’t know if that is the mechanics behind it, but that’s how I would think the system would work.

Continuing what I said about this not being a method for GUIs: This system must create a ray, scan every face of every object in the scene to see if that face intersects the ray and then if it does intersect it must return the closest intersection point’s object. This is an outrageously large amount of calculations to do for a 2d GUI. MouseOver seems to be more for RPGs where you can select a character or position on a map. I do agree that a user friendly version of a GUI editor should be added to blender but it would be better for the engine and people’s games if it was not based on mouseOver and was instead based on what higher level game engines use which follows what I outlined.

I think you discovered the exact problem ;).

It does.
The sensed mouse position is discrete which means it will not change within one frame. The ray and therefore the result will be the same (within one frame and with the same sensor setup). So it is no problem to cache the results. Indeed the cache has top be cleared before processing the next frame.

Please have a look at BGE Guide to the GameLoop

Making a GUI is not part of this thread. It is an example as it is an obvious use case for mouse over.

Beside of that the BGE does not provide a 2D GUI. If you really want you can use BGUI for that. For a simple 3D GUI as needed for a game mouse over is sufficient enough.

Oh, I was under the impression that you wanted to make the mouseOver sensor cache for more than one frame, which is why you can see why I said it was impossible. So what you are saying is to make all of the mouseOver sensors read from one master scan, which I can understand and agree.

Basically, yes.

More specific: Cache the first ray result within a frame and reuse it for other sensors with the same setup within the same frame.

I was working on a demo. I had a whole bunch of Mouse Over Sensors, didn’t even know about the Mouse Over Any. I must say I’m very happy I could read about it in the API. I only use one Mouse Over Any with true level triggering on in the Scene and another one in the HUD. Since I started to learn Python, I can’t imagine making a game without it anymore. It is so useful and so easy once you get the hang of it. I have no issues with performance and I don’t see why you should be using more than one Mouse Over Any per active scene, but having an X-ray feature on the Mouse Over Any sure would be great. I think I could be using this for the demo, so I welcome the proposal too. I’m a bit curious though if sending a ray from the Camera to the Mouse Pointer would be faster than a Mouse Over Any. If so I might be switching to that approach, because this has the X-ray feature.

Happy blending, you all!

The X-Ray option is a different proposal ;).

It is from logic point of view thesame.
From efficiency perspective the mouse over any is slightly faster as it does not start the python interpreter especially if it is not necessary. This is important to keep in mind as the ray will be send out at each frame.

Thanks for that info, Monster. Better to use the built-in functions then.

I think if it would be possible to cache the result of one sensor in order to reuse it by other Mouse Over Sensors, this would be an improvement for sure. Come to think of it, for a platform game, it would be convenient to just put a Mouse Over on every enemy, obstacle, … instead of redirecting a part of their logic into one ‘placeholder’ script with the use of just one Mouse Over Any. Although I think it’s manageable making use of modules like so:

from Data import actions
        
def base(cont):

    player = cont.owner
    weapon = player['weapon']

    mouseOverAny = cont.sensors['MouseOverAny']
    mouseButtonL = cont.sensors['MouseButtonL']
    
    hitOb = mouseOverAny.hitObject
    hitPos = mouseOverAny.hitPosition
    
    if mouseButtonL.positive == True:
        if weapon == 'banana':
            actions.shootBanana(player, hitOb, hitPos)
        elif weapon == 'water':
            actions.throwWater(player, hitOb, hitPos)

Would this be your approach also?

The proposal dos not change anything on the logic as it is right now. It increases performance when using multiple mouse over sensors.

performance perspective:
mouse over any: The code will be executed on ANY object under the cursor. In most cases it is not the object the code is interested in. Nevertheless the code will be called.

mouse over: The code will be executed ONLY when detecting the sensors owner. In ALL cases this is the object the code is interested in. Therefore much less processing time is wasted.

Difference to the current behavior of the BGE: Multiple mouse over can become faster than a mouse over any (depends on the specific situation)

Logic/design perspective:
mouse over any: can be processed by any object within the scene. This allows the usage of “managers”. The logic does not need to be at the detected object.

mouse over: can be processed by the sensing object only. This means this object needs the logic to react on the mouse cursor.

I understand better now. But I’m wondering. If processing time depends on whether the sensors owner is detected, why is having multiple objects with each their own Mouse Over Sensor giving performance issues? If it is activated on one object, in most cases it won’t be on other objects. So I was thinking if you mean that the problem resides in ‘switching’ from one sensor to the other? Then how would caching be a solution? Not that I question your expertise on this, but I’m just curious in knowing if my assumption is right. Or maybe I just have to be quiet and leave it up to others to share their comments on this. Anyhow, I’m happy to learn, that’s why I ask these questions.

This is because ALL (active) sensors are evaluated at EACH frame. A sensor is active when it is connected to an active controller. A controller is active when it resides in an active state.

Imagine you have 10 mouse over.
Currently:
They send 10 rays at each frame. Only 1 of them triggers the connected controller (for the reasons you wrote above).
This means worst case: 10 x ray processing time + 1 controller time.
average case: 10 x ray processing (the likelihood to hit an sensed object is usually not high)

Proposal:
Send 1 ray at each frame and cache the result. The next 9 sensors access the cached result.
This means worst case: 1 x ray processing time + 9 x cache access + 1 controller time
Average case: 1 x ray processing + 9x cache access

Remarks: Accessing a cache is usually very fast

One Mouse over any:
Send 1 ray at each frame.
This means worst case: 1 x ray processing time + 1 Python controller processing time
average case: 1 x ray processing time + 1 Python controller processing time

Note: (1 x ray processing + 9x cache access) in average is usually faster than (1 x ray processing time + 1 Python controller processing time)

Note2: This is not to avoid mouse over any, but to avoid the need to choose mouse over any if it is not necessary. With mouse over you do not always need Python.

Thanks for the reply, Monster. Wow, I’m slow (maybe also a little dumb). Didn’t realize up to know that a Mouse Over Sensor, even without having true level triggering enabled, is sending its ray all the time. Of course this is logical, because how else would the controller be activated if the sensor doesn’t check for intersection with the Mouse Cursor all the time? Yes, indeed, I believe your proposal is very neat. Hope you get it through.