I have a game that I'm working on, and I'm attempting to implement a dungeon-crawling minigame. I want the minigame to appear on a screen--though this isn't entirely mandatory--and I want the screen to move smoothly, and I don't want to render too much at once, so that the screen loads laglessly, and gameplay is nice and smooth.
The Pink Engine demonstrates this behavior quite well. Unfortunately, I cannot use it, because my game is already extremely established in my own engine. I was hoping that by looking at its code, I might figure out how to do it, but I'm afraid I can't quite understand it without significantly more experience.
This is the screen I display maps on:
Code: Select all
screen DungeonMap(dungeon):
python:
width = dungeon.GetActiveFloor().GetFloorWidth()
height = dungeon.GetActiveFloor().GetFloorHeight()
ts = dungeon.TileSize
grid width + 22 height + 22:
at transform:
subpixel True
anchor (math.floor((dungeon.LastCameraX + 11) * ts), math.floor((dungeon.LastCameraY + 11) * ts))
linear 0.2 anchor (math.floor((dungeon.CameraX + 11) * ts), math.floor((dungeon.CameraY + 11) * ts))
transpose True
pos (0.5, 0.5)
for x in range(-11, width + 11):
for y in range(-11, height + 11):
python:
dx = (dungeon.CameraX - 0.5) - x
dy = (dungeon.CameraY - 0.5) - y
distance = math.sqrt(dx**2 + dy**2)
if (abs(dx) < 11 and abs(dy) < 7 and distance < 9):
add dungeon.GetTile((x, y))
else:
null height ts width ts
use DungeonButtons(dungeon)
The map itself is pretty simple, with a lot of the tile-fetching work being offset to the Dungeon's GetTile() function, seen here. (And truncated for brevity.)
Code: Select all
def GetTile(self, coords):
ts = self.Dungeon.TileSize
cameraCoords = (self.Dungeon.CameraX, self.Dungeon.CameraY)
finaltile = Null()
blockedwalls = []
if (self.IsWall((coords[0] - 1, coords[1] - 1))):
blockedwalls.append("TL")
if (self.IsWall((coords[0], coords[1] - 1))):
blockedwalls.append("T")
||TRUNCATION||
if (self.IsWall((coords[0] + 1, coords[1] + 1))):
blockedwalls.append("BR")
if (blockedwalls == ["TL", "T", "TR", "L", "R", "BL", "B", "BR"] or self.IsInRoom(coords) and blockedwalls == []):
sheetcoords = RandomChoice([(1, 1), (1, 1), (1, 1), (4, 1), (7, 1)], True, coords)
elif (blockedwalls == ["T", "L", "R", "B"]):
sheetcoords = (1, 7)
elif (blockedwalls == ["TL", "T", "TR", "L", "R", "B"]):
sheetcoords = (1, 12)
||TRUNCATION||
elif ("L" in blockedwalls):
sheetcoords = (2, 7)
elif ("T" in blockedwalls):
sheetcoords = (1, 8)
else:
sheetcoords = (1, 4)
visibility = self.GetVisibility(coords)
if (self.IsWall(coords)):
finaltile = Transform("images/sprites/{}.webp".format(self.GetPalette()), crop=(84 + sheetcoords[0] * 25, 163 + sheetcoords[1] * 25, 24, 24), zoom=4)
elif (self.IsInRoom(coords)):
finaltile = Transform("images/sprites/{}.webp".format(self.GetPalette()), crop=(84 + (sheetcoords[0] + 9) * 25, 163 + sheetcoords[1] * 25, 24, 24), zoom=4)
if (coords in self.SpecialTiles.keys()):#visibility == 0 and
finaltile = Composite((ts, ts),
(0, 0), finaltile,
(0, 0), self.SpecialTiles[coords].GetTile())
if (visibility == 0):
finaltile = Composite((ts, ts),
(0, 0), finaltile,
(0, 0), self.GetMob(coords))
else:
finaltile = Composite((ts, ts),
(0, 0), finaltile,
(0, 0), Transform("images/sprites/fog{}.png".format(visibility), size=(ts, ts)))
if (coords == cameraCoords and self.Dungeon.DungeonMode == "FE"):
finaltile = Composite((ts, ts),
(0, 0), finaltile,
(0, 0), "cursor")
return finaltile
It's worth mentioning that everything looks correct, graphically, as long as the screen is stationary, and I do not attempt to change the position of anything. However, right now, when I adjust the camera's offset, the map still jerks ahead in spasms--the transform applied to the screen's grid is just a band-aid. Further, as I mentioned, this map becomes quite laggy when I implement all the features I want, even if the map is relatively small.
Here is a video demonstrating the behavior. My recording software makes it look worse than it is, but it's still pretty bad.
I would prefer not to toss away all this work, but, at this point, I'm more than willing to, in order to get this done properly.
I do not know what's causing my errors, but there are a couple possible ideas that occur:
1. Each cropped tile is loading the entire spritesheet, filling memory up far more than I think it should. My understanding is that the spritesheet is only loaded once, but perhaps I'm wrong. Would the solution in this case be to separate the spritesheet up into separate sprites? That sounds miserable.
2. Ren'Py's renderer simply cannot handle the 100-odd sprites I want to put onscreen at once. I'll simply have to change how much the player can see at once.
3. When compositing images, as I do multiple times to determine the "final tile" to display, each composited level is its own image, so each "foggy" tile represents two sprites in memory. I'm not sure how I would fix this one.
I have even less of an idea as to how I could seem to make the movement of the sprite characters smooth, such that I can't even offer something I'd been considering. I can somewhat fake it through the transform applied to the parental grid, but as you've seen in the video above, it doesn't particularly work.
I would absolutely appreciate any advice that anyone might be able to offer.