How to make a 2-dimensional slider?

Hi,

I recently had to make little change to a project that use jMonkey, however I never used it before. I need to implement a 2-dimensional slider.
If possible, it looks like a square, and I can put the control anywhere on it, and it tells me the x and y axis.
I couldn’t find anything related, but maybe I just didn’t search the correct keywords.

Do you mean something similar to like those simulated joysticks on mobile apps?

Yes, the idea is that we could latter use a joystick to move it, but for now doing it with a mouse is enough. Like that : joystick
The idea is that we can place the red dot, and it gives us the x and y position.

Why not use a circle? Because only then the distance to the center is always equal (i.e. the walking speed is always the same).

Where are you stuck?
You can start with an empty quad and use some mouse listener and place the dot relative to the quad.
It all depends on your ideas, e.g. should the mouse be locked and only the relative movement be shown or should someone click inside the quad.

If it were me, the thumb would be a child of the square… and I’d cut and paste Lemur’s drag handler and change it to constrain the dragging to the parent size. Then the local position of thumb is the x,y of the ‘slider’.

Why not use a circle? Because only then the distance to the center is always equal (i.e. the walking speed is always the same).

It’s a simulator of a physical system, so we try to be as close to the physical system as possible.

Where are you stuck?

I was looking to see if something like this was already implemented, or if it was too specific.

It all depends on your ideas, e.g. should the mouse be locked and only the relative movement be shown or should someone click inside the quad.

The movement would be via dragging with the mouse.
So you think the best idea would be to create a quad and my own mouse listener to get the position ?

Yeah, or what paul recommended: Lemur’s Drag Handler

Indeed, your fastest route would be to use Lemur.

In code it should look something like this:

Spatial moveableControl = new Geometry("control", new Sphere(16, 16, 20));
CursorEventListener.addListenersToSpatial(moveableControl, new DragHandler(input -> moveableControl));

Now you can drag the ‘moveableControl’ around. The only thing left is to make sure that it stays within the boundaries. Create a method that you call in the update loop:

public void clampBoundaries(Spatial spatial) {
    Vector3f location = new Vector3f(spatial.getLocalTranslation());
    location.setX(FastMath.clamp(location.x, startX, endX));
    location.setY(FastMath.clamp(location.y, startY, endY));

    spatial.setLocalTranslation(location);
}

note, this is from the top of my head, so the code can slightly deviate

1 Like

if you are using lemur, I can share 2 panels that i have made.
I have a JoystickPanel and a 2d slider panel that i call AnalogSliderPanel, i made them for android and they work fine. For each of them, i have its own layout and some extra classes to make them work, but its not to much

3 Likes

Hi! I am in the same team work as EV and I would be interested in seeing your work with Lemur. It is very nice to propose.

Hi, im always glad to share my stuff, lol, just copy this classes and let me know if theres any problem. i think you have to change the imports because i have them in different folders.
And maybe i lied a little, theres 9 classes to make the JoystickPanel to work and 6 more classes for the AnalogSliderPanel, ill share first the JoystickPanel because of the limit of the text here

here is the JoystickPanel class

package com.Capuchin.Panels;

import com.Capuchin.Layouts.JoystickLayout;
import com.Capuchin.Listeners.DragListener;
import com.Capuchin.Listeners.JoystickListener;
import com.Capuchin.Listeners.Move;
import com.jme3.math.Vector2f;
import com.simsilica.lemur.event.CursorEventControl;
import com.simsilica.lemur.style.ElementId;

/**
 *
 * @author Pankey
 */
public class JoystickPanel extends DropPanel{

    public static final String ELEMENT_ID = "joystick";
    public static final String STICK_ELEMENT_ID = "stick";
    protected StickPanel AnalogStick ;
    protected DragListener listener ;
    protected static int s = 0 ;
    protected Move move;
    protected float stickPos=0;

    public JoystickPanel(String style) {
        this(null,Move.All,false,new ElementId(ELEMENT_ID),style);
    }
    public JoystickPanel(Move move,String style) {
        this(null,move,false,new ElementId(ELEMENT_ID),style);
    }
    public JoystickPanel(String name, String style) {
        this(null,Move.All,false,new ElementId(ELEMENT_ID), style);
    }

    public JoystickPanel( boolean behind,String style) {
        this(null,Move.All,behind,new ElementId(ELEMENT_ID),style);
    }
    public JoystickPanel(Move move, boolean behind,String style) {
        this(null,move,behind,new ElementId(ELEMENT_ID),style);
    }
    public JoystickPanel(String name, boolean behind,String style) {
        this(null,Move.All,behind,new ElementId(ELEMENT_ID), style);
    }

    public JoystickPanel(String name,Move move, boolean behind,ElementId elementid, String style) {
        super(true,elementid,style);
        if(name == null){
            this.setName("JoystickPanel"+String.valueOf(s));
            s++;
        }else{
            this.setName(name);
        }
        this.move =move;

        this.AnalogStick = new StickPanel(elementid.child(STICK_ELEMENT_ID),style);
        this.AnalogStick.setName("analogstick");
        JoystickListener jcol = new JoystickListener(this.move,this.AnalogStick,behind);
        CursorEventControl.addListenersToSpatial(this, jcol);
        this.listener = jcol;
        setLayout(new JoystickLayout());
        this.addChild(this.AnalogStick,"buttonAnalogSlider");
        this.AnalogStick.setAlpha(0);
    }

    public void removeListener() {
        CursorEventControl.removeListenersFromSpatial(this, this.listener);
    }

    public StickPanel getJoystick() {
        return this.AnalogStick;
    }

    public boolean isDragging() {
        return this.listener.isDragging();
    }

    public Vector2f getDragLocalTranslation() {
        return this.listener.getDragLocalTranslation();
    }
}

here is the JoystickLayout class

package com.Capuchin.Layouts;

import com.Capuchin.Panels.StickPanel;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.component.AbstractGuiComponent;
import com.simsilica.lemur.core.GuiLayout;
import java.util.Collection;

/**
 *
 * @author Pankey
 */
public class JoystickLayout extends AbstractGuiComponent
                              implements DragDropLayout, Cloneable{
    private StickPanel StickContainer = null ;

    public JoystickLayout(){
    }

    @Override
    public void calculatePreferredSize(Vector3f size) {

    }

    @Override
    public void reshape(Vector3f pos, Vector3f size) {
        this.StickContainer.setGuiSize( this.StickContainer,this.StickContainer.getGuiSize() );
    }

    @Override
    public <T extends Node> T addChild(T n, Object... constraints) {
        if(constraints != null){
            if(constraints.length > 0){
                if(constraints[0] instanceof String){
                    String s = (String) constraints[0];
                    if(s.equals("buttonAnalogSlider")){
                        this.StickContainer= (StickPanel) n;
                        this.getNode().attachChild(n);
                    }
                }
            }
        }
        return n;
    }

    @Override
    public void removeChild(Node n) {
    }

    @Override
    public Collection<Node> getChildren() {
        return null;
    }

    @Override
    public void clearChildren() {

    }

    @Override
    public GuiLayout clone() {
        return new JoystickLayout();
    }

    @Override
    public <T extends Node> T replaceChild(T t, Vector3f dropPosition, Object... os) {
        return null;
    }

}

here is the StickPanel class, this class is really small, and could have use a Panel class but i intend to write some styles for this later

package com.Capuchin.Panels;

