Download as pdf or txt
Download as pdf or txt
You are on page 1of 1

Inventory Tutorial

In this tutorial we will make a drag and drop inventory like this.

1 Setting up the class


Let's begin by making an Inventory class

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__()

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
app.run()

2 Adding graphics
However, if we run the code, we'll see that there's nothing visible.
Let's parent it to the ui and set the model to 'quad', an included model.

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__(
parent = camera.ui,
model = 'quad'
)

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
app.run()

If we look at it now, we see it's a white square in the middle of the screen.
Let's set it to a nicer shape. We also want (0,0) to be in the upper left corner
because it makes it easier to add items later. Let's also give it a texture and a color.
If we have both, the color value will tint the texture.

Now, this is nice and all, but wouldn't it be nice to show a grid as well?
There are multiple way to do that, but in this case, we'll simply make the texture repeat
by setting texture_scale to (5,6). That'll nicely fit the size of our inventory.

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__(
parent = camera.ui,
model = 'quad',
scale = (.5, .8),
origin = (-.5, .5),
position = (-.3,.4),
texture = 'white_cube',
texture_scale = (5,8),
color = color.dark_gray
)

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
app.run()

3 Adding placeholder items


Let's try to add some items to the inventory.

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__(
parent = camera.ui,
model = 'quad',
scale = (.5, .8),
origin = (-.5, .5),
position = (-.3,.4),
texture = 'white_cube',
texture_scale = (5,8),
color = color.dark_gray
)

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
item = Button(parent=inventory, color=color.red, position=(0,0))
item = Button(parent=inventory, color=color.green, position=(2,0))
app.run()

Well, that didn't go as planned. The items cover the entire inventory and
the second item is way off to the left.
Let's fix this by making another object to put the items under.
Scale the object to the size of an item.

They also don't fit the grid. Fix that by setting origin to the upper left
corner, (-.5,.5).

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__(
parent = camera.ui,
model = 'quad',
scale = (.5, .8),
origin = (-.5, .5),
position = (-.3,.4),
texture = 'white_cube',
texture_scale = (5,8),
color = color.dark_gray
)

self.item_parent = Entity(parent=self, scale=(1/5,1/8))

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
item = Button(parent=inventory.item_parent, origin=(-.5,.5), color=color.red, position=(0,0))
item = Button(parent=inventory.item_parent, origin=(-.5,.5), color=color.green, position=(2,0))
app.run()

4 Adding items
We've added some items manually to make sure they get the right scale and position,
but we should make an append() function so it's easy to add items.

Let's start by making a function called 'append()' and make it spawn an item
when we send it a string. We'll also set the button's text to the string we
receive so we can differentiate them.
Let's give them a random color too, why not.

Lastly, let's call inventory.append('test item') a couple of times to make sure it works.

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__(
parent = camera.ui,
model = 'quad',
scale = (.5, .8),
origin = (-.5, .5),
position = (-.3,.4),
texture = 'white_cube',
texture_scale = (5,8),
color = color.dark_gray
)

self.item_parent = Entity(parent=self, scale=(1/5,1/8))

def append(self, item):


Button(
parent = inventory.item_parent,
model = 'quad',
origin = (-.5,.5),
color = color.random_color(),
z = -.1
)

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
inventory.append('test item')
inventory.append('test item')
app.run()

5 Find a free slot in the inventory


The items gets added, but they overlap. We need to find the first open slot in the inventory
and place the item there. We can to this by checking each grid position and see if any
if the items occupy that position already.

from ursina import *

class Inventory(Entity):
def __init__(self):
super().__init__(
parent = camera.ui,
model = 'quad',
scale = (.5, .8),
origin = (-.5, .5),
position = (-.3,.4),
texture = 'white_cube',
texture_scale = (5,8),
color = color.dark_gray
)

self.item_parent = Entity(parent=self, scale=(1/5,1/8))

def find_free_spot(self):
taken_spots = [(int(e.x), int(e.y)) for e in self.item_parent.children]
for y in range(8):
for x in range(5):
if not (x,-y) in taken_spots:
return (x,-y)

def append(self, item):


Button(
parent = inventory.item_parent,
model = 'quad',
origin = (-.5,.5),
color = color.random_color(),
position = self.find_free_spot(),
z = -.1
)

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()
for i in range(7):
inventory.append('test item')
app.run()

6 Add random item button


