How do I make an inventory with lots of "slots"?

Hi!

I want to make an inventory thing, with lots (tens) of slots for items. I want the player to be ablet to click on and drag-n-drop to/from these slots. What is the best way to do that? Should I make a panel for every slot, of just one big panel and somehow calculate the slot the player clicked on? Both options seem viable.

What coordinate system will the x and y values be in, in the MouseButtonEvent?

If I add one panel (or something) for each slot, how do I control the layout of them? Can I add some sort of layout manager, like “GridLayout”, to the parent?

@tuffe said: Hi!

I want to make an inventory thing, with lots (tens) of slots for items. I want the player to be ablet to click on and drag-n-drop to/from these slots. What is the best way to do that? Should I make a panel for every slot, of just one big panel and somehow calculate the slot the player clicked on? Both options seem viable.

What coordinate system will the x and y values be in, in the MouseButtonEvent?

If I add one panel (or something) for each slot, how do I control the layout of them? Can I add some sort of layout manager, like “GridLayout”, to the parent?

I’ll try and answer this as best I can.

  • For the container, you can use a single image displaying the droppable areas.
  • You should add elements for each of the droppable areas, however… since they don’t require a display component, call setAsContainerOnly() on each.
  • Use the DragElement for movable items
  • Register the droppable areas with the DragElement to limit which effect the DragElement
  • If the DragElement is being dragged onto a window from some other location (like the screen), you would screen.removeElement(myDragElement) and then inventory.addChild(myDragElement).
  • You can set the drag element to lock to the droppable, so it will handle positioning for you.

I’ll put together a small example of how this works today, as I’ll need it for the tutorials section of the Wiki.

To answer your question about the x,y coords. After adding a component for the first time… Y coords are flipped (0 being the bottom of the screen) as they are in all of JME. The only reason this is not the case when you first create them is to make static layout creation easier.

2 Likes

2 Likes
@normen said:

What’s this from? For some reason… the avatar in the corner looks really familiar.

@t0neg0d said: What's this from? For some reason... the avatar in the corner looks really familiar.

http://www.gemstonedragon.com …not the sloths tho ^^

1 Like

@t0neg0d Nice! Thank you!

1 Like

IMPORTANT!!! Don’t quote this post, or the thread will become unreadable!!

@tuffe Here ya go: (Sorry about the wait… the videos take a while to upload)

** The example shows you how to interact with your game’s scene as well

[video]http://youtu.be/EzNY7jPgok0[/video]

** In the example, I store the scene node as userData in the DragElement… you will likely have another way of doing this. But it worked for the demo.
** If you have any questions about the example code (seeing that it is uncommented atm), feel free to ask.

Btw, thanks for asking about this, I haven’t actually used the DragElement before, past the simple test I made to ensure it worked. It’s actually pretty neat.

EDIT: Updated with the material color fix :wink:

AA_GUIDemo_Inventory.java (add the package to the top to run)
[java]
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.VideoRecorderAppState;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.RawInputListener;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Torus;
import tonegod.gui.controls.extras.DragElement;
import tonegod.gui.controls.windows.Window;
import tonegod.gui.core.Element;
import tonegod.gui.core.Screen;
import tonegod.gui.core.utils.UIDUtil;