import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.style.Styles;

/**
 *
 * @author Pankey
 */
public class StickPanel extends DropPanel{
    public static final String ELEMENT_ID = "stick";

    public StickPanel(ElementId elementId, String style){
        this(true,elementId,style);
    }
    protected StickPanel(boolean applyStyles, ElementId elementId, String style){
        super(false,elementId,style);

        if( applyStyles ) {
            Styles styles = GuiGlobals.getInstance().getStyles();
            styles.applyStyles(this, elementId, style);
        }  
    }
}

here is the DragListener interface

package com.Capuchin.Listeners;

import com.jme3.math.Vector2f;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.event.CursorListener;

/**
 *
 * @author Pankey
 */
public interface DragListener<T extends DragListener> extends CursorListener{
    public boolean isDragging();
    public Vector2f getDragLocalTranslation();
    public Vector2f getStartDragLocalTranslation();
    public Vector2f getDragWorldTranslation();
    public Vector2f getStartDragWorldTranslation();

    public void addDraggCommand(Command<T> list);
}

here is the JoystickListener class

package com.Capuchin.Listeners;

import com.Capuchin.Panels.JoystickPanel;
import com.Capuchin.Panels.StickPanel;
import com.jme3.input.MouseInput;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import com.jme3.util.SafeArrayList;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.event.CursorButtonEvent;
import com.simsilica.lemur.event.CursorMotionEvent;

/**
 *
 * @author Pankey
 */
public class JoystickListener implements DragListener<JoystickListener>{

    private Vector2f drag = null;
    private Vector2f draging = null;
    private Vector3f basePosition;
    private Vector2f current = null;
    private Vector3f worldPosition;
    private Move move;
    private StickPanel stick;
    private boolean behind=true;
    public SafeArrayList<Command> onDraggCommand;


    public JoystickListener(StickPanel stick, boolean behind) {
        this(Move.All, stick,behind);
    }

    public JoystickListener(Move move,StickPanel stick, boolean behind) {
        this.move = move;
        this.stick = stick;
        this.behind = behind;
        this.onDraggCommand = new SafeArrayList<Command>(Command.class);
    }

    @Override
    public boolean isDragging() {
        return this.drag != null;
    }

    @Override
    public Vector2f getStartDragLocalTranslation() {
        return new Vector2f(basePosition.x , basePosition.y);
    }

    @Override
    public Vector2f getDragWorldTranslation() {
        return current;
    }

    @Override
    public Vector2f getStartDragWorldTranslation() {
        return drag;
    }

    @Override
    public Vector2f getDragLocalTranslation() {
        if(isDragging()){
            return draging;
        }

        return new Vector2f();
    }

    public void setMove(Move move) {
        this.move =move;
    }

    public Move getMove() {
        return this.move;
    }

    protected void startDrag( CursorButtonEvent event, Spatial target, Spatial capture ) {
        drag = new Vector2f(event.getX(), event.getY());
        basePosition = stick.getLocalTranslation().clone();
        worldPosition = capture.getWorldTranslation().clone();
        event.setConsumed();
    }

    protected void endDrag( CursorButtonEvent event, Spatial target, Spatial capture ) {
        drag = null;
        basePosition = null;
        worldPosition = null;
        draging.set(0, 0);
        this.stick.setAlpha(0);
    }

    @Override
    public void cursorButtonEvent( CursorButtonEvent event, Spatial target, Spatial capture ) {
        if( event.getButtonIndex() != MouseInput.BUTTON_LEFT )
            return;

        if( event.isPressed() ) {
            startDrag(event, target, capture);
        } else {
            if( drag != null ) {
                endDrag(event, target, capture);
            }
        }
    }

    @Override
    public void cursorMoved( CursorMotionEvent event, Spatial target, Spatial capture ) {
        if( drag == null || capture == null )
            return;

        JoystickPanel joyPanel;
        if(capture instanceof JoystickPanel){
            joyPanel = (JoystickPanel)capture;
        }else{
            return;
        }
        ViewPort vp = event.getViewPort(); 
        Camera cam = vp.getCamera();

        if( cam.isParallelProjection() || capture.getQueueBucket() == RenderQueue.Bucket.Gui ) {
            current = new Vector2f(event.getX(), event.getY());
            Vector2f delta = current.subtract(drag);
            Vector2f stickextra = drag.subtract(this.worldPosition.x,this.worldPosition.y).subtract((joyPanel.getPreferredSize().x*this.stick.getGuiSize().x)/200, -(joyPanel.getPreferredSize().y*this.stick.getGuiSize().y)/200);

            // Make sure if Z has changed that it is applied to base
            basePosition.z = capture.getLocalTranslation().z;

            if(this.move == Move.All){
                draging=new Vector2f(delta.x, delta.y);
            }
            if(this.move == Move.Not){
                draging=new Vector2f(-delta.x, -delta.y);
            }
            if(this.move == Move.X){
                draging=new Vector2f(delta.x, 0);
            }
            if(this.move == Move.Y){
                draging=new Vector2f(0, delta.y);
            }
            if(this.move == Move.NotX){
                draging=new Vector2f(-delta.x, -delta.y);
            }
            if(this.move == Move.NotY){
                draging=new Vector2f(0, -delta.y);
            }
            if(this.move == Move.MixXY){
                draging=new Vector2f(delta.y, delta.x);
            }
            if(this.move == Move.MixNotXY){
                draging=new Vector2f(-delta.y, -delta.x);
            }
            Vector3f baseP = null;
            if(this.behind){
                baseP = new Vector3f(this.draging.x+stickextra.x, this.draging.y+stickextra.y, -0.1f);
            }else{
                baseP = new Vector3f(this.draging.x+stickextra.x, this.draging.y+stickextra.y, 0.1f);
            }

            //Vector3f baseP = this.basePosition.add(this.draging.x, this.draging.y, -0.1f);

            this.stick.setLocalTranslation(baseP);

            this.stick.setAlpha(1);
        }

    }

    @Override
    public void cursorEntered(CursorMotionEvent event, Spatial target, Spatial capture) {

    }

    @Override
    public void cursorExited(CursorMotionEvent event, Spatial target, Spatial capture) {

    }

    @Override
    public void addDraggCommand(Command<JoystickListener> list) {
        this.onDraggCommand.add(list);
    }

    public void loadDraggCommands() {
        for(int x=0; x < this.onDraggCommand.size(); x++){
            this.onDraggCommand.get(x).execute(this);
        }
    }
}

here is the Move enum class

package com.Capuchin.Listeners;

/**
 *
 * @author Pankey
 */
public enum Move {
    All,
    Not,
    X,
    Y,
    Z,
    NotX,
    NotY,
    NotZ,
    MixXY,
    MixNotXY;
}

here is the ScreenGuiUtils class

package com.Capuchin.Utils;

import com.jme3.app.Application;
import com.jme3.asset.AssetManager;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.system.AppSettings;

/**
 *
 * @author Pankey
 */

public class ScreenGuiUtils {

    private final AppSettings settings;
    private final AssetManager assets;
    private static ScreenGuiUtils guiUtils = null;

    public ScreenGuiUtils(Application app){
        this.assets = app.getAssetManager();
        this.settings = app.getContext().getSettings();
    }

    public static void initialize(Application app){
        ScreenGuiUtils.guiUtils = new ScreenGuiUtils(app);
    }