Make a button to add a random item to the inventory. This is not part of the inventory itself,
but it's useful in order to test it.
Assign the button's 'on_click' to a function, and it will call that function when we click it.
button.on_click = inventory

Let's make an tuple with those and make the button choose a random item from the tuple
using random.choice(items)

if __name__ == '__main__':
app = Ursina()
inventory = Inventory()

def add_item():
inventory.append(random.choice(('bag', 'bow_arrow', 'gem', 'orb', 'sword')))

for i in range(7):
add_item()

add_item_button = Button(
scale = (.1,.1),
x = -.5,
color = color.lime.tint(-.25),
text = '+',
tooltip = Tooltip('Add random item'),
on_click = add_item
)

app.run()

7 Adding textures and hover text


To add textures, just set texture=item and it will try to find a
texture with that name. It searches first in the project's assets and then
in the assets included with ursina. I've included some icons for the purpose
if this tutorial, but feel free to add your own.

Let's also remove the random color and instead show the item's name when we hover it with the mouse.
If a button has 'tooltip' set to something, it will show it when we hover the Button.

def append(self, item):


icon = Button(
parent = inventory.item_parent,
model = 'quad',
texture = item,
color = color.white,
origin = (-.5,.5),
position = self.find_free_spot(),
z = -.1,
)
name = item.replace('_', ' ').title()
icon.tooltip = Tooltip(name)
icon.tooltip.background.color = color.color(0,0,0,.8)

8 Drag n' drop


We can't move the items! That's not fun.
However, that's easy to change. Just spawn a Draggable instead of Button.
Draggable inherits from Button, so the tooltips will still work!

def append(self, item):


icon = Draggable(
parent = inventory.item_parent,
model = 'quad',
texture = item,
color = color.white,
origin = (-.5,.5),
position = self.find_free_spot(),
z = -.1,
)
name = item.replace('_', ' ').title()
icon.tooltip = Tooltip(name)
icon.tooltip.background.color = color.color(0,0,0,.8)

9 Snap to grid on_drop


The items can be moved now, but they don't follow the grid.
Let's round the position when we drop it.
Draggable's will automatically call on_drag() and on_drop() if has them.

round the position on drop

def append(self, item):


icon = Draggable(
parent = inventory.item_parent,
model = 'quad',
texture = item,
color = color.white,
origin = (-.5,.5),
position = self.find_free_spot(),
z = -.1,
)
name = item.replace('_', ' ').title()
icon.tooltip = Tooltip(name)
icon.tooltip.background.color = color.color(0,0,0,.8)

def drop():
icon.x = int(icon.x)
icon.y = int(icon.y)

icon.drop = drop

10 Swap items
Add a drag function to remember the start position as org_pos.

def drag():
icon.org_pos = (icon.x, icon.y)

def drop():
icon.x = int(icon.x)
icon.y = int(icon.y)

'''if the spot is taken, swap positions'''


for c in self.children:
if c == icon:
continue

if c.x == icon.x and c.y == icon.y:


print('swap positions')
c.position = icon.org_pos

icon.drag = drag
icon.drop = drop

11 Stay inside the inventory, please


We shouldn't be able to drop the items outside of the inventory.

def drop():
icon.x = int(icon.x)
icon.y = int(icon.y)

'''if outside, return to original position'''


if icon.x < 0 or icon.x >= 1 or icon.y > 0 or icon.y <= -1:
icon.position = (icon.org_pos)
return

'''if the spot is taken, swap positions'''


for c in self.children:
if c == icon:
continue

if c.x == icon.x and c.y == icon.y:


print('swap positions')
c.position = icon.org_pos

icon.drop = drop

12 Bugfix: Make the dragged items render on top


All the items are at the same depth, so it hard to say how they will overlap.
It feels natural that the item we're currently dragging stays on top,
so we'll move it back a bit when we drag it and forward when we drop it.

def drag():
icon.org_pos = (icon.x, icon.y)
icon.z -= .01 # ensure the dragged item overlaps the rest

def drop():
icon.x = int(icon.x)
icon.y = int(icon.y)
icon.z += .01

'''if outside, return to original position'''


if icon.x < 0 or icon.x >= 1 or icon.y > 0 or icon.y <= -1:
icon.position = (icon.org_pos)
return

'''if the spot is taken, swap positions'''


for c in self.children:
if c == icon:
continue

if c.x == icon.x and c.y == icon.y:


print('swap positions')
c.position = icon.org_pos

icon.drag = drag
icon.drop = drop

You might also like