Inventory Scale
Hello, loves!
Let’s see if we can sort out the scaling issue with the inventory display. Tout le monde déteste l’IA.
The scaling issue is this: to get the inventory pictures to display in the inventory panel, we have to scale them up:
class DungeonView:
...
with self.cameras.inventory_cam.activate():
cam = self.cameras.inventory_cam
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x,y)
rectangle = arcade.rect.XYWH(x,y,w, h)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = h - 64
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
sprite.scale = params.scale_texture(sprite.textures[-1], item.scale * 64)
sprite.position = x,sprite_y
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 64
Setting the scale works, in that the display looks right. But we shouldn’t have to do it, since the items display just fine when they’re in the dungeon. It came to me, when I was describing this problem yesterday, that because the map screen’s zoom factor is generally 64, the native size of the items is very small. So it “should” be the case that if we scale the inventory_cam at 64, we could stop changing the scale of the items displayed.
So we’ll try that. We’ll have to change the locations where we display them as well, of course. I expect to have to play with the numbers to get it right. My belief, from reading the documentation, is that cam.width, height, and position are all in world coordinates. I’m not sure how that affects things, but we’ll find out.
I’ll begin with an experiment, scaling the cam, setting its position to zero, and positioning the images all at zero. Like this:
with self.cameras.inventory_cam.activate():
cam = self.cameras.inventory_cam
cam.zoom = 64
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x,y)
cam.position = (0, 0)
rectangle = arcade.rect.XYWH(x,y,w, h)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = h - 64
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
# sprite.scale = params.scale_texture(sprite.textures[-1], item.scale * 64)
# sprite.position = x,sprite_y
sprite.position = (0,0)
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 64
I just set zoom, bashed in a couple of (0,0), removed the scaling. Let’s see what happens.

So that’s nearly good. Since the rectangle is still being draw with the old (x,y) that probably accounts for the offset of the allegedly dark slate grey bar. And the torch looks to be the right size and centered. Let’s try using the x and y from the little calculation at the top.
Same picture but the panel is now filled properly. Let’s think about positioning them vertically now. We were stepping by 64 before. Does that mean we can step by just one now?
with self.cameras.inventory_cam.activate():
cam = self.cameras.inventory_cam
cam.zoom = 64
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x,y)
rectangle = arcade.rect.XYWH(x,y,w, h)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = h - 1
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
sprite.position = x,sprite_y
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 1

Perfect! Don’t you just love it when a plan comes together?
Let’s commit this and then refactor a bit. Commit: rescale inventory cam, no need to rescale sprites.
This drawing method is way too long:
def on_draw(self):
self.clear()
player_cell = self.dungeon.player_cell
if player_cell:
self.cameras.scroll_dungeon_cam(self.dungeon.player_cell)
with self.cameras.dungeon_cam.activate():
self.keyed_sprites.draw()
self.draw_adventurer()
self.draw_flood()
with self.cameras.inventory_cam.activate():
cam = self.cameras.inventory_cam
cam.zoom = 64
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x,y)
rectangle = arcade.rect.XYWH(x,y,w, h)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = h - 1
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
sprite.position = x,sprite_y
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 1
Let’s extract a few methods here:
def on_draw(self):
self.clear()
self.draw_dungeon_pane()
self.draw_inventory_pane()
def draw_dungeon_pane(self):
player_cell = self.dungeon.player_cell
if player_cell:
self.cameras.scroll_dungeon_cam(self.dungeon.player_cell)
with self.cameras.dungeon_cam.activate():
self.keyed_sprites.draw()
self.draw_adventurer()
self.draw_flood()
def draw_inventory_pane(self):
with self.cameras.inventory_cam.activate():
cam = self.cameras.inventory_cam
cam.zoom = 64
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x, y)
rectangle = arcade.rect.XYWH(x, y, w, h)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = h - 1
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
sprite.position = x, sprite_y
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 1
Now that draw_inventory_pane is isolated, it’s easy to see that the first few lines are unchanging. Let’s move those to Cameras, where this camera is set up.
We’ll do it in small steps. First this:
def draw_inventory_pane(self):
with self.cameras.inventory_cam.activate() as cam:
cam.zoom = 64
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x, y)
x, _ = cam.position
rectangle = arcade.rect.LRBT(0, cam.width, 0, cam.height)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = cam.height - 1
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
sprite.position = x, sprite_y
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 1
Note that the with-as statement can provide the cam. I had assumed long ago that that would work but had not tried it until now. I changed the rectangle from XYWH to LRBT (left right bottom top) for convenience. My plan is to move the statements above the break over into Cameras. I believe that all the statement below the break only read from the cam when they need to, so we can move the code above over to Cameras now:
class Cameras:
def __init__(self, view, max_x, max_y, zoom):
self.view = view
self.max_x = max_x
self.max_y = max_y
width = self.max_x*cell_size
height = self.max_y*cell_size
viewport = arcade.rect.LRBT(0,width,0,height)
self.dungeon_cam = arcade.Camera2D(viewport=viewport)
inventory_port = arcade.rect.LRBT(width, width+8*cell_size, 0, height)
cam = self.inventory_cam = arcade.Camera2D(viewport=inventory_port)
cam.zoom = 64
w = cam.width
h = cam.height
x = w / 2
y = h / 2
cam.position = (x, y)
class DungeonView:
def draw_inventory_pane(self):
with self.cameras.inventory_cam.activate() as cam:
x, _ = cam.position
rectangle = arcade.rect.LRBT(0, cam.width, 0, cam.height)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
sprite_y = cam.height - 1
for item in self.dungeon.player_inventory:
sprite = self.keyed_sprites.sprite_at(item)
sprite.position = x, sprite_y
sprite.visible = True
arcade.draw_sprite(sprite)
sprite_y -= 1
That init is way too messy, but it’s the simplest move I could think of to make. Everything works. Let’s be committing: refactoring inventory camera logic.
Here’s a nice idea:
def draw_inventory_pane(self):
with self.cameras.inventory_cam.activate() as cam:
x, _ = cam.position
rectangle = arcade.rect.LRBT(0, cam.width, 0, cam.height)
arcade.draw_rect_filled(rectangle, color=arcade.color.DARK_SLATE_GRAY)
for dy, item in enumerate(self.dungeon.player_inventory):
sprite = self.keyed_sprites.sprite_at(item)
sprite.position = x, cam.height - dy - 1
sprite.visible = True
arcade.draw_sprite(sprite)
Using enumerate we can avoid the explicit y offset and its incrementing.
I think we’ll let this ride. Do a final commit and sum up.
Summary
In a series of small steps, we’ve moved from an experiment where we slammed down a rectangle and a sprite, to a rather well-factored additional camera handling the inventory display. The code is located in roughly the right objects, with the initialization of the inventory camera in Cameras, and its use in a separate method in DungeonView.
I think we should look at DungeonView and see whether we’d do well to separate out our Cameras using the Section object. I’m not sure about that: Section is a pretty weak object, seemingly providing little value beyond isolation of the code. That’s not zero value, but with small methods, I’m not sure it adds much. We’ll think about it.
We’ll find a lot more to do with the inventory, and we need to have Buzz accept the flower from Dot, not just admire it as he apparently does now, since he gives Dot the honeycomb but doesn’t take the flower.
Sometimes people just don’t want the flower. Sometimes you have to let them walk away. — Amanda Palmer
So. A decent morning’s improvement. See you next time!