    public static ScreenGuiUtils getScreenGuiUtils(){
        if(ScreenGuiUtils.guiUtils == null){
            return null;
        }
        return ScreenGuiUtils.guiUtils;
    }

    public Vector2f getScreenSize(){        
        return new Vector2f(settings.getWidth(),settings.getHeight());
    }

    public float QuadX(float x){        
        return setSize(new Vector2f(x,0)).getX();
    }
    public float QuadY(float y){        
        return setSize(new Vector2f(0,y)).getY();
    }
    public Vector3f setSize(float x,float y,float z){        
        return new Vector3f(QuadX(x),QuadY(y),z);
    }
    public Vector2f setSize(float x,float y){        
        return setSize(new Vector2f(x,y));
    }
    public Vector2f setSize(Vector2f vec){
        return setSize(vec, 100);
    }
    public Vector2f setSize(Vector2f vec,float rel){
        float x = ((settings.getWidth()/rel)*vec.getX());
        float y = ((settings.getHeight()/rel)*vec.getY());
        return new Vector2f(x,y);
    }
    public Vector2f setSize(float width,float height,float x,float y){
        return this.setSize(width, height, new Vector2f(x,y),100);
    }
    public Vector2f setSize(float width,float height,Vector2f vec,float rel){
        float x = ((width/rel)*vec.getX());
        float y = ((height/rel)*vec.getY());
        return new Vector2f(x,y);
    }
    public Vector3f setSize(float width,float height,float x,float y,float z){
        return this.setSize(width, height, new Vector3f(x,y,z),100);
    }

    public Vector3f setSize(float width,float height,Vector3f vec,float rel){
        return this.setSize(new Vector3f(width,height,1), vec, rel);
    }

    public Vector3f setSize(Vector3f vec){
        return this.setSize(vec, 100);
    }

    public Vector3f setSize(Vector3f vec,float rel){
        float x = ((settings.getWidth()/rel)*vec.getX());
        float y = ((settings.getHeight()/rel)*vec.getY());
        return new Vector3f(x,y,vec.z);
    }

    public Vector3f setSize(Vector3f size,Vector3f vec,float rel){
        float x = ((size.x/rel)*vec.getX());
        float y = ((size.y/rel)*vec.getY());
        return new Vector3f(x,y,vec.z);
    }
    public Vector3f setLeftTopSize(float x,float y,float z){
        return this.setLeftTopSize(settings.getWidth(), settings.getHeight(), new Vector3f(x,y,z),100);
    }
    public Vector3f setLeftTopSize(float width,float height,float x,float y,float z){
        return this.setLeftTopSize(width, height, new Vector3f(x,y,z),100);
    }
    public Vector3f setLeftTopSize(float width,float height,Vector3f vec,float rel){
        float x = ((width/rel)*vec.getX());
        float y = ((height/rel)*vec.getY());
        return new Vector3f(x,-y,vec.z);
    }
    public Vector3f setLeftTopSize(Vector3f size,Vector3f vec,float rel){
        float x = ((size.x/rel)*vec.getX());
        float y = ((size.y/rel)*vec.getY());
        return new Vector3f(x,-y,vec.z);
    }
    public Vector3f setLeftTopSizeYFlip(Vector3f size,Vector3f vec,float rel){
        float x = ((size.x/rel)*vec.getX());
        float y = ((size.y/rel)*vec.getY());
        return new Vector3f(x,y,vec.z);
    }

