[Solved] Lemur: continuous picks on spatial

Hi

Wanted to know if there’s way to do continuous spatial picks, (like the nice MouseEventControl.addListenersToSpatial but continuously (or say, x times per second).

The code snippet I currently have:

MouseEventControl.addListenersToSpatial(spatial,
                new DefaultMouseListener() {
                    
                    
            @Override
            protected void click(MouseButtonEvent event, Spatial target, Spatial capture) {
...

However, I’d like to capture the mousedown as well as collision on the spatial. Hope that makes sense.

Reason for this: Right now, a mouse click sends the coordinates of the collision to the server and a map tile is placed. I’d like for the user to be able to hold down mouse button and drag the mouse, and then send the coordinates continuously - so that map-editing becomes easier than having to press the mouse a hundred times.

If I do:

/**
 * Implementation of the AnalogFunctionListener interface.
 */
@Override
public void valueActive(FunctionId func, double value, double tpf) {
    if (func == PlayerMovementFunctions.F_THRUST) {
        this.forward = value;
    } else if (func == PlayerMovementFunctions.F_TURN) {
        this.rotate = value;
    } else if (func == PlayerMovementFunctions.F_MOUSE1) {
        this.mouse1 = value;
    } else if (func == PlayerMovementFunctions.F_MOUSE2) {
        this.mouse2 = value;
    } else if (func == PlayerMovementFunctions.F_MOUSE3) {
        this.mouse3 = value;
    }
}

And in PlayerMovementFunctions.java:

    /*
    Mouse
    */
    if (!inputMapper.hasMappings(F_MOUSE1)) {
        inputMapper.map(F_MOUSE1, Button.MOUSE_BUTTON1);
    }

    if (!inputMapper.hasMappings(F_MOUSE2)) {
        inputMapper.map(F_MOUSE2, Button.MOUSE_BUTTON2);
    }

    if (!inputMapper.hasMappings(F_MOUSE3)) {
        inputMapper.map(F_MOUSE3, Button.MOUSE_BUTTON3);
    }

Then I can capture the button down on the mouse, but then I do not have the MouseButtonEvent

Okay, so my transition away from spatiallistener was pretty quick, I got it working now.

Why continuously send, though, when only the end result is evaluated? Isn’t the transition just a client side visualization?

To send mouse ‘clicks’ continuously, so that the player doesn’t have to click 50 times with the mouse to interact 50 times. If I had to do an analogy, it would be trying to paint in MS Paint without painting continuously, but having to do it by pressing the mouse button for every pixel you want to colour :slight_smile:

Why not override the mouse moved event instead?

So… you want the down and the up… then fire whatever as long as you are getting moves.

…all of which is available through the mouse listener.

Ah, that’s a better solution. Thanks

That works too :slight_smile:

I ended up with this:

public void addArenaMouseListeners(Spatial arena) {

    MouseEventControl.addListenersToSpatial(arena,
            new DefaultMouseListener() {

        boolean isPressed;
        int keyIndex;
        
        private int xDown;
        private int yDown;

        @Override
        protected void click(MouseButtonEvent event, Spatial target, Spatial capture) {

        }

        @Override
        public void mouseButtonEvent(MouseButtonEvent event, Spatial target, Spatial capture) {
            event.setConsumed();

            isPressed = event.isPressed();
            keyIndex = event.getButtonIndex();
            
            if (event.isPressed()) {
                xDown = event.getX();
                yDown = event.getY();
            } else if (isClick(event, xDown, yDown)) {
                click(event, target, capture);
            }
        }

        @Override
        public void mouseEntered(MouseMotionEvent event, Spatial target, Spatial capture) {
            //Material m = ((Geometry) target).getMaterial();
            //m.setColor("Color", ColorRGBA.Yellow);
        }

        @Override
        public void mouseExited(MouseMotionEvent event, Spatial target, Spatial capture) {
            //Material m = ((Geometry) target).getMaterial();
            //m.setColor("Color", ColorRGBA.Blue);
        }

        @Override
        public void mouseMoved(MouseMotionEvent event, Spatial target, Spatial capture) {
            if (isPressed && keyIndex == MouseInput.BUTTON_LEFT) {
                GameSession session = getState(ConnectionState.class).getService(GameSessionClientService.class);
                if (session == null) {
                    throw new RuntimeException("ModelViewState requires an active game session.");
                }
                Camera cam = getState(CameraState.class).getCamera();

                Vector2f click2d = new Vector2f(event.getX(), event.getY());

                Vector3f click3d = cam.getWorldCoordinates(click2d.clone(), 0f).clone();
                Vector3f dir = cam.getWorldCoordinates(click2d.clone(), 1f).subtractLocal(click3d).normalizeLocal();

                Ray ray = new Ray(click3d, dir);
                CollisionResults results = new CollisionResults();
                target.collideWith(ray, results);
                if (results.size() != 1) {
                    log.error("There should only be one collision with the arena when the user clicks it");
                }
                Vector3f contactPoint = results.getCollision(0).getContactPoint();
                session.editMap("", contactPoint.x, contactPoint.y);
            }
        }
    });
}