In My Defense ...
Hello, loves!
In my defense … I don’t think I’ve ever actually claimed to be good at this. Let’s try, I don’t know … simplicity?
Our current task has to do with putting a column of images over on the right side of the screen, which is supposed to contain images of the items in Dot’s inventory. I struggled in yesterday’s article, trying to get the column in the right place, of the right size, and to fit the dungeon map over on the left like always. And I worked further yesterday afternoon, with no better results: I kept winding up with gaps between the window and the dungeon, or overlaps of half a cell. Wouldn’t look right, though I did get to the point where I was nearly sure that I could get what I wanted.
Along the way, I’ve said a few times here that I need to better understand what Arcade’s Camera object can do, because there were hints and allegations that suggested to me that it had more capability than I have been using. And in fact I have been reading about it, and mostly finding out that there isn’t much useful writing about how to use it. But there’s some and there are examples: just not quite on point for our needs here.
In my defense … I come from an era where we had to code up all the mapping from internal structures to the screen, scaling and scrolling and affine transforms and matrices and all that jazz1. So I do know—or at least I used to know—how to do all that, and so when an issue arises, I am likely to reach for a quick addition to a coordinate or grab for multiplication by a factor to make things larger or smaller.
I’m just trying to be funny: I’ve been writing in this “warts and all” style for decades now, and I’m really not worried what the reader will think of me. I’m here, I did this, this happened, then I did that.
However, I did come up last night with two insights that surprised me, made me laugh at myself, and that I think will make things a lot easier.
Even / Odd Insight
You may recall that as things have been for a while, the center of the screen isn’t aimed at the middle of Dot’s tile, it is aimed at the lower left corner. Here’s an ancient picture showing the X center slicing down between tiles. Y would be underneath Dot’s tile.

As part of trying to get things sorted for the new inventory panel, I was going through the display-oriented code, and simplifying it, kind of trying to get back to basics. Along the way, I removed the adjustment that puts center at the lower left of Dot’s cell, and instead, put screen center at the center of her cell. Having done that, I couldn’t seem to get the map to come out properly positioned in its panel.
After a while I drew a little picture of some squares and finally realized that if the dungeon contains an even number of cells in x and y, and we try to pin the center to the center of the window, it can never come out with room’s filling the space from edge to edge. That’s not to say that you can’t ever get the pane filled exactly: but if you do, Dot will no longer be at the center: she’ll have to be offset half a tile2.
So what this suggests to me is that since we own the size of the dungeon, we could set it to contain an odd number of tiles in X and Y, and things would almost certainly go more simply.
Scaling Insight
The dungeon is currently 56 by 56 cells. There is a parameter cell_size, which is 16, and given a cell’s x and y, we convert that to x*cell_size, y*cell_size at a low level before scaling. Why do we do that? Because otherwise the whole dungeon would display in a 56x56 array of pixels and it would be really hard to see. Of course at scale 1, it is still hard to see, but we can get the whole map into the window at that size. And you’re intended to actually play at scale 4. Which makes the tiles nice and large.
The Arcade Camera writeup thinks in terms of a viewport and a world space, connected by the viewport’s position, a world space coordinate, and the Camera’s zoom factor.
So, and here is another point where I would smack myself in the forehead if that were the sort of thing I’d do, because it seems obvious on the face of it that instead o doing all that explicit multiplication by cell_size and then zooming by 1-4, we could not multiply by anything and zoom by 16-64.
Bottom Line
If these insights are accurate, and I’m sure they are, we can vastly simplify our display code, which should make adding the inventory and other panels much easier.
Se that’s what we’ll start on today. And we’ll discuss, when we reach a stopping point, how we should really feel about all this. We’re at a save point.
Let’s begin by finding all the uses of cell_size and deciding how to stop doing them.
Cell has two uses, and when we look at them we can see they are odd:
class Cell:
@property
def position(self):
return self.x*cell_size, self.y*cell_size
def center_position(self, size):
cx = self.x*size + size // 2
cy = self.y*size + size // 2
return cx, cy
Cell should have made up its mind, either it knows about cell_size or it doesn’t. We see where getting the center to the lower left of Dot comes from. Change both of those to ignore the size:
class Cell:
@property
def position(self):
return self.xy
def center_position(self, size):
return self.xy
I can’t resist running the game to see what this does. I expect a tiny map and possibly the images will be large. Yes, interesting:

Amusing. Continue finding things using cell_size:
class Cameras:
def max_position(self):
mx = self.max_x * cell_size
my = self.max_y * cell_size
return mx, my
Just remove that math.
def max_position(self):
return self.max_x, self.max_y
PyCharm isn’t as helpful as it might be in finding these, for some reason. Anyway
class Dungeon:
# dev-only visualization
def maker_flood(self):
if len(self.flood_list) > 0:
self.flood_list = SpriteList()
return
for cell, distance in (Flooder(layout=self.layout, origin=self.player_cell)
.can_traverse()
.flood()):
cx, cy = cell.center_position(cell_size)
text = arcade.create_text_sprite(text=str(distance),font_size=8)
text.center_x = cx
text.center_y = cy
self.flood_list.append(text)
We can use position there, since it is now the center. Done.
Changed this as shown:
class DungeonView:
def draw_adventurer(self):
player_cell = self.dungeon.player_cell
if not player_cell: return
cx, cy = self.dungeon.player_cell.position
arcade.draw_circle_filled(cx, cy, 1 / 4, arcade.color.RED)
And this, again just use position:
class KeyedSpriteList:
def make_visible_content_sprite(self, item, cell):
sprite = ContentSpriteMaker(item.resources, item.scale).sprite
sprite.position = cell.position
self.add_content(item, cell, sprite)
if self.sprite_at(cell).visible:
sprite.visible = True
And in main:
...
screen_multiplier = 16
screen_width = screen_multiplier*dungeon.max_x
screen_height = screen_multiplier*dungeon.max_y
...
That used to use cell_size. Let’s run again and see what we get. I expect much the same but with small images. I am mistaken. It’s much the same, including the big images, but centered properly on Dot.
The issue is the tile scaling and other image stuff, which, I fear, is probably all over.
Nothing for it but to look. But first, instead of dealing with that, let’s bump up the scale by 16.
LOL!

OK, I fear we need to deal with the images. But let’s try just not displaying the contents and see what happens. Ah. We can’t: the content and tiles are all in one collection.
Details to follow, but I’m in the middle of things, so just a quick note: I’ve found a couple of places to whack that scale down the images. Need to adjust the zooming code before we can see what’s going on.
class Cameras:
def adjust_zoom(self, scroll_movement):
min_zoom = 16
max_zoom = 64
scale = 1
self.init_zoom(max(min_zoom, min(self.dungeon_cam.zoom + scroll_movement / scale, max_zoom)))
Those figures used to be 1, 4 and 10. Now we have a dungeon that looks nearly good with one major exception, the content is so small we can’t see it:

The content is there, taking up approximately one pixel.
Apparently tiles and content scale differently somehow.
OK, I found the place to whack. There’s some cleanup needed but here’s the game running, at full zoom in and full zoom out:


I think I’ll commit this: refactoring out use of cell_size parameter, simpler display code, margins not working.
That was a lot of changes, though all very tiny. Let’s reflect, and probably take a break.
Reflection
Almost all the changes made were to remove code, like multiplication by cell_size, or to change constants, such as 4 to 64 and 1 to 16, to reflect the new zoom size variation. We’ll review the changes next time, but I think the only real bumps in the road were around scaling of the images, which I’ve complained about before. The issues include:
- Images in our resources are not all to the same scale, but our image data structures do not include that information. Instead, various chunks of ad-hoc code adjust things.
- Images for floor tiles and content come through slightly different paths of finding the image, and then funnel through a single method to scale them. There is an adjustment factor in that method, which is odd, and which I had to change to make things work. So that needs work.
All that said, the process consisted mostly of removal, and the resulting code is unquestionably simpler than it was. I think it will serve as a better basis for our inventory panel.
Next steps will include a scan of what we’ve done, probably cleaning up a bit, probably not dealing with scaling at the level it should be dealt with, and putting margin handling back in. Somewhere in there, we’ll allocate the section of the screen for the inventory, and after that, start filling it in.
I have a p-baked3 idea for that, where 0 <= p < 1, which came to me about the same time as the other insights above. Content items are sprites, with a position, the position of the cell they are “in”. Suppose we were to allocate screen addresses outside the dungeon dimensions, and suppose that when an item goes to inventory, we changed its position to be over there with the other inventory stuff. Then, maybe, with a bit of hand-waving, they would just automatically draw over in the inventory pane.
All that is for future times. Today, we have shown that simpler is better. Who knew?
See you next time!
-
Yes, we also had to walk ten miles to and from school, man and boy, five feet of snow, uphill both ways. That goes without saying. ↩
-
You may recognize this as an instance of the fence-post problem, where you need one more fence post than you have fence panels, and your code almost invariably gets it wrong. ↩
-
If an idea can be half-baked, surely it can be p-baked for any p between zero and 1. ↩