Game Development Community

Drag and Drop Inventory system.

by Luke Griffin · in Torque 3D Professional · 04/03/2011 (1:14 pm) · 15 replies

Hey all,

I'm trying to make a drag and drop inventory system and I'm having a few problems. I would like to create a system that is split into two parts. Items the player is carrying and items that are currently equipped in their hands. Here is an example image:

http://i1215.photobucket.com/albums/cc512/griffinendurance/Luke%20Griffins%20Album/invent.jpg

I have just thrown together some icons to test the concept as it includes all types of items, large and small. As you can see it will use a 4X4 grid layout. You cant rotate images just move them around. You will also need to be able to save and check what space is free for item pickups and have some stackable items. I am focusing on the GUI stuff first before I look at getting it working with the player item system. Unfortunately, I have spent several weeks looking for resources or tutorials on drag and drop functionality of this type, and have been unsuccessful in finding anything I can recreate. I was hoping that someone else has attempted this before, or has some incite into how I could do it. If anyone has any ideas, script snippets or links to resources, it would be much appreciated.

#1
04/03/2011 (2:11 pm)
Haven't used it but there is a guidraganddropctrl.

Looks like it implements a standard way of drag and drop, notifying other objects as it is dragged across them. It is all documented in the script docs (and the source).

Briefly: seems that you construct the DnD control on the fly (ie on button press: %mydndctrl = new GuiDragAndDropControl(){...} ), populate it with a payload (ie %mydndctrl.add(mypayloadbitmapctrl)) then your drop zone should have a listener activated on the drop (ie %dropzone.onControlDropped(){ dosomething(); } ). There are also other callbacks to deal with the other situations. You will need to change the above and fill in the blanks for your particular situation, of course.

Mileage may vary on whether or not it works properly as I don't know of any current usage. Would like to hear your results!
#2
04/03/2011 (2:14 pm)
Oops, I take that back. It is used in swatchbuttons.ed.cs, the guieditortoolbox.ed.cs, and the terraineditor.ed.cs.

