Creating a Drag and Drop Spell Bar

From Unify Community Wiki
Revision as of 18:48, 17 September 2010 by Fishypants (Talk | contribs)

Jump to: navigation, search

Implementing a Drag and Drop Spell Bar:
by Mike Cook.


Contents

Description:

I am sure this has been covered somewhere in the Unity Forums, but I couldn't find any relevant information when I was starting out on this, so hopefully this tutorial will help someone starting out with Unity. Basically we will be mimicking the spell bar seen in such games as World of Warcraft, or Aion.

Step 1: Plan what you want to do before you do it

I have a very bad habit of "rushing" into things. I get an idea, and I HAVE TO IMPLEMENT IT THAT VERY SECOND. So I quickly slap things together in my main script and it usually results in a mess. I can't stress enough the fact that careful planning will go a long way. For our custom drag and drop spell bar, we want to implement the following features:

  • Easily add items to the spell bar.
  • Drag and drop items off spell bar to delete them.
  • Drag and drop item into empty slot to switch its location.
  • Drag and drop item into occupied slot to swap the items.
  • Restrict certain items from going into certain slots.
  • Drag and drop feature will be "sticky" meaning the user will have to drag a ways for the icon to disengage from the slot.
  • Clicking an item will activate it, or use it.
  • Right clicking while dragging will cancel the currently dragged item.

Unity out of the box does not support any button that will accomplish what we want to do (that I am aware of). The default GUI.Button only registers when you release the button, and the Repeat Button only registers when the user is holding the mouse down while above the button. It became clear when I first started this project that I would have to implement my own buttons and ditch Unity's all together.

We actually only need to know 2 things to implement this drag and drop system:

  • Where the mouse is when the user presses the mouse button down.
  • Where the mouse is when the user releases the mouse button.

Thats pretty much it. It will get a little more complicated then that, but those are the key points to get the drag and drop system to work. One of the hardest parts of this script was "knowing where the mouse is" at all times. I struggled with different ways to loop through different Rect's and try to figure out where the mouse was at. Then I had an idea, what if we check if the mouse is inside a button, as we draw it? It is definitely the way to go, and so we will create our own function to draw each icon that records if the mouse is inside it or not.

Ok so on to the rest of the tutorial. If you haven't yet learned these features, you will know them pretty well by the time we are done. For our drag and drop system we will be using:

  • Classes
  • Multi-Dimensional Arrays.
  • Passing variables by Reference (kinda similar to pointers in C++)

Sounds confusing and scary doesn't it? It's not, just bare with me and I will explain them to you as we go.

Step 2: Creating the backbone

First and foremost, create a new project in Unity, and chose not to import any of the standard packages. We will be working with a clean scene.

I have created some test icons we can use and can be downloaded here: Drag and Drop Icons.zip

So download the zip and extract the icons. Import them into your project using Assets > Import new asset...

I like nice, clean, crisp images when working with the GUI, so click on each image and in the Insepector set the Texture Format to ARGB32, and click the apply button. Our system will rely on manually loading resources, so create a Resources folder in your project and dump all the images in there. In case you haven't done this before, if you make a Resources folder, anything you put in there you can load using Resources.Load()

Ok, now we need an empty GameObject and a script to control the HUD. Create an empty GameObject, name it something like _Setup (or whatever you want), create a new javascript and name it HUD and attach it to the GameObject. Now open the HUD script and delete everything inside and paste this in there:

JavaScript - HUD.js

<javascript> private var highlight : Texture2D = Resources.Load("slot-highlight"); private var activated : Texture2D = Resources.Load("slot-active-highlight"); private var slotBG : Texture2D = Resources.Load("slots-bg");

private var clickInfo = new Array(); // Information where the mouse has clicked private var hoverInfo = new Array(); // Information where the mouse is currently hovering private var dragMode : boolean = false; // If we are dragging or not

function Start(){ } function Update (){ } function OnGUI(){ } </javascript>

At the top we declare a highlight, activated, and slotBG variables and tell Unity to load the corresponding textures at startup. We also declared a clickInfo array, a hoverInfo array, and a dragMode boolean.

The drag and drop system will basically work like this:
The hoverInfo array will constantly store information about what icon the mouse is currently hovering over, or it will be empty if we aren't over anything. If the user clicks the mouse button down, and they are over an icon, hoverInfo will copy everything it has to the clickInfo array. When the user releases the mouse button, we do all the checks to determine what happened, and if we drag and dropped something. A simple example would be, if hoverInfo is not the same as clickInfo, then we dragged the icon from one slot to another. If hoverInfo and clickInfo are the same, then we just pressed the button, etc.

Step 3: Continuing on