/**

  • test

  • @author normenhansen
    */
    public class AA_GUIDemo_Inventory extends SimpleApplication implements RawInputListener, ActionListener {
    VideoRecorderAppState vrAppState;

    private Screen screen;
    private int winCount = 0;
    private float iconSize = 40;
    Vector2f dim = new Vector2f();
    Vector4f windowPadding = new Vector4f();
    float dragBarHeight;
    private Node obj1Node, obj2Node, obj3Node;

    Vector2f click2d = new Vector2f(), tempV2 = new Vector2f();
    Vector3f click3d = new Vector3f(), pickDir = new Vector3f(), tempV3 = new Vector3f();
    Ray pickRay = new Ray();
    CollisionResults rayResults = new CollisionResults();
    CollisionResult closest;

    public static void main(String[] args) {
    AA_GUIDemo_Inventory app = new AA_GUIDemo_Inventory();

     app.start();
    

    }

    @Override
    public void simpleInitApp() {
    vrAppState = new VideoRecorderAppState();
    vrAppState.setQuality(0.35f);

     setupKeys();
     
     setupLights();
     setupCamera();
     
     createGUIScreen();
     layoutGUI();
     
     addSceneObjects();
     
     flyCam.setMoveSpeed(30);
     flyCam.setDragToRotate(true);
     inputManager.deleteMapping("FLYCAM_RotateDrag");
     inputManager.addMapping("FLYCAM_RotateDrag", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
     inputManager.setCursorVisible(true);
    

    }

    private void setupKeys() {
    inputManager.addRawInputListener(this);
    inputManager.addMapping(“F9”, new KeyTrigger(KeyInput.KEY_F9));
    inputManager.addListener(this, “F9”);
    }

    private void setupLights() {
    AmbientLight al = new AmbientLight();
    al.setColor(new ColorRGBA(1f, 1f, 1f, 1f));
    rootNode.addLight(al);

     DirectionalLight sun = new DirectionalLight();
     sun.setDirection(new Vector3f(0f,-.25f,0f).normalizeLocal());
     sun.setColor(new ColorRGBA(1f,1f,1f,1f));
     rootNode.addLight(sun);
    

    }

    private void setupCamera() { }

    private void addSceneObjects() {
    Box obj1 = new Box(1,1,1);
    Material mat1 = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);
    mat1.setColor(“Diffuse”, ColorRGBA.Blue);
    mat1.setColor(“Ambient”, new ColorRGBA(0,0,0.25f,1));
    mat1.setBoolean(“UseMaterialColors”, true);
    Geometry obj1Geom = new Geometry();
    obj1Geom.setMesh(obj1);
    obj1Node = new Node();
    obj1Node.attachChild(obj1Geom);
    obj1Node.setMaterial(mat1);
    obj1Node.setLocalTranslation(-3,0,0);
    rootNode.attachChild(obj1Node);

     Sphere obj2 = new Sphere(8,8,1f);
     Material mat2 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
     mat2.setColor("Diffuse", ColorRGBA.Red);
     mat2.setColor("Ambient", new ColorRGBA(0.25f,0,0,1));
     mat2.setBoolean("UseMaterialColors", true);
     Geometry obj2Geom = new Geometry();
     obj2Geom.setMesh(obj2);
     obj2Node = new Node();
     obj2Node.attachChild(obj2Geom);
     obj2Node.setMaterial(mat2);
     obj2Node.setLocalTranslation(0,0,0);
     rootNode.attachChild(obj2Node);
     
     Torus obj3 = new Torus(8,8,0.5f,1f);
     Material mat3 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
     mat3.setColor("Diffuse", ColorRGBA.Green);
     mat3.setColor("Ambient", new ColorRGBA(0,0.25f,0,1));
     mat3.setBoolean("UseMaterialColors", true);
     Geometry obj3Geom = new Geometry();
     obj3Geom.setMesh(obj3);
     obj3Node = new Node();
     obj3Node.attachChild(obj3Geom);
     obj3Node.setMaterial(mat3);
     obj3Node.setLocalTranslation(3,0,0);
     rootNode.attachChild(obj3Node);
    

    }

    private void createGUIScreen() {
    screen = new Screen(this);
    screen.setUseUIAudio(true);
    screen.setUIAudioVolume(1f);
    guiNode.addControl(screen);

    }

    private void layoutGUI() {
    windowPadding.set(screen.getStyle(“Window#Dragbar”).getVector4f(“indents”));
    dragBarHeight = screen.getStyle(“Window#Dragbar”).getFloat(“defaultControlSize”);

     Element innerContent = new Element(
     	screen,
     	UIDUtil.getUID(),
     	new Vector2f(
     		windowPadding.x,
     		(windowPadding.y*2)+dragBarHeight
     	),
     	Vector2f.ZERO,
     	Vector4f.ZERO,
     	null
     );
     innerContent.setAsContainerOnly();
     
     for (int i = 0; i < 10; i++) {
     	float x = i*iconSize;
     	x += 5;
     	Element e = createInventorySlot(i, x, 0);
     	innerContent.addChild(e);
     }
     
     innerContent.sizeToContent();
     
     dim.set(
     	innerContent.getWidth()+(windowPadding.x*2),
     	innerContent.getHeight()+(windowPadding.y*3)+dragBarHeight
     );
     Window win = new Window(screen, Vector2f.ZERO, dim);
     win.addChild(innerContent);
     win.setlockToParentBounds(true);
     win.setIsResizable(false);
     screen.addElement(win);
    

    }

    private Element createInventorySlot(int index, float x, float y) {
    Element slot = new Element(
    screen,
    “InvSlot” + index,
    new Vector2f(x,y),
    new Vector2f(iconSize,iconSize),
    screen.getStyle(“CheckBox”).getVector4f(“resizeBorders”),
    screen.getStyle(“CheckBox”).getString(“defaultImg”)
    );
    slot.setIsDragDropDropElement(true);
    return slot;
    }

    private DragElement createNewDragElement() {
    DragElement e = new DragElement(
    screen,
    new Vector2f(
    screen.getMouseXY().x-(iconSize/2),
    screen.getHeight()-(screen.getMouseXY().y+(iconSize/2))

     	),
     	new Vector2f(iconSize,iconSize),
     	Vector4f.ZERO,
     	"tonegod/gui/style/def/Common/Particles/spark.png"
     ) {
     	@Override
     	public void onDragStart(MouseButtonEvent evt) {
     		
     	}
     	@Override
     	public boolean onDragEnd(MouseButtonEvent evt, Element dropElement) {
     		if (dropElement != null) {
     			setlockToParentBounds(false);
     			return true;
     		} else {
     			Node n = getUserData("worldObject");
     			rootNode.attachChild(n);
     			screen.removeElement(this);
     			return false;
     		}
     	}
     };
     e.setlockToParentBounds(true);
     e.setUseLockToDropElementCenter(true);
     e.setUseSpringBack(true);
     screen.addElement(e);
     return e;
    

    }

    @Override
    public void simpleUpdate(float tpf) {

    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }

    public void onAction(String binding, boolean value, float tpf) {
    if (binding.equals(“F9”)) {
    if (!value) {
    if (stateManager.hasState(vrAppState)) {
    System.out.println(“Stopping video recorder”);
    stateManager.detach(vrAppState);
    } else {
    System.out.println(“Starting video recorder”);
    stateManager.attach(vrAppState);
    }
    }
    }
    }

    public void beginInput() { }
    public void endInput() { }
    public void onJoyAxisEvent(JoyAxisEvent evt) { }
    public void onJoyButtonEvent(JoyButtonEvent evt) { }
    public void onMouseMotionEvent(MouseMotionEvent evt) { }
    public void onMouseButtonEvent(MouseButtonEvent evt) {
    if (evt.getButtonIndex() == 0 && evt.isPressed()) {
    click2d.set(inputManager.getCursorPosition());
    tempV2.set(click2d);
    click3d.set(cam.getWorldCoordinates(tempV2, 0f));
    pickDir.set(cam.getWorldCoordinates(tempV2, 1f).subtractLocal(click3d).normalizeLocal());
    pickRay.setOrigin(click3d);
    pickRay.setDirection(pickDir);
    rayResults.clear();

     	// Check for targeting collision
     	rootNode.collideWith(pickRay, rayResults);
     	closest = rayResults.getClosestCollision();
    
     	if (closest != null) {
     		Geometry geom = closest.getGeometry();
     		Node parent = geom.getParent();
     		DragElement de = createNewDragElement();
     		de.setUserData("worldObject", parent);
     		parent.removeFromParent();
     	//	inputManager.setCursorVisible(true);
     	}
     }
    

    }
    public void onKeyEvent(KeyInputEvent evt) { }
    public void onTouchEvent(TouchEvent evt) { }
    }
    [/java]

8 Likes

That’s some top notch monkey support right there @t0neg0d, tip of the hat to you madam!

3 Likes

I just noticed that I set the diffuse color in the materials and never set the useMaterialColors boolean to true. If you want pretty colored objects… add the following to createSceneObjects():

[java]
mat1.setBoolean(“UseMaterialColors”, true);
[/java]

Etc… and you’ll likely want to change the direction of the directional light :wink:

EDIT: You’ll also want to set the ambient color in the materials as well. >.<

EDIT 2: And… ignore this… I updated the example code to add the colors.

Thanks! The video looks great.

1 Like
@tuffe said: Thanks! The video looks great.

Since we are on the subject, and I am always open to adding to the functionality. I’d like to get your input on this as you go.

Here are my thoughts atm:

  • I should add nested z-order handling for any child Element that is movable but does not effect it’s parent. (basically, what you click comes to the front of the list of siblings… I added this to my local version and will likely add it to the library as a standard MO)
  • The DragElement should store a reference to it’s current locked drop element. This will make it easier for doing things like swapping positions if an object is already on the drop element you choose while dragging.
  • There was one other, but I can’t remember it atm.

Anyways, as you go through this effort, keep me up to date on your progress and how it works for you. If it is possible already, I’ll let you know… otherwise, perhaps we can make the drag/drop stuff a bit more robust.

How would i modify your code example to work with an arraylist of ints refering to an id for each item in the game? Right now i dont understand what is going on with the scene graph modifying in the example.

@8Keep123
In onMouseButtonEvent, I cast a ray into the scene.

[java]
if (closest != null) {
Geometry geom = closest.getGeometry();
Node parent = geom.getParent();
DragElement de = createNewDragElement();
de.setUserData(“worldObject”, parent);
parent.removeFromParent();
// inputManager.setCursorVisible(true);
}
[/java]

If there is a collision (which returns a geometry):

I call createNewDragElement() to create the new GUI component and store the geomety’s parent (The Node in your scene that was clicked) in the userData of the DragElement and then remove that Node from the scene.

Here, you could just as well add that to whatever structure (List, Data struct, etc) you use to represent you inventory.

In the Drag Element’s onDragEnd, I check to see if the drop component that is passed in is null or not. If it is not null, I was over a drop Element in the UI. In this case, I assume that it is an inventory drop element because there are no others.

If the drop Element is null, I put the Node back into the scene and remove the DragElement from the UI.

If it is not null, I remove the DragElement from the screen and add it as a child to the inventory window. I return true to let DragElement know that I want it to stick to the drop Element it is over.

During this process, you can manage your inventories data structure again.

– Hopefully this answer’s your question. Let me know if it didn’t

@t0neg0d

“Anyways, as you go through this effort, keep me up to date on your progress and how it works for you. If it is possible already, I’ll let you know… otherwise, perhaps we can make the drag/drop stuff a bit more robust.”

Sure! It might take a few days though.

1 Like
@tuffe said: @t0neg0d

“Anyways, as you go through this effort, keep me up to date on your progress and how it works for you. If it is possible already, I’ll let you know… otherwise, perhaps we can make the drag/drop stuff a bit more robust.”

Sure! It might take a few days though.

There is no rush, even if it’s months from now… just shout at me and let me know.

@tuffe
Hey there… just wanted to post an update to the inventory example to show how to limit the inventory slots to a single item and swap items when the slot is currently occupied. Hope you find it useful.

[java]
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.VideoRecorderAppState;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.RawInputListener;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Torus;
import tonegod.gui.controls.extras.DragElement;
import tonegod.gui.controls.windows.Window;
import tonegod.gui.core.Element;
import tonegod.gui.core.Screen;
import tonegod.gui.core.utils.UIDUtil;

/**

  • @author t0neg0d
    */
    public class AA_GUIDemo_Inventory extends SimpleApplication implements RawInputListener, ActionListener {
    VideoRecorderAppState vrAppState;

    private Screen screen;
    private int winCount = 0;
    private float iconSize = 40;
    Vector2f dim = new Vector2f();
    Vector4f windowPadding = new Vector4f();
    float dragBarHeight;
    private Node obj1Node, obj2Node, obj3Node;

    Vector2f click2d = new Vector2f(), tempV2 = new Vector2f();
    Vector3f click3d = new Vector3f(), pickDir = new Vector3f(), tempV3 = new Vector3f();
    Ray pickRay = new Ray();
    CollisionResults rayResults = new CollisionResults();
    CollisionResult closest;

    Window win;

    public static void main(String[] args) {
    AA_GUIDemo_Inventory app = new AA_GUIDemo_Inventory();

     app.start();
    

    }

    @Override
    public void simpleInitApp() {
    vrAppState = new VideoRecorderAppState();
    vrAppState.setQuality(0.35f);

     setupKeys();
     
     setupLights();
     setupCamera();
     
     createGUIScreen();
     layoutGUI();
     
     addSceneObjects();
     
     flyCam.setMoveSpeed(30);
     flyCam.setDragToRotate(true);
     inputManager.deleteMapping("FLYCAM_RotateDrag");
     inputManager.addMapping("FLYCAM_RotateDrag", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
     inputManager.setCursorVisible(true);
    

    }

    private void setupKeys() {
    inputManager.addRawInputListener(this);
    inputManager.addMapping(“F9”, new KeyTrigger(KeyInput.KEY_F9));
    inputManager.addListener(this, “F9”);
    }

    private void setupLights() {
    AmbientLight al = new AmbientLight();
    al.setColor(new ColorRGBA(1f, 1f, 1f, 1f));
    rootNode.addLight(al);

     DirectionalLight sun = new DirectionalLight();
     sun.setDirection(new Vector3f(0f,-.25f,0f).normalizeLocal());
     sun.setColor(new ColorRGBA(1f,1f,1f,1f));
     rootNode.addLight(sun);
    

    }

    private void setupCamera() { }

    private void addSceneObjects() {
    Box obj1 = new Box(1,1,1);
    Material mat1 = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);
    mat1.setColor(“Diffuse”, ColorRGBA.Blue);
    mat1.setColor(“Ambient”, new ColorRGBA(0,0,0.25f,1));
    mat1.setBoolean(“UseMaterialColors”, true);
    Geometry obj1Geom = new Geometry();
    obj1Geom.setMesh(obj1);
    obj1Node = new Node();
    obj1Node.attachChild(obj1Geom);
    obj1Node.setMaterial(mat1);
    obj1Node.setLocalTranslation(-3,0,0);
    rootNode.attachChild(obj1Node);

     Sphere obj2 = new Sphere(8,8,1f);
     Material mat2 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
     mat2.setColor("Diffuse", ColorRGBA.Red);
     mat2.setColor("Ambient", new ColorRGBA(0.25f,0,0,1));
     mat2.setBoolean("UseMaterialColors", true);
     Geometry obj2Geom = new Geometry();
     obj2Geom.setMesh(obj2);
     obj2Node = new Node();
     obj2Node.attachChild(obj2Geom);
     obj2Node.setMaterial(mat2);
     obj2Node.setLocalTranslation(0,0,0);
     rootNode.attachChild(obj2Node);
     
     Torus obj3 = new Torus(8,8,0.5f,1f);
     Material mat3 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
     mat3.setColor("Diffuse", ColorRGBA.Green);
     mat3.setColor("Ambient", new ColorRGBA(0,0.25f,0,1));
     mat3.setBoolean("UseMaterialColors", true);
     Geometry obj3Geom = new Geometry();
     obj3Geom.setMesh(obj3);
     obj3Node = new Node();
     obj3Node.attachChild(obj3Geom);
     obj3Node.setMaterial(mat3);
     obj3Node.setLocalTranslation(3,0,0);
     rootNode.attachChild(obj3Node);
    

    }

    private void createGUIScreen() {
    screen = new Screen(this);
    screen.setUseUIAudio(true);
    screen.setUIAudioVolume(1f);
    guiNode.addControl(screen);

    }

    private void layoutGUI() {
    windowPadding.set(screen.getStyle(“Window#Dragbar”).getVector4f(“indents”));
    dragBarHeight = screen.getStyle(“Window#Dragbar”).getFloat(“defaultControlSize”);

     Element innerContent = new Element(
     	screen,
     	UIDUtil.getUID(),
     	new Vector2f(
     		windowPadding.x,
     		(windowPadding.y*2)+dragBarHeight
     	),
     	Vector2f.ZERO,
     	Vector4f.ZERO,
     	null
     );
     innerContent.setAsContainerOnly();
     
     for (int i = 0; i &lt; 10; i++) {
     	float x = i*iconSize;
     	x += 5;
     	Element e = createInventorySlot(i, x, 0);
     	innerContent.addChild(e);
     }
     
     innerContent.sizeToContent();
     
     dim.set(
     	innerContent.getWidth()+(windowPadding.x*2),
     	innerContent.getHeight()+(windowPadding.y*3)+dragBarHeight
     );
     win = new Window(screen, Vector2f.ZERO, dim);
     win.addChild(innerContent);
     win.setlockToParentBounds(true);
     win.setIsResizable(false);
     screen.addElement(win);
    

    }

    private Element createInventorySlot(int index, float x, float y) {
    Element slot = new Element(
    screen,
    “InvSlot” + index,
    new Vector2f(x,y),
    new Vector2f(iconSize,iconSize),
    screen.getStyle(“CheckBox”).getVector4f(“resizeBorders”),
    screen.getStyle(“CheckBox”).getString(“defaultImg”)
    );
    slot.setIsDragDropDropElement(true);
    return slot;
    }

    private DragElement createNewDragElement() {
    DragElement e = new DragElement(
    screen,
    new Vector2f(
    screen.getMouseXY().x-(iconSize/2),
    screen.getHeight()-(screen.getMouseXY().y+(iconSize/2))

     	),
     	new Vector2f(iconSize,iconSize),
     	Vector4f.ZERO,
     	"tonegod/gui/style/def/Common/Particles/spark.png"
     ) {
     	@Override
     	public void onDragStart(MouseButtonEvent evt) {
     		if (getElementParent() != null) {
     			if (getElementParent() != this.getScreen()) {
     				float z = 0;
     				for (Element el : getElementParent().getElements()) {
     					if (el.getZOrder() &gt; z)
     						z = el.getZOrder();
     				}
     				z += screen.getZOrderStepMinor();
     				setZOrder(z);
     			}
     		}
     	}
     	@Override
     	public boolean onDragEnd(MouseButtonEvent evt, Element dropElement) {
     		if (dropElement != null) {
     			setlockToParentBounds(false);
     			if (!dropElement.getDraggableChildren().isEmpty()) {
     				if (!dropElement.getDraggableChildren().contains(this)) {
     					DragElement de1 = (DragElement)dropElement.getDraggableChildren().get(0);
     					Element drop1 = null;
     					if (getParentDroppable() != null) {
     						drop1 = getParentDroppable();
     						getParentDroppable().removeChild(this);
     						clearParentDroppable();
     					}
     					
     					if (de1 != null) {
     						de1.getParentDroppable().removeChild(de1);
     						de1.clearParentDroppable();
     					}
     					
     					if (drop1 != null &amp;&amp; de1 != null) {
     						de1.bindToDroppable(drop1);
     					} else {
     						Node n = de1.getUserData("worldObject");
     						Node n1 = getUserData("worldObject");
     						if (n != n1)
     							rootNode.attachChild(n);
     					}
     					evt.setConsumed();
     					return true;
     				} else {
     					return true;
     				}
     			} else {
     				return true;
     			}
     		} else {
     			if (getParentDroppable() != null) {
     				getParentDroppable().removeChild(this);
     				clearParentDroppable();
     			}
     			Node n = getUserData("worldObject");
     			rootNode.attachChild(n);
     			screen.removeElement(this);
     			return false;
     		}
     	}
     };
     e.setlockToParentBounds(true);
     e.setUseLockToDropElementCenter(true);
     e.setUseSpringBack(true);
     e.setUseSpringBackEffect(true);
     screen.addElement(e);
     return e;
    

    }

    @Override
    public void simpleUpdate(float tpf) {

    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }

    public void onAction(String binding, boolean value, float tpf) {
    if (binding.equals(“F9”)) {
    if (!value) {
    if (stateManager.hasState(vrAppState)) {
    System.out.println(“Stopping video recorder”);
    stateManager.detach(vrAppState);
    } else {
    System.out.println(“Starting video recorder”);
    stateManager.attach(vrAppState);
    }
    }
    }
    }

    public void beginInput() { }
    public void endInput() { }
    public void onJoyAxisEvent(JoyAxisEvent evt) { }
    public void onJoyButtonEvent(JoyButtonEvent evt) { }
    public void onMouseMotionEvent(MouseMotionEvent evt) { }
    public void onMouseButtonEvent(MouseButtonEvent evt) {
    if (evt.getButtonIndex() == 0 && evt.isPressed()) {
    click2d.set(inputManager.getCursorPosition());
    tempV2.set(click2d);
    click3d.set(cam.getWorldCoordinates(tempV2, 0f));
    pickDir.set(cam.getWorldCoordinates(tempV2, 1f).subtractLocal(click3d).normalizeLocal());
    pickRay.setOrigin(click3d);
    pickRay.setDirection(pickDir);
    rayResults.clear();

     	// Check for targeting collision
     	rootNode.collideWith(pickRay, rayResults);
     	closest = rayResults.getClosestCollision();
    
     	if (closest != null) {
     		Geometry geom = closest.getGeometry();
     		Node parent = geom.getParent();
     		DragElement de = createNewDragElement();
     	//	evt.setConsumed();
     	//	de.bindToDroppable(screen.getElementById("InvSlot1"));
     		de.setUserData("worldObject", parent);
     		parent.removeFromParent();
     	}
     }
    

    }
    public void onKeyEvent(KeyInputEvent evt) { }
    public void onTouchEvent(TouchEvent evt) { }
    }
    [/java]

1 Like

@t0neg0d Thanks! I’m playing around with blender at the moment, but I will get back to coding eventually. :wink:

I’m working on an inventory similar to this as well, but I’m having some problems with Z-ordering. I took a look at this example to see what I might be doing wrong, but it turns out that the same issues occur. There are two:

  1. It looks like the code under onDragStart() is meant to bring the dragged element to the front, but this doesn’t always work. I’m not sure why, but I can reproduce it by following these steps:
  • Move the blue, red, and green objects to the inventory in that order, and place them in the same order.
  • Pick up and drag the red draggable; it appears above the other two.
  • Pick up and drag the blue draggable; it moves over the green one, but behind the red one.
  1. If you drag those elements around too much, it breaks the Z-order. The drag bar will be pushed behind another element and may become unclickable. You can test this with this example just by adding a panel or some other element to the scene, then moving the drag bar in front of it and playing with the draggables for a while.

I’ve tried a number of ways to get this working in my own code, such as calling Screen.updateZOrder() on the draggable in onDragStart(), but everything I do produces similar problems.

One thing I’ve noticed is that Screen.removeElement() can break the Z-order if you call it on an element that isn’t attached to the screen. It doesn’t actually check that before updating the Z-order, so multiple bad removeElement() calls can produce issue #2. It seems that it should either throw an exception like addElement() does, or at least not touch the Z-order unless the element is actually found and removed. I don’t know if that’s directly related here, but there could be a similar Z-order leak somewhere else.

While we’re on the subject, is there any way that the simulated mouse click in DragElement.bindToDroppable() can be replaced with more focused code? I’m filling an inventory screen with draggables built from the player’s existing inventory, and I’ve had to code around this behavior in my onDragEnd() method. Am I just overlooking an easier way?

@Mentalepsy said: I'm working on an inventory similar to this as well, but I'm having some problems with Z-ordering. I took a look at this example to see what I might be doing wrong, but it turns out that the same issues occur. There are two:
  1. It looks like the code under onDragStart() is meant to bring the dragged element to the front, but this doesn’t always work. I’m not sure why, but I can reproduce it by following these steps:
  • Move the blue, red, and green objects to the inventory in that order, and place them in the same order.
  • Pick up and drag the red draggable; it appears above the other two.
  • Pick up and drag the blue draggable; it moves over the green one, but behind the red one.
  1. If you drag those elements around too much, it breaks the Z-order. The drag bar will be pushed behind another element and may become unclickable. You can test this with this example just by adding a panel or some other element to the scene, then moving the drag bar in front of it and playing with the draggables for a while.

I’ve tried a number of ways to get this working in my own code, such as calling Screen.updateZOrder() on the draggable in onDragStart(), but everything I do produces similar problems.

One thing I’ve noticed is that Screen.removeElement() can break the Z-order if you call it on an element that isn’t attached to the screen. It doesn’t actually check that before updating the Z-order, so multiple bad removeElement() calls can produce issue #2. It seems that it should either throw an exception like addElement() does, or at least not touch the Z-order unless the element is actually found and removed. I don’t know if that’s directly related here, but there could be a similar Z-order leak somewhere else.

While we’re on the subject, is there any way that the simulated mouse click in DragElement.bindToDroppable() can be replaced with more focused code? I’m filling an inventory screen with draggables built from the player’s existing inventory, and I’ve had to code around this behavior in my onDragEnd() method. Am I just overlooking an easier way?

Yeah… I noticed this as well. Best thing to do is remove the draggable from the inventory window and add it to the screen while dragging it around. If it hits an acceptable drop area, re-add it to the window and remove from the screen, etc, etc. The library doesn’t really have nested z-order handling past last added = top.

And, for the other problem, lemme have some time to mill this one over. The reason this is being done this way is, to avoid having problems arise in the future when people a) extend the DragElement class, b) change effects/default configs, c) etc, etc. Simulating the mouse click = will work no matter what. Unfortunately this comes at the cost of having to valid something you set and may be expecting from onDragStart().

Thanks for the tip. I was able to get this working pretty well using that technique, though getting it right was a bit finicky. When I have a chance, I’ll migrate it back into your example code and post it here, so others can see what I did.

1 Like