    public float setMiddlePosX(float x){
        return setMiddlePos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public float setMiddlePosY(float y){
        return setMiddlePos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setMiddlePosZ(float z){
        return setMiddlePos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public Vector2f setMiddlePos(float x,float y){
        return new Vector2f(setMiddlePosX(x),setMiddlePosY(y));
    }
    public Vector3f setMiddlePos(float x,float y,float z){
        return setMiddlePos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setMiddlePos(float x,float y,float z,float width,float height){
        return setMiddlePos(new Vector3f(x,y,z),width,height,100);
    }
    public Vector3f setMiddlePos(Vector3f vec){
        return setMiddlePos(vec,0,0,100);
    }
    public Vector3f setMiddlePos(Vector3f vec,float width,float height){
        return setMiddlePos(vec,width,height,100);
    }
    public Vector3f setMiddlePos(Vector3f vec,float width,float height,float rel){
        float x = ((settings.getWidth()/2)-(width/2))+(((settings.getWidth()/2)/(rel))*vec.getX());
        float y = ((settings.getHeight()/2)-(height/2))+(((settings.getHeight()/2)/(rel))*vec.getY());
        return new Vector3f(x,y,vec.getZ());
    }
    public float setCenter_From_RawPosZ(float z){
        return setCenter_From_RawPos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public float setCenter_From_RawPosY(float y){
        return setCenter_From_RawPos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setCenter_From_RawPosX(float x){
        return setCenter_From_RawPos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public Vector3f setCenter_From_RawPos(float x,float y,float z){
        return setCenter_From_RawPos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setCenter_From_RawPos(Vector3f vec,float width,float height){
        return setCenter_From_RawPos(vec,width,height,100);
    }
    public Vector3f setCenter_From_RawPos(Vector3f vec,float width,float height,float rel){
        float x = (vec.getX()-((settings.getWidth()/2)-(width/2)))/((settings.getWidth()/2)/(rel));
        float y = (vec.getY()-((settings.getHeight()/2)-(height/2)))/((settings.getHeight()/2)/(rel));
        return new Vector3f(x,y,vec.getZ());
    }

    public float setLeftTopPosX(float x){
        return setLeftTopPos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public float setLeftTopPosY(float y){
        return setLeftTopPos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setLeftTopPosZ(float z){
        return setLeftTopPos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public Vector2f setLeftTopPos(float x,float y){
        return new Vector2f(setLeftTopPosX(x),setLeftTopPosY(y));
    }
    public Vector3f setLeftTopPos(float x,float y,float z){
        return setLeftTopPos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setLeftTopPos(Vector3f vec){
        return setLeftTopPos(vec,0,0,100);
    }
    public Vector3f setLeftTopPos(Vector3f vec,float width,float height){
        return setLeftTopPos(vec,width,height,100);
    }
    public Vector3f setLeftTopPos(Vector3f vec,float width,float height,float rel){
        float x = -(width/2) + (((settings.getWidth())/(rel))*vec.getX());
        float y = ((settings.getHeight())-(height/2))-(((settings.getHeight())/(rel))*vec.getY());
        return new Vector3f(x,y,vec.getZ());
    }

    public float setLeftTop_From_RawPosZ(float z){
        return setLeftTop_From_RawPos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public float setLeftTop_From_RawPosY(float y){
        return setLeftTop_From_RawPos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setLeftTop_From_RawPosX(float x){
        return setLeftTop_From_RawPos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public Vector3f setLeftTop_From_RawPos(float x,float y,float z){
        return setLeftTop_From_RawPos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setLeftTop_From_RawPos(Vector3f vec,float width,float height){
        return setLeftTop_From_RawPos(vec,width,height,100);
    }
    public Vector3f setLeftTop_From_RawPos(Vector3f vec,float width,float height,float rel){
        float x = (vec.getX()-(-(width/2)))/((settings.getWidth())/(rel));
        float y = (vec.getY()-((settings.getHeight())-(height/2)))/((settings.getHeight())/(rel));
        return new Vector3f(x,y,vec.getZ());
    }

    public boolean isInsideViewPort(Vector4f viewport , Vector2f cursor){
        return  ( viewport.x <= cursor.x )  &&
                ( viewport.y >= cursor.x )  &&
                ( viewport.z <= cursor.y )  &&
                ( viewport.w >= cursor.y );
    }

    public boolean isViewPorFullSize(Vector4f viewport){
        return  viewport.x==0.00f && 
                viewport.y==1.00f &&
                viewport.z==0.00f &&
                viewport.w==1.00f;
    }

    public Vector2f getViewPortPosition(Vector2f cursor){
        float x = cursor.getX()/settings.getWidth();
        float y = cursor.getY()/settings.getHeight();
        return new Vector2f(x,y);
    }

    public AppSettings getSettings(){
        return settings;
    }

    public int getScreenHeight(){
        return settings.getHeight();
    }

    public int getScreenWidth(){
        return settings.getWidth();
    }


}

here is the DropPanel class

package com.Capuchin.Panels;

import com.Capuchin.Layouts.DragDropLayout;
import com.Capuchin.Utils.ScreenGuiUtils;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.style.StyleAttribute;
import com.simsilica.lemur.style.Styles;

/**
 *
 * @author Pankey
 */
public class DropPanel extends Panel{
    protected Vector3f guiSize;
    protected Vector3f guiTranslation;
    public static final String ELEMENT_ID = "droppanel";

    protected DropPanel(boolean applyStyles, ElementId elementId, String style){
        super(false,elementId,style);

        if( applyStyles ) {
            Styles styles = GuiGlobals.getInstance().getStyles();
            styles.applyStyles(this, elementId, style);
        }  
    }

    public <T extends Node> T addChild( T child, Object... constraints ) {
        return getLayout().addChild(child, constraints);
    }

    public void removeChild( Node child ) {
        getLayout().removeChild(child);
    }

    public void clearChildren() {
        getLayout().clearChildren();   
    }

    @Override
    public Spatial detachChildAt( int index ) {
        Spatial child = getChild(index);

        // See if this child is managed by the layout
        if( child instanceof Node && getLayout().getChildren().contains((Node)child) ) {
            removeChild((Node)child);
            return child;
        } else {
            // Just let the superclass do its thing with the 
            // unmanaged child
            return super.detachChildAt(index);
        }        
    }

    public void setLayout( DragDropLayout layout ) {
        getControl(GuiControl.class).setLayout(layout);
    }

    public DragDropLayout getLayout() {
        return getControl(GuiControl.class).getLayout();
    }

    public void setGuiTranslation(Node object,Vector3f localTranslation) {
        if(ScreenGuiUtils.getScreenGuiUtils() == null){
            return;
        }
        if(object.getParent() != null){
            if(object.getParent() instanceof Panel){
                Vector3f size = ((Panel)object.getParent()).getPreferredSize().clone();
                if(size==null){
                    object.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(localTranslation));
                }else{
                    if(size.equals(Vector3f.ZERO)){
                        object.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(localTranslation));
                    }else{
                        //System.out.println(size);
                        //System.out.println(ScreenGuiUtils.getScreenGuiUtils().setLeftTopSize(size,localTranslation,100));
                        object.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopSize(size,localTranslation,100));
                    }
                }
            }else{
                object.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(localTranslation));
            }
        }else{
            object.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(localTranslation));
        }
    }

    public void setLocalGuiTranslation(float x,float y,float z) {
        if(ScreenGuiUtils.getScreenGuiUtils() == null){
            return;
        }
        if(this.parent != null){
            if(this.parent instanceof Panel){
                Vector3f size = ((Panel)this.parent).getPreferredSize().clone();
                if(size==null){
                    this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(x,y,z));
                }else{
                    if(size.equals(Vector3f.ZERO)){
                        this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(x,y,z));
                    }else{
                        this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopSize(size.x,size.y,x,y,z));
                    }
                }
            }else{
                this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(x,y,z));
            }
        }else{
            this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(x,y,z));
        }

    }

    @StyleAttribute(value="guiTranslation", lookupDefault=false)
    public void setLocalGuiTranslation(Vector3f localTranslation) {
        this.guiTranslation = localTranslation;
        this.setGuiTranslation(this, localTranslation);
    }

    public Vector3f getLocalGuiTranslation(){
        return this.guiTranslation;
    }

    @Override
    @StyleAttribute(value="localTranslation", lookupDefault=false)
    public void setLocalTranslation(Vector3f localTranslation) {
        super.setLocalTranslation(localTranslation);
    }

    public void setGuiSize(Panel object,Vector3f size) {
        if(ScreenGuiUtils.getScreenGuiUtils() == null){
            return;
        }
        if(this.parent != null){
            if(this.parent instanceof Panel){
                Vector3f parentsize = ((Panel)object.getParent()).getSize().clone();
                if(parentsize==null){
                    object.setSize(ScreenGuiUtils.getScreenGuiUtils().setSize(size,100));
                }else{
                    object.setSize(ScreenGuiUtils.getScreenGuiUtils().setSize(parentsize,size,100));
                }
            }else{
                object.setSize(ScreenGuiUtils.getScreenGuiUtils().setSize(size,100));
            }
        }else{
            object.setPreferredSize(ScreenGuiUtils.getScreenGuiUtils().setSize(size,100));
        }
    }

    @StyleAttribute(value="guiSize", lookupDefault=false)
    public void setLocalGuiSize(Vector3f size) {
        this.guiSize = size;
        this.setGuiSize(this,size);
    }

    public Vector3f getGuiSize() {
        return this.guiSize;
    }

    public <T extends Node> T replaceChild(T child,Vector3f localTranslation, Object... os) {
        return this.getLayout().replaceChild(child, localTranslation, os);
    }

    @Override
    public String toString() {
        return getClass().getName() + "[layout=" + getLayout() + ", elementId=" + getElementId() + "]";
    }

}

here is the DragDropLayout class

package com.Capuchin.Layouts;

import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.core.GuiLayout;

/**
 *
 * @author Pankey
 */
public interface DragDropLayout extends GuiLayout{
    public <T extends Node> T replaceChild(T t,Vector3f dropPosition, Object... os);
}

here starts the AnalogSlider

package com.Capuchin.Panels;

import com.Capuchin.Layouts.AnalogSliderLayout;
import com.Capuchin.Listeners.DragListener;
import com.Capuchin.Listeners.GuiRestrictionListener;
import com.Capuchin.Listeners.Move;
import com.Capuchin.VersionedObjects.DefaultRangedVector2fModel;
import com.Capuchin.VersionedObjects.RangedVector2fModel;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.event.CursorEventControl;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.style.Styles;

/**
 *
 * @author Pankey
 */
public class AnalogSlider extends DropPanel{

    public static final String ELEMENT_ID = "analogslider";
    public static final String STICK_ELEMENT_ID = "stick";
    protected StickPanel AnalogStick ;
    protected DragListener listener ;
    protected static int s = 0 ;
    protected Move move;
    protected float stickPos=0;
    protected GuiOrigin guiOrigin = GuiOrigin.TopLeft;
    protected VersionedReference<Vector2f> state;
    protected RangedVector2fModel model = new DefaultRangedVector2fModel();
    protected Vector3f prefSize =null;
    protected Vector2f limit =null;
    protected Vector3f guiSize;


    public AnalogSlider(String style) {
        this(null,new ElementId(ELEMENT_ID),Move.All,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(GuiOrigin guiOrigin,String style) {
        this(null , new ElementId(ELEMENT_ID) , Move.All , guiOrigin , new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(Move move,String style) {
        this(null,new ElementId(ELEMENT_ID),move,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(Move move,GuiOrigin guiOrigin,String style) {
        this(null,new ElementId(ELEMENT_ID),move,guiOrigin,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(String name,String style) {
        this(null,new ElementId(ELEMENT_ID),Move.All,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(Move move,ElementId id,String style) {
        this(null,id,move,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(Move move,GuiOrigin guiOrigin,ElementId id,String style) {
        this(null,id,move,guiOrigin,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(String name,ElementId id,String style) {
        this(name,id,Move.All,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider( boolean applyStyles, String name,Move move,ElementId id,String style) {
        this(applyStyles,name,id,move,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(String name,Move move,ElementId id,String style) {
        this(name,id,move,GuiOrigin.TopLeft,new DefaultRangedVector2fModel(),style);
    }
    public AnalogSlider(String name,Move move,GuiOrigin guiOrigin,ElementId id,String style) {
        this(name,id,move,guiOrigin,new DefaultRangedVector2fModel(),style);
    }

    public AnalogSlider(String name,ElementId id,Move move,GuiOrigin guiOrigin,RangedVector2fModel model,String style) {
        this(true,name,id,move,guiOrigin,new DefaultRangedVector2fModel(),style);
    }

    public AnalogSlider( boolean applyStyles, String name,ElementId elementId,Move move, GuiOrigin guiOrigin, RangedVector2fModel model,String style) {
        super(false,elementId,style);
        this.limit = new Vector2f(100,100);

        if( applyStyles ) {
            Styles styles = GuiGlobals.getInstance().getStyles();
            styles.applyStyles(this, elementId, style);
        }
        this.guiOrigin = guiOrigin;
        if(name == null){
            this.setName("AnalogSlider"+String.valueOf(s));
            s++;
        }else{
            this.setName(name);
        }
        this.move =move;
        this.model = model;

        this.AnalogStick = new StickPanel(elementId.child(STICK_ELEMENT_ID),style);
        this.AnalogStick.setName("stick");
        GuiRestrictionListener gcol = new GuiRestrictionListener(this.move,this);
        CursorEventControl.addListenersToSpatial(this.AnalogStick, gcol);
        this.listener = gcol;
        setLayout(new AnalogSliderLayout());
        this.addChild(this.AnalogStick,"buttonAnalogSlider");
        if(this.guiOrigin == GuiOrigin.TopLeft){
            this.model.setMaximum(100, 100);
            this.model.setMinimum(0, 0);
        }
        if(this.guiOrigin == GuiOrigin.TopRight){
            this.model.setMaximum(100, 100);
            this.model.setMinimum(0, 0);
        }
        if(this.guiOrigin == GuiOrigin.BotLeft){
            this.model.setMaximum(100, 100);
            this.model.setMinimum(0, 0);
        }
        if(this.guiOrigin == GuiOrigin.BotRight){
            this.model.setMaximum(100, 100);
            this.model.setMinimum(0, 0);
        }
        if(this.guiOrigin == GuiOrigin.Center){
            this.model.setMaximum(100, 100);
            this.model.setMinimum(-100, -100);
        }
        setAnalog(0,0);
        this.state = this.model.createReference();
    }

    public RangedVector2fModel getModel() {
        return this.model;
    }

    public void removeListener() {
        CursorEventControl.removeListenersFromSpatial(this, this.listener);
    }

    public boolean isDragging() {
        if(this.listener == null){
            return false;
        }
        return this.listener.isDragging();
    }

    public StickPanel getAnalogStick() {
        return this.AnalogStick;
    }

    public Vector2f getDragLocalTranslation() {
        return this.listener.getDragLocalTranslation();
    }

    public VersionedReference<Vector2f> getDragReference() {
        return this.state;
    }

    public Vector2f getStrickGuiTranslation() {
        return this.state.get();
    }

    @Override
    public void updateLogicalState(float tpf) {
        super.updateLogicalState(tpf);
        if( isDragging() ) {
            this.model.setValue(getAnalog());
        }
    }

    public Vector2f getAnalog() {
        Vector3f sizeAnalogStick = this.AnalogStick.getGuiSize();
        Vector3f sizeContainer = this.getPreferredSize();
        Vector3f TotalSize = sizeContainer.subtract(sizeContainer.mult(sizeAnalogStick.divide(100)));
        Vector3f localTranslation = this.AnalogStick.getLocalTranslation();
        Vector2f analog = new Vector2f();
        if(this.guiOrigin == GuiOrigin.TopLeft){
            analog.x = localTranslation.x * (this.limit.x/TotalSize.x);
            analog.y = -localTranslation.y * (this.limit.y/TotalSize.y);
        }
        if(this.guiOrigin == GuiOrigin.TopRight){
            analog.x = ( TotalSize.x - localTranslation.x )* (this.limit.x/TotalSize.x);
            analog.y = -localTranslation.y * (this.limit.y/TotalSize.y);
        }
        if(this.guiOrigin == GuiOrigin.BotLeft){
            analog.x = localTranslation.x * (this.limit.x/TotalSize.x);
            analog.y = ( TotalSize.y +localTranslation.y) * (this.limit.y/TotalSize.y);
        }
        if(this.guiOrigin == GuiOrigin.BotRight){
            analog.x = ( TotalSize.x - localTranslation.x )* (this.limit.x/TotalSize.x);
            analog.y = ( TotalSize.y +localTranslation.y) * (this.limit.y/TotalSize.y);
        }
        if(this.guiOrigin == GuiOrigin.Center){
            analog.x = localTranslation.x * (this.limit.x/TotalSize.x);
            analog.y = -localTranslation.y * (this.limit.y/TotalSize.y);
            if(analog.y > (this.limit.y/2)){
                analog.y = ( analog.y -(this.limit.y/2) ) * -2;
            }else{
                analog.y = ( -analog.y +(this.limit.y/2) ) * 2;
            }
            if(analog.x > (this.limit.x/2)){
                analog.x = ( analog.x -(this.limit.x/2) ) * 2;
            }else{
                analog.x = ( -analog.x +(this.limit.x/2) ) * -2;
            }
        }
        return analog;
    }
    public void setLimit(float x,float y) {        
        this.limit.set(x, y);
    }
    public Vector2f getLimit() {        
        return this.limit;
    }
    public float getAnalogX() {        
        return  getAnalog().x;
    }
    public float getAnalogY() {        
        return getAnalog().y;
    }

    public void setAnalogX(float x) {
        if(this.prefSize == null){
            this.setAnalog(new Vector2f(x,this.getAnalogY()), this.getAnalogStick().getGuiSize(), this.getPreferredSize());
        }else{
            this.setAnalog(new Vector2f(x,this.getAnalogY()),this.prefSize);
        }
    }

    public void setAnalogY(float y) {
        if(this.prefSize == null){
            this.setAnalog(new Vector2f(this.getAnalogX(),y), this.getAnalogStick().getGuiSize(), this.getPreferredSize());
        }else{
            this.setAnalog(new Vector2f(this.getAnalogX(),y),this.prefSize);
        }
    }

    public void setAnalog(float x,float y) {
        if(this.prefSize == null){
            this.setAnalog(new Vector2f(x,y), this.getAnalogStick().getGuiSize(), this.getPreferredSize());
        }else{
            this.setAnalog(new Vector2f(x,y),this.prefSize);
        }
    }

    public void setAnalog(float x,float y,Vector3f sizeContainer) {
        this.setAnalog(new Vector2f(x,y),sizeContainer);
    }

    public void setAnalog(Vector2f analog) {
        if(this.prefSize == null){
            this.setAnalog(analog, this.getAnalogStick().getGuiSize(), this.getPreferredSize());
        }else{
            this.setAnalog(analog,this.prefSize);
        }
    }

    public void setAnalog(Vector2f analog,Vector3f sizeContainer) {
        this.prefSize = sizeContainer;
        this.setAnalog(analog, this.getAnalogStick().getGuiSize(), this.prefSize);
    }

    public void setAnalog(Vector2f analog,Vector3f sizeAnalogStick,Vector3f sizeContainer) {
        Vector3f TotalSize = sizeContainer.subtract(sizeContainer.mult(sizeAnalogStick.divide(100)));
        Vector3f localTranslation = this.getLocalTranslation();
        if(this.guiOrigin == GuiOrigin.TopLeft){
            localTranslation.x = analog.x * (TotalSize.x/this.limit.x);
            localTranslation.y = -analog.y * (TotalSize.y/this.limit.y);
        }
        if(this.guiOrigin == GuiOrigin.TopRight){
            localTranslation.x = ( this.limit.x -analog.x ) * (TotalSize.x/this.limit.x);
            localTranslation.y = -analog.y * (TotalSize.y/this.limit.y);
        }
        if(this.guiOrigin == GuiOrigin.BotLeft){
            localTranslation.x = analog.x * (TotalSize.x/this.limit.x);
            localTranslation.y = ( -this.limit.y +analog.y )* (TotalSize.y/this.limit.y);
        }
        if(this.guiOrigin == GuiOrigin.BotRight){
            localTranslation.x = ( this.limit.x -analog.x ) * (TotalSize.x/this.limit.x);
            localTranslation.y = ( -this.limit.y +analog.y )* (TotalSize.y/this.limit.y);
        }
        if(this.guiOrigin == GuiOrigin.Center){
            analog.y = ( (this.limit.y/2) - (analog.y/2) );
            analog.x = ( (this.limit.x/2) + (analog.x/2) );
            localTranslation.x = analog.x * (TotalSize.x/this.limit.x);
            localTranslation.y = -analog.y * (TotalSize.y/this.limit.y);
        }
        localTranslation.z = 0.1f;
        ((AnalogSliderLayout)getControl(GuiControl.class).getLayout()).setAnalogPosition(localTranslation);
        this.model.setValue(getAnalog());
    }
}

here is the AnalogSliderLayout class

package com.Capuchin.Layouts;

import com.Capuchin.Panels.StickPanel;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.component.AbstractGuiComponent;
import com.simsilica.lemur.core.GuiLayout;
import java.util.Collection;

/**
 *
 * @author Pankey
 */
public class AnalogSliderLayout extends AbstractGuiComponent
                              implements DragDropLayout, Cloneable{
    private StickPanel StickContainer = null ;
    private final Vector3f mpos;
    private final Vector3f msize;
    private boolean apply = true;

    public AnalogSliderLayout(){
        this.msize = new Vector3f();
        this.mpos = new Vector3f();
    }

    @Override
    public void calculatePreferredSize(Vector3f size) {

    }

    @Override
    public void reshape(Vector3f pos, Vector3f size) {
        this.mpos.set((pos.x/2) - (this.StickContainer.getPreferredSize().x/2) , 
                                                 -(pos.y/2) + (this.StickContainer.getPreferredSize().y/2) , 
                                                 pos.z+0.1f);
        if(apply){
            this.StickContainer.setLocalTranslation( this.mpos );
        }
        this.StickContainer.setGuiSize( this.StickContainer,this.StickContainer.getGuiSize() );
    }

    @Override
    public <T extends Node> T addChild(T n, Object... constraints) {
        if(constraints != null){
            if(constraints.length > 0){
                if(constraints[0] instanceof String){
                    String s = (String) constraints[0];
                    if(s.equals("buttonAnalogSlider")){
                        this.StickContainer= (StickPanel) n;
                        n.setLocalTranslation(0, 0, 100.1f);
                        this.getNode().attachChild(n);
                    }
                }
            }
        }
        invalidate();
        return n;
    }

    @Override
    public void removeChild(Node n) {
        this.getNode().detachChild(n);
    }

    @Override
    public Collection<Node> getChildren() {
        return null;
    }

    @Override
    public void clearChildren() {

    }

    @Override
    public GuiLayout clone() {
        return new AnalogSliderLayout();
    }

    public void setAnalogPosition( Vector3f pos ) {
        apply = false;
        this.StickContainer.setLocalTranslation(pos);
    }

    @Override
    public <T extends Node> T replaceChild(T t, Vector3f dropPosition, Object... os) {
        return null;
    }

}

here is the GuiRestrictionListener class

package com.Capuchin.Listeners;

import com.jme3.input.MouseInput;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import com.jme3.util.SafeArrayList;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.event.CursorButtonEvent;
import com.simsilica.lemur.event.CursorMotionEvent;


/**
 *
 * @author Pankey
 */
public class GuiRestrictionListener implements DragListener<GuiRestrictionListener>{

    private Vector2f drag = null;
    private Vector2f draging = null;
    private Vector3f basePosition;
    private Vector2f current = null;
    //private Vector3f worldPosition;
    private Move move;
    private Panel container;
    public SafeArrayList<Command> onDraggCommand;


    public GuiRestrictionListener(Panel container) {
        this(Move.All, container);
    }

    public GuiRestrictionListener(Move move,Panel container) {
        this.move = move;
        this.container = container;
        this.onDraggCommand = new SafeArrayList<Command>(Command.class);
    }

    @Override
    public boolean isDragging() {
        return this.drag != null;
    }

    @Override
    public Vector2f getStartDragLocalTranslation() {
        return new Vector2f(basePosition.x , basePosition.y);
    }

    @Override
    public Vector2f getDragWorldTranslation() {
        return current;
    }

    @Override
    public Vector2f getStartDragWorldTranslation() {
        return drag;
    }

    @Override
    public Vector2f getDragLocalTranslation() {
        if(isDragging()){
            return draging;
        }

        return new Vector2f();
    }

    public void setMove(Move move) {
        this.move =move;
    }

    public Move getMove() {
        return this.move;
    }

    protected void startDrag( CursorButtonEvent event, Spatial target, Spatial capture ) {
        drag = new Vector2f(event.getX(), event.getY());
        basePosition = capture.getLocalTranslation().clone();
//        worldPosition = capture.getWorldTranslation().clone();
        event.setConsumed();
    }

    protected void endDrag( CursorButtonEvent event, Spatial target, Spatial capture ) {
        drag = null;
        basePosition = null;
//        worldPosition = null;
        draging.set(0, 0);
    }

    @Override
    public void cursorButtonEvent( CursorButtonEvent event, Spatial target, Spatial capture ) {
        if( event.getButtonIndex() != MouseInput.BUTTON_LEFT )
            return;

        if( event.isPressed() ) {
            startDrag(event, target, capture);
        } else {
            if( drag != null ) {
                endDrag(event, target, capture);
            }
        }
    }

    @Override
    public void cursorMoved( CursorMotionEvent event, Spatial target, Spatial capture ) {
        if( drag == null || capture == null )
            return;

        ViewPort vp = event.getViewPort(); 
        Camera cam = vp.getCamera();

        if( cam.isParallelProjection() || capture.getQueueBucket() == RenderQueue.Bucket.Gui ) {
            current = new Vector2f(event.getX(), event.getY());
            Vector2f delta = current.subtract(drag);

            // Make sure if Z has changed that it is applied to base
            basePosition.z = capture.getLocalTranslation().z;

            if(this.move == Move.All){
                draging=new Vector2f(delta.x, delta.y);
            }
            if(this.move == Move.Not){
                draging=new Vector2f(-delta.x, -delta.y);
            }
            if(this.move == Move.X){
                draging=new Vector2f(delta.x, 0);
            }
            if(this.move == Move.Y){
                draging=new Vector2f(0, delta.y);
            }
            if(this.move == Move.NotX){
                draging=new Vector2f(-delta.x, -delta.y);
            }
            if(this.move == Move.NotY){
                draging=new Vector2f(0, -delta.y);
            }
            if(this.move == Move.MixXY){
                draging=new Vector2f(delta.y, delta.x);
            }
            if(this.move == Move.MixNotXY){
                draging=new Vector2f(-delta.y, -delta.x);
            }

            Vector3f baseP = this.basePosition.add(this.draging.x, this.draging.y, 0);
            //capture.setLocalTranslation(baseP);
            Vector2f base_lefttop = new Vector2f(baseP.x, -baseP.y);
            Vector2f base_rightop = new Vector2f(baseP.x+(((Panel)capture).getSize().x), -baseP.y);
            Vector2f base_leftbot = new Vector2f(baseP.x, -baseP.y+(((Panel)capture).getSize().y));
            Vector2f base_rightbot = new Vector2f(baseP.x+(((Panel)capture).getSize().x), -baseP.y+(((Panel)capture).getSize().y));
//            System.out.print("base_lefttop ");System.out.println(base_lefttop.toString());
//            System.out.print("base_rightop ");System.out.println(base_rightop.toString());
//            System.out.print("base_leftbot ");System.out.println(base_leftbot.toString());
//            System.out.print("base_rightbot ");System.out.println(base_rightbot.toString());

            Vector2f container_lefttop = new Vector2f(0, 0);
            Vector2f container_rightop = new Vector2f(0+(((Panel)this.container).getSize().x), 0);
            Vector2f container_leftbot = new Vector2f(0, (((Panel)this.container).getSize().y));
            Vector2f container_rightbot = new Vector2f(0+(((Panel)this.container).getSize().x), (((Panel)this.container).getSize().y));
//            System.out.print("container_lefttop ");System.out.println(container_lefttop.toString());
//            System.out.print("container_rightop ");System.out.println(container_rightop.toString());
//            System.out.print("container_leftbot ");System.out.println(container_leftbot.toString());
//            System.out.print("container_rightbot ");System.out.println(container_rightbot.toString());

//            //inside
//            if( (container_lefttop.x <= base_lefttop.x) && 
//                (container_rightop.x >= base_rightop.x) &&
//                (container_lefttop.y <= base_lefttop.y) &&
//                (container_rightbot.y >= base_rightbot.y))     {
//                
//                capture.setLocalTranslation(baseP);
//                return;
//            }
            //outside top
            if( (container_lefttop.y > base_lefttop.y))     {
                baseP.setY(container_lefttop.y);
            }
            //outside bot
            if( (container_rightbot.y < base_rightbot.y))     {
                baseP.setY(-container_rightbot.y+(((Panel)capture).getSize().y));
            }
            //outside left
            if( (container_lefttop.x > base_lefttop.x))     {
                baseP.setX(container_lefttop.x);
            }
            //outside right
            if( (container_rightop.x < base_rightop.x))     {
                baseP.setX(container_rightop.x-(((Panel)capture).getSize().x));
            }
//            System.out.print("baseP ");System.out.println(baseP.toString());
            baseP.z = 0.1f;
            capture.setLocalTranslation(baseP);
        }

    }

    @Override
    public void cursorEntered(CursorMotionEvent event, Spatial target, Spatial capture) {

    }

    @Override
    public void cursorExited(CursorMotionEvent event, Spatial target, Spatial capture) {

    }

    @Override
    public void addDraggCommand(Command<GuiRestrictionListener> list) {
        this.onDraggCommand.add(list);
    }

    public void loadDraggCommands() {
        for(int x=0; x < this.onDraggCommand.size(); x++){
            this.onDraggCommand.get(x).execute(this);
        }
    }
}

here is the DefaultRangedVector2fModel class

package com.Capuchin.VersionedObjects;

import com.jme3.math.Vector2f;
import com.simsilica.lemur.core.VersionedReference;

/**
 *
 * @author Pankey
 */
public class DefaultRangedVector2fModel implements RangedVector2fModel {

    private long version;
    private Vector2f min;
    private Vector2f max;
    private Vector2f value;

    public DefaultRangedVector2fModel() {
        this(new Vector2f(-100,-100), new Vector2f(100,100), new Vector2f(-100,100));
    }

    public DefaultRangedVector2fModel( Vector2f min, Vector2f max, Vector2f value ) {
        this.min = min;
        this.max = max;
        this.value = value;
        checkRange();
    }

    @Override
    public long getVersion() {
        return version;
    }

    @Override
    public Vector2f getObject() {
        return getValue();
    }

    @Override
    public VersionedReference<Vector2f> createReference() {
        return new VersionedReference<Vector2f>(this);
    }

    protected void checkRange() {
        value.set(Math.max(min.x, value.x), Math.max(min.y, value.y));
        value.set(Math.min(max.x, value.x), Math.min(max.y, value.y));
    }

    @Override
    public void setValue( Vector2f value ) {
        if( this.value == value )
            return;
        this.value = value;
        version++;
        checkRange();
    }

    @Override
    public void setValue( float x, float y ) {
        if( this.value == new Vector2f(x,y) ){
            return;
        }
        this.value.set(x,y);
        version++;
        checkRange();
    }

    @Override
    public Vector2f getValue() {
        return value;
    }

    @Override
    public void setPercent(float x, float y) {
        float rangeX = max.x - min.x;
        float rangeY = max.y - min.y;
        float projectedX = min.x + rangeX * x;
        float projectedY = min.y + rangeY * y;
        setValue(projectedX,projectedY);
    }

    public void setPercent( Vector2f v ) {
        float rangeX = max.x - min.x;
        float rangeY = max.y - min.y;
        float projectedX = min.x + rangeX * v.x;
        float projectedY = min.y + rangeY * v.y;
        setValue(projectedX,projectedY);
    }

    public Vector2f getPercent() {
        Vector2f range = max.subtract(min);
        if( range == Vector2f.ZERO ){
            return new Vector2f(0,0);
        }
        Vector2f part = getValue().subtract(min);
        return new Vector2f(part.x/range.x,part.y/range.y);
    }

    @Override
    public void setMaximum(float x, float y) {
        setMaximum( new Vector2f(x, y) );
    }

    public void setMaximum( Vector2f max ) {
        if( this.max == max ) 
            return;
        this.max = max;
        version++;
        checkRange();
    }

    @Override
    public Vector2f getMaximum() {
        return max;
    }

    @Override
    public void setMinimum(float x, float y) {
        setMinimum( new Vector2f(x, y) );
    }

    @Override
    public void setMinimum( Vector2f min ) {
        if( this.min == min ) 
            return;
        this.min = min;
        version++;
        checkRange();
    }

    @Override
    public Vector2f getMinimum() {
        return min;
    }

    @Override
    public String toString() {
        return getClass().getName() + "[value=" + value + ", min=" + min + ", max=" + max + "]";
    }
}

here is the RangedVector2fModel interface

    package com.Capuchin.VersionedObjects;

    import com.jme3.math.Vector2f;
    import com.simsilica.lemur.core.VersionedObject;

    /**
     *
     * @author Pankey
     */
    public interface RangedVector2fModel extends VersionedObject<Vector2f> {

        public void setValue( Vector2f val );
        public void setValue( float x, float y );
        public Vector2f getValue();

        public void setPercent( Vector2f val );
        public void setPercent( float x, float y );
        public Vector2f getPercent();

        public void setMaximum( Vector2f max );
        public void setMaximum( float x, float y );
        public Vector2f getMaximum();

        public void setMinimum( Vector2f min );
        public void setMinimum( float x, float y );
        public Vector2f getMinimum();
    }

here is the GuiOrigin enum class

package com.Capuchin.Panels;

/**
 *
 * @author Pankey
 */
public enum GuiOrigin {
    TopLeft,
    TopRight,
    BotLeft,
    BotRight,
    Center;
}

and now the style class

package com.TCUProyect;

import com.jme3.math.Vector3f;
import com.jme3.texture.Texture;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.Attributes;
import com.simsilica.lemur.style.Styles;

/**
 *
 * @author Pankey
 */
public class TestStyle {

    public TestStyle(String style){
        Styles styles =GuiGlobals.getInstance().getStyles();
        Attributes attrs;

        Texture monkey = GuiGlobals.getInstance().loadTexture("Textures/Monkey.png",false,false);
        Texture black = GuiGlobals.getInstance().loadTexture("Textures/black.jpg",false,false);


        attrs = styles.getSelector("joystick", style);
        attrs.set("background", new QuadBackgroundComponent (monkey));
        attrs.set("guiSize", new Vector3f(50,50,1));

        attrs = styles.getSelector("joystick.stick", style);
        attrs.set("background", new QuadBackgroundComponent (black));
        attrs.set("guiSize", new Vector3f(50,50,1));

        attrs = styles.getSelector("analogslider", style);
        attrs.set("background", new QuadBackgroundComponent (monkey));
        attrs.set("guiSize", new Vector3f(50,50,1));

        attrs = styles.getSelector("analogslider.stick", style);
        attrs.set("background", new QuadBackgroundComponent (black));
        attrs.set("guiSize", new Vector3f(50,50,1));

    }
}

and finally the simpleapplication

package com.TCUProyect;

import com.Capuchin.Listeners.Move;
import com.Capuchin.Panels.AnalogSlider;
import com.Capuchin.Panels.GuiOrigin;
import com.Capuchin.Panels.JoystickPanel;
import com.Capuchin.Utils.ScreenGuiUtils;
import com.jme3.app.DebugKeysAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.simsilica.lemur.GuiGlobals;



/**
 *
 *
 *  @author    Pankey
 */
public class Main extends SimpleApplication {
    JoystickPanel joy;
    AnalogSlider slider;

    public static void main( String... args ) throws Exception {
        Main main = new Main();
        main.start();
    }

    public Main() {
        //super( new StatsAppState(), new DebugKeysAppState(), new TestGuiNodeState());
        super(
                 new StatsAppState()
                ,new DebugKeysAppState()
        );
    }

    @Override
    public void simpleInitApp() {
        this.setDisplayStatView(false);

        GuiGlobals.initialize(this);
        ScreenGuiUtils.initialize(this);

        GuiGlobals.getInstance().getStyles().setDefault(new TestStyle("StyleName"));

        slider = new AnalogSlider(Move.All, GuiOrigin.Center, "StyleName");
        slider.setAnalog(0, 0);
        slider.setLocalGuiTranslation(50, 0, 0);
        this.guiNode.attachChild(slider);

        joy= new JoystickPanel(Move.All, "StyleName");
        joy.setLocalGuiTranslation(0, 50, 0);
        this.guiNode.attachChild(joy);

    } 

    @Override
    public void simpleUpdate(float tpf) {
        System.out.println("joystick "+this.joy.getDragLocalTranslation());
        System.out.println("analogslider "+this.slider.getStrickGuiTranslation());
    }
}

let me know if this works for you, this joystick panel doesnt have a limit to the movement of the stick because i didnt like it that way, bot the analogslider does have that limit

Thanks you very much! We will try next week ! :slight_smile:

I tried this morning but without success… When I run the Main, I have an exception…

here is a preview :
“GRAVE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at com.simsilica.lemur.GuiGlobals.(GuiGlobals.java:104)
at vifa.Main.simpleInitApp(Main.java:36)
at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:220)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:130)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:211)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory”

As the exception explains: it requires slf4j.

Add the dependency to your build file: Maven Central Repository Search

If you are using gradle it’s something like this:

compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'

Apart from slf4j, you will also need to add the guava library.
This is all written in the Getting Started page of Lemur. Getting Started · jMonkeyEngine-Contributions/Lemur Wiki · GitHub

2 Likes

It’s work fine now! I forget to remove an older version of slf4j.

Thanks all!!

Can you please provide a screenshot of the slider provided by @pankey? Interested to see how it looks like. :slightly_smiling_face:

Thanks

1 Like

This is the beginning so it still needs to be changed ( background, style) :wink:

1 Like

some info about the AnalogSlider panel, there are some constructors, you can use this to setup most of the funtionalities,

slider = new AnalogSlider(Move.All,GuiOrigin.Center,"StyleName");

Move.All let you move in any direction, but you can also use Move.X to move only in the x direction, as well Move.Y for the y direction, there are silly movements like Move.NotX that will move in the x direction but if you move the mouse to the left side, the stick will move to the right, lol

GuiOrigin let you choose from where is going to start the local gui translation coordinate of the stick, and you can choose manually where is going to start with

slider.setAnalog(0, 0);

Since i made this for android but i wanted to develop on my pc everything and then just copy the code into android studio, i made the sizing and position on a percentage and not on the real resolution of the screen because everything changes from one place to another, so i made this

slider.setLocalGuiTranslation(50, 0, 0);

the places the slider on the midle of the x coordinate(50) and on the top of the screen on the y coordinate(0), on android and pc

I also made this on the style for the size

attrs.set("guiSize", new Vector3f(50,50,1));

that size if based on the total size of the screen, and that 50,50 will make it cover a quarter of the screen, and with 100,100 will be full size

and if you want to set the gui translation in the style you can use this

attrs.set("guiTranslation", new Vector3f(50,0,0));

to see where is the stick you can use this

this.slider.getStrickGuiTranslation()

there is a small typo, but lets ignore that, lol, that stick gui translation depend on the GuiOrigin that you choose and its returning a Vector2f of the position inside the parent panel, i dont remember well, but i think you can choose that relative position with

slider.getModel().setMaximum(100,100);
slider.getModel().setMinimum(0,0);
slider.setLimit(100, 100);
2 Likes