Look in those files for better examples.
#3
04/03/2011 (3:23 pm)
I recently setup a drag drop gui, i cant find the link to the resource i used to set it up. but ive got this example gui from the resource that you could use as a template to get the basic draggin and droppin working
#4
04/03/2011 (3:24 pm)
//--- OBJECT WRITE BEGIN ---
%guiContent = new GuiControl(DragDropGui) {
position = "0 0";
extent = "1024 768";
minExtent = "8 8";
horizSizing = "right";
vertSizing = "bottom";
profile = "GuiDefaultProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "1";
canSave = "1";
canSaveDynamicFields = "1";
helpTag = "0";

new GuiBitmapCtrl(background) {
bitmap = "art/gui/inv/stone_back";
wrap = "0";
position = "156 84";
extent = "620 460";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "GuiDefaultProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiBitmapCtrl() {
bitmap = "art/gui/inv/tile.png";
wrap = "0";
position = "231 140";
extent = "64 64";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "GuiDefaultProfile";
visible = "1";
active = "1";
command = "DragDropGui::StartDrag (%this);";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiBitmapCtrl() {
bitmap = "art/gui/inv/tile.png";
wrap = "0";
position = "560 143";
extent = "64 64";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "GuiDefaultProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiBitmapButtonCtrl(droptarget) {
bitmapMode = "Stretched";
autoFitExtents = "0";
groupNum = "-1";
buttonType = "PushButton";
useMouseEvents = "1";
position = "565 149";
extent = "55 52";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "GuiDefaultProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};

new GuiBitmapButtonCtrl(Source) {
bitmap = "art/gui/inv/knife.png";
bitmapMode = "Stretched";
autoFitExtents = "0";
groupNum = "-1";
buttonType = "PushButton";
useMouseEvents = "1";
position = "238 146";
extent = "49 50";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "GuiDefaultProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
};
//--- OBJECT WRITE END ---

function DragDropGui::StartDrag (%this)
{
echo("1");
}
function droptarget::onControlDragged(%this,%payload,%position)
{
echo("2");
}
function droptarget::onControlDragEntered(%this,%payload,%position)
{
echo("3");
}
function droptarget::onControlDragExit(%this,%payload,%position)
{
echo("4");
}
function droptarget::onControlDropped(%this,%payload,%position)
{
%this.bitmap=%payload.bitmap;
echo("5");
}

function Source::onMouseDragged(%this)
{
echo ("dragged");
echo ("bitmap = " @ %this.bitmap);
%position = %this.getGlobalPosition();
%cursorpos = Canvas.getCursorPos();

%payload = new GuiBitmapCtrl() {
profile = "GuiDefaultProfile";
horizSizing = "width";
vertSizing = "height";
position = "0 0";
extent = "32 32";
minExtent = "8 8";
visible = "1";
helpTag = "0";
bitmap = %this.bitmap;
wrap = "0";
};
#5
04/03/2011 (3:25 pm)
%xOffset = getWord(%payload.extent, 0) /2;
%yOffset = getWord(%payload.extent, 1) / 2;

// position where the drag will start, to prevent visible jumping. Down and to the right also
%xPos = getWord(%cursorpos, 0) + 32; //%xOffset;
%yPos = getWord(%cursorpos, 1) + 32; //%yOffset;

//Now create the drag and drop control which will be deleted when mouse is up, delivering its payload
%dragCtrl = new GuiDragAndDropControl() {
canSaveDynamicFields = "0";
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = %xPos SPC %yPos;
extent = %payload.extent;
MinExtent = "32 32";
canSave = "1";
Visible = "1";
hovertime = "1000";
deleteOnMouseUp = true;
};

%dragCtrl.add(%payload);
Canvas.getContent().add(%dragCtrl);

//We want the new dragdrop to be offset from the cursor
%dragCtrl.startDragging(%xOffset -32, %yOffset - 32);
}

//-------------------------------------------------------------
worked for me :)
#6
04/04/2011 (12:29 am)
@x_ming_x

I think I came accross your post a few days ago, I have just copied and pasted all of that into a DragDropGui.gui file and added it to a key binding, but when I click on the icon to drag it nothing happens?
#7
04/04/2011 (4:03 pm)
hiya

hmm did you change the images to your own. i remember i had some issues with visibility that i fixed by going through the echos. maybe a typo somewhere that i fixed, few months ago now so its all a bit hazy.

my drag and drop gui (based on this) is definately working so its doable

if your really stuck i spose you could send me your gui to have a look at.
#8
04/04/2011 (4:09 pm)
oh and did you init the dragdrop.gui
#9
04/05/2011 (1:53 pm)
I've found a possible problem. I am experimenting with changing the gui cursor insted of actually dragging an image, that way it will only require a mouse button down and up function for each icon. However, although ::onMouseClick seems to work, ::onMouseDragged, ::onMouseDown and others don't seem to be working. I can get everything to work on a click but Is there a feature that would let me perform one action for when the mouse button is clicked and another for when it is released? The fact that onMouseDragged is not working could also explain why x_ming_x's suggestion is not working for me.
#10
04/05/2011 (2:57 pm)
hi luke

mousedragged does definately work in my gui

ive had to write a function for the mousedragged for each draggable item (in my script im calling them tiles)

heres an onmousedragged func from my gui maybe itll help

function DragDropGui::StartDrag (%this)
{
echo("1");
}

// functions for each droptarget

function droptarget1::onControlDragged(%this,%payload,%position)
{
echo("2");
}
function droptarget1::onControlDragEntered(%this,%payload,%position)
{
echo("3");
}
function droptarget1::onControlDragExit(%this,%payload,%position)
{
echo("4");
}
function droptarget1::onControlDropped(%this,%payload,%position)
{

%this.bitmap=%payload.bitmap;
echo("5");
}

function droptarget1::onMouseDragged(%this)
{
echo ("dragged");
echo ("bitmap = " @ %this.bitmap);
%position = %this.getGlobalPosition();
%cursorpos = Canvas.getCursorPos();

%payload = new GuiBitmapCtrl() {
profile = "GuiDefaultProfile";
horizSizing = "width";
vertSizing = "height";
position = "0 0";
extent = "200 200";
minExtent = "8 8";
visible = "1";
helpTag = "0";
bitmap = %this.bitmap;
wrap = "0";
};

%xOffset = getWord(%payload.extent, 0) /2;
%yOffset = getWord(%payload.extent, 1) / 2;

// position where the drag will start, to prevent visible jumping. Down and to the right also
%xPos = getWord(%cursorpos, 0) + 32; //%xOffset;
%yPos = getWord(%cursorpos, 1) + 32; //%yOffset;

//Now create the drag and drop control which will be deleted when mouse is up, delivering its payload
%dragCtrl = new GuiDragAndDropControl() {
canSaveDynamicFields = "0";
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = %xPos SPC %yPos;
extent = %payload.extent;
MinExtent = "190 190";
canSave = "1";
Visible = "1";
hovertime = "1000";
deleteOnMouseUp = true;
};

%dragCtrl.add(%payload);
Canvas.getContent().add(%dragCtrl);

//We want the new dragdrop to be offset from the cursor
%dragCtrl.startDragging(%xOffset -32, %yOffset - 32);
}
#11
04/05/2011 (7:34 pm)
Quote:
However, although ::onMouseClick seems to work, ::onMouseDragged, ::onMouseDown and others don't seem to be working

I was debugging a similar issue, and it was because "useMouseEvents" wasn't set to true on the control I was listenening on.

The callbacks don't fire on the control without it set to true, although I have noticed some other controls like guiObjectView swallow the events because the parent::onMouseDragged isn't called.

%ctrl = new GuiBitmapButtonTextCtrl(){
  
  class = "MyClass";
  useMouseEvents = true; 
  
  ...
  
};
   
function MyClass::onMouseDragged( %this )
{ 
  echo("woot");
}
#12
04/06/2011 (7:06 am)
Thanks for all your replies so far. @Joshua, useMouseEvents = true; worked to a certain degree, I can now get ::onMouseDown and ::onMouseUp working which is what I wanted for my idea, however ::onMouseDragged is still not having and effect what so ever.

I now have a click function that changes the cursor two the selected item and then returns to normal cursor when the mouse button is released. What I would like to do is make a series of If statments to determine whether the item can be placed where the cursor is. I would like to do it so that when your mouse is over a "empty" slot image, the weapon can go there "so check to see if an image is visible under the cursor" does anyone have any idea how to do this? Also I need to find a way to automatically set the cursor size to match that of the item you are selecting in multiple resolutions, is there an option to change custom cursors size?
#13
04/07/2011 (9:30 am)
OK Perhaps I should post more info and some code. The system I have come up with removes the need for a DragAndDrop Control as this control I found to be extremely unstable and crashes my game a lot. It works as following:

1. There are three sets of images:
- The first are empty boxes
- The second is the weapon or item with a box background to act as the static slot images "Image that is visible when you are just looking at the inventory and you have said item"
- The third are cursor replacements for the cursor so I can "Drag" the item to a slot.

My inventoryGUI.gui (I obviously have a background and other stuff too)

new GuiBitmapButtonCtrl(grid1) {
      bitmap = "art/gui/inventory/guielements/grid1";
      groupNum = "-1";
      buttonType = "PushButton";
      useMouseEvents = "0";
      isContainer = "0";
      Profile = "GuiButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "231 215";
      Extent = "76 75";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      tooltipprofile = "GuiToolTipProfile";
      hovertime = "1";
      canSaveDynamicFields = "0";
      Enabled = "1";
   };

      new GuiBitmapButtonCtrl(grid2) {
      bitmap = "art/gui/inventory/guielements/grid1";
      groupNum = "-1";
      buttonType = "PushButton";
      useMouseEvents = "0";
      isContainer = "0";
      Profile = "GuiButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "400 400";
      Extent = "76 75";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "0";
      tooltipprofile = "GuiToolTipProfile";
      hovertime = "1";
      canSaveDynamicFields = "0";
      Enabled = "1";
   };

   new GuiBitmapButtonCtrl(PistolInventoryIcon) {
      bitmap = "art/gui/inventory/items/pistol1";
      groupNum = "-1";
      buttonType = "PushButton";
      useMouseEvents = "1";
      isContainer = "0";
      Profile = "GuiButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "231 215";
      Extent = "76 75";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      tooltipprofile = "GuiToolTipProfile";
      hovertime = "1";
      canSaveDynamicFields = "0";
      Enabled = "1";
   };

Scripts/Client/customcursors.cs:

new GuiCursor(MyDefaultCursor)
{
  hotSpot = "1 1";
  renderOffset = "0 0";
  bitmapName = "/art/gui/cursor.png";
};

//Inventory Cursors

new GuiCursor(PistolDragIcon)
{
  hotSpot = "64 64";
  renderOffset = "0 0";
  bitmapName = "/art/gui/inventory/items/Pistol1drag.png";
};

I then have a .cs file to control mouse events. In this file I instruct the cursor to be changed and the PistolInventoryIcon to be hidden and the grid1 image to be made visible. This happens when the player starts their "drag". I then run another function when the mouse button is released.

Scripts/Client/inventoryGUI.cs
function PistolInventoryIcon::onMouseDown(%this)
{
  Canvas.setCursor(PistolDragIcon);
  echo("Pistol Button Pressed");
}

function PistolInventoryIcon::onMouseUp(%this)
{
   Canvas.setCursor(MyDefaultCursor);
  echo("Button Released");
}

Now what I am looking for is a way of determining where the mouse is on mouseup and then if it is a free slot. There will be 16 in all so it needs to be able to check what image is under the cursor. Needless to say I am out of ideas, Any help would be great.
#14
04/08/2011 (8:15 am)
hi luke
im not sure how it would work using the canvas.setcursor (not been there yet )

in my version its this bit of script that handles moving the image with the cursor

//Now create the drag and drop control which will be deleted when mouse is up, delivering its payload
%dragCtrl = new GuiDragAndDropControl() {
canSaveDynamicFields = "0";
Profile = "GuiDefaultProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = %xPos SPC %yPos;
extent = %payload.extent;
MinExtent = "190 190";
canSave = "1";
Visible = "1";
hovertime = "1000";
deleteOnMouseUp = true;
};

as for the logic stuff i would imagine a good place to start is "onControlDropped(%this,%payload,%position)"

:)

#15
04/08/2011 (9:27 am)
Thanks for the reply, I've found a not so elegant method of doing it and its currently working well at the moment, I was able to solve the problem with the mouse cursor by just keeping the menu the same size reguardless of resolution rather then scaling it up on large resolutions. I still cant get the ::onMouseDragged to work at all, purhaps I should look into that further in case I need that in the future.