Ok so under the OnGUI() function, put this code: <javascript> function OnGUI(){ // Reset mouse over info to null hoverInfo = new Array(); } </javascript> This is very improtant, because the hoverInfo array needs to be reset each OnGUI() cycle. So what we are doing is at the start of each OnGUI() cycle, we reset the array, then we draw all the icons. If we are inside one of the icons, the hoverInfo array will be filled out with all the information about that icon. If we continue to keep the mouse over that icon, it will get reset at the begining of OnGUI() and be set back to whatever icon we are hovering over. Confusing yet? No? Good! :)

Now lets add the slot backgrounds and buttons. Add the following code to your OnGUI() function: <javascript> function OnGUI(){ // Reset mouse over info to null hoverInfo = new Array();

// Draw the slot backgrounds GUI.DrawTexture(Rect(200,200,349,30), slotBG); GUI.DrawTexture(Rect(200,234,349,30), slotBG);

if(GUI.Button(Rect(200,300,200,25), "Add items to action bar")){ } if(GUI.Button(Rect(200,330,200,25), "Add items to bag bar")){ } } </javascript> We will leave the buttons blank for now and fill them in later. Lets create the structures that will actually hold our icons! For this we will be using multi-dimensional arrays. If you haven't used those before, basically its a fancy word for an array of arrays. So at the top of our script lets declare 2 new variables, actionList and bagList. These correspond to the two slot bars that we have already made. So our script should look like this so far: <javascript> private var highlight : Texture2D = Resources.Load("slot-highlight"); private var activated : Texture2D = Resources.Load("slot-active-highlight"); private var slotBG : Texture2D = Resources.Load("slots-bg");

private var clickInfo = new Array(); // Information where the mouse has clicked private var hoverInfo = new Array(); // Information where the mouse is currently hovering private var dragMode : boolean = false; // If we are dragging or not

private var actionList = new Array(); // List of action classes, rects, and requirements private var bagList = new Array(); // List of bag classes, rects, and requirements

function Start(){ } function Update (){ } function OnGUI(){ // Reset mouse over info to null hoverInfo = new Array();

// Draw the slot backgrounds GUI.DrawTexture(Rect(200,200,349,30), slotBG); GUI.DrawTexture(Rect(200,234,349,30), slotBG);

if(GUI.Button(Rect(200,300,200,25), "Add items to action bar")){ } if(GUI.Button(Rect(200,330,200,25), "Add items to bag bar")){ } } </javascript> Now we created the actionList and bagList arrays, but we need them to be multi-dimensional, right now they would just be a single list, so under the Start() function, add the following code: <javascript> function Start(){ // Set actionList attributes actionList = new Array(12); for(i=0; i<actionList.length; i++) actionList[i] = new Array(4); for(i=0; i<actionList.length; i++){ actionList[i][1] = Rect(203+(i*29),203, 24, 24); // Set slot rect actionList[i][2] = new Array("action"); // Set slot restrictions } // Set actionList keymappings actionList[0][3] = KeyCode.Alpha1; actionList[1][3] = KeyCode.Alpha2; actionList[2][3] = KeyCode.Alpha3; actionList[3][3] = KeyCode.Alpha4; actionList[4][3] = KeyCode.Alpha5; actionList[5][3] = KeyCode.Alpha6; actionList[6][3] = KeyCode.Alpha7; actionList[7][3] = KeyCode.Alpha8; actionList[8][3] = KeyCode.Alpha9; actionList[9][3] = KeyCode.Alpha0; actionList[10][3] = KeyCode.Minus; actionList[11][3] = KeyCode.Equals;

// Set bagList attributes bagList = new Array(12); for(i=0; i<bagList.length; i++) bagList[i] = new Array(3); for(i=0; i<bagList.length; i++){ bagList[i][1] = Rect(203+(i*29), 237, 24, 24); // Set slot rect bagList[i][2] = new Array("item", "bag", "action"); // Set slot restrictions } } </javascript> Kinda confusing at first, so lets take a look at the bagList attributes. The actionList section is the same, but with the KeyCode section added. First we set bagList to a new Array(12), meaning we want a list that contains 12 entries (we have 12 slots per slot bar). Then we have a For Loop that makes a new Array(3) in each one of the bagList entries. So a list thats 3 entries long inside each entry in the main list. Then we have another For Loop that sets values in the 2nd and 3rd entry but leaves the 1st one blank. Our lists will have the following structure: <javascript> bagList[0] - [0]item class [1]slot Rect [2]slot restrictions bagList[1] - [0]item class [1]slot Rect [2]slot restrictions bagList[2] - [0]item class [1]slot Rect [2]slot restrictions bagList[3] - [0]item class [1]slot Rect [2]slot restrictions bagList[4] - [0]item class [1]slot Rect [2]slot restrictions bagList[5] - [0]item class [1]slot Rect [2]slot restrictions bagList[6] - [0]item class [1]slot Rect [2]slot restrictions bagList[7] - [0]item class [1]slot Rect [2]slot restrictions bagList[8] - [0]item class [1]slot Rect [2]slot restrictions bagList[9] - [0]item class [1]slot Rect [2]slot restrictions bagList[10] - [0]item class [1]slot Rect [2]slot restrictions bagList[11] - [0]item class [1]slot Rect [2]slot restrictions </javascript> So for each slot in bagList[i], inside it we have access to what icon class is stored in the slot bagList[i][0], the slot Rect bagList[i][1] and any slot restrictions that the slot might have bagList[i][2]. You can drill down to each sub list by putting more [] after the index that you want (but only up to how many levels you make). The actionList is exactly the same, but we built an array that has 4 indexes per slot instead of 3 like the bagList, and we store which KeyCode corresponds to that slot. We could have done this with multiple Arrays, but I find it easier / cleaner to just work with one.

Step 4: Custom Functions!

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox