A Curve
Hello, loves!
A surprise requirement challenges me. They want the minimap changed substantially! Tout le monde déteste l’IA.
Having looked at the mini-map, performing as requested, my Product Definition Personality has decided to change their mind.
How about, instead of this big wide map, we just let the player use mouse scrolling to zoom in and out on the main map?
Well, how about that? Sure, why not, we’ll just have to figure out some stuff. Let’s try some experiments and we’ll see what wee can do.
Prefatory Remarks
Yesterday, while not writing an article, I improved the KeyedSpriteList to separate the floor tiles from the content tiles, so that the mini-map no longer displays content. The changes aren’t really germane to anything, although what we do with this new idea may or may not rely on them. It depends on whether we decide to show content in the zoomed out view. We might argue that you’re making a map as you go and that you mark all the things in all the rooms.
Round Tuit
I think the first thing I’ll try is a keyboard stroke that changes the zoom, just to see what happens, if anything. My first attempt fails:
class KeyPress:
elif symbol == arcade.key.Z:
params.zoom_factor = 1 if params.zoom_factor == 4 else 1
No effect. I think we’ll need to work in here:
class Cameras:
def __init__(self, view, max_x, max_y, zoom):
self.max_x = max_x
self.max_y = max_y
self.scroller = Scroller(lines=4, base=(512,800))
self.dungeon_cam = arcade.Camera2D()
self.dungeon_cam.zoom = zoom
width_margin = self.compute_margin(max_x, zoom)
height_margin = self.compute_margin(max_y, zoom)
print(f'{view.width=}, {view.height=} {width_margin=} {height_margin=}')
self.dungeon_camera_bounds = (
self.margin_rectangle(view.width/2, view.height, width_margin, height_margin)
)
Let’s extract that last bit and call it from our zoom code, see what that does.
After a couple of whacks, this code produces an interesting result:
class KeyPress:
elif symbol == arcade.key.Z:
camera = self.view.cameras.dungeon_cam
zoom = 1 if camera.zoom == 4 else 4
self.view.cameras.init_zoom(zoom)
class Cameras:
def __init__(self, view, max_x, max_y, zoom):
self.view = view
self.max_x = max_x
self.max_y = max_y
self.scroller = Scroller(lines=4, base=(512,800))
self.dungeon_cam = arcade.Camera2D()
self.init_zoom(zoom)
def init_zoom(self, zoom):
self.dungeon_cam.zoom = zoom
width_margin = self.compute_margin(self.max_x, zoom)
height_margin = self.compute_margin(self.max_y, zoom)
print(f'{self.view.width=}, {self.view.height=} {width_margin=} {height_margin=}')
self.dungeon_camera_bounds = (
self.margin_rectangle(self.view.width / 2, self.view.height, width_margin, height_margin)
)
Before Z:

After Z:

THe zooming worked but the map is not properly centered. I had hoped that moving Dot would cause the map to center properly, but it did not. I think we’ll find that code near that constrain_xy that I freely grant I do not understand.
def scroll_dungeon_cam(self, cell):
cx, cy = cell.xy
shift = -14*8 # TODO
self.dungeon_cam.position = (shift + cx * cell_size, cy * cell_size)
self.dungeon_cam.position = (
arcade.camera.grips.constrain_xy(
self.dungeon_cam.view_data, self.dungeon_camera_bounds
)
)
I strongly suspect that magic shift calculation. It needs to be inversely proportional to zoom, I’d wager. (Not much, but I would wager.)
I find two places referring to that same magic number and I replace them both:
def margin_rectangle(self, view_width, view_height, width_margin, height_margin):
shift = -14*8*4/self.dungeon_cam.zoom # move rectangle left to shift the viewpoint right
rect = arcade.LRBT(shift + width_margin, shift + view_width - width_margin, height_margin, view_height - height_margin)
return rect
def scroll_dungeon_cam(self, cell):
cx, cy = cell.xy
shift = -14*8*4/self.dungeon_cam.zoom # TODO
self.dungeon_cam.position = (shift + cx * cell_size, cy * cell_size)
self.dungeon_cam.position = (
arcade.camera.grips.constrain_xy(
self.dungeon_cam.view_data, self.dungeon_camera_bounds
)
That’s producing the display I want. When I zoom out, the view matches the minimap, as intended:

The code isn’t even particularly bad. We’ll commit this as a good start: Z key now toggles between zoom 4 and 1.
Summary
I had hoped that it would be this straightforward. Even though I did have to tweak that -112, I was aware of it and what it’s doing, and that it needs to be larger when the picture is smaller is somewhat obvious. It still needs to be made more clear: all the scaling needs that. But it’s visible enough and isolated enough to be acceptable, I think, if not actually good enough.
Now we can decide what we really want. I think a continuous zoom using mouse scrolling will be interesting, and a bit challenging, but it might actually be nearly good. I have some concern that we may need to do some rounding to get the margins right, but worst case that will be an opportunity to provide better code and to better understand how and why that works.
For now, we have a perfect time to take a break. See you next time!