It’s a dungeon exploration game. You are a niece of an extraordinarily eccentric mad scientist. You come in your blimp to visit him in his huge underground lab, when you notice the lab is devastated, all the specimen are running lose, the staff has been turned into mutant zombies and the robots try to exterminate everything in sight. Somebody needs to stop this, by getting to the lowest levels of the lab and aborting whatever weird experiment has gone wrong. On the way down you collect nifty lab equipment and parts for your portable doomsday machine.
The game is not playable yet, although you can walk around a predefined map and hit some monsters. As it’s a hobby on the side, it might get a while to finish.
The game is going to be set in a rather small and irrelevant part of the JunkJungleWorld.
Finally the problem with walls is solved! Took me several months of not thinking about it. I just remembered about it today and did it. Took less work than anticipated because of two dirty tricks:
Because of another pause in development (mostly because of another intrusion of real life, but also because of distraction with DandelionWiki) I posted another release at http://pygame.org, just to wrap it up. Soon after that I got an e-mail with a question about the license of this game, from people wanting to work on it as a school project. Amazing!
Anyways, the code of JunkJungle is now licensed under GPL v2 or later and the artwork is under Creative Commons BY-SA license. Enjoy!
After another period of cleaning up the code I decided to put off making the inventory menu, and work a little on the map instead.
It’s going to be an exploration game, so the map is important. I want to make it consisting of rooms, pretty much how it looks so far, in the good old Rogue style. However, there is no exploration if you can see the whole map at once – you have to discover the map by visiting it. It means that initially the screen should be empty, and the rooms appear only ofter you have visited them at least once. To simplify things, once a room appears, it stays visible – the player character is supposed to have hearing and memory good enough for this.
Of course, just drawing the rooms suddenly as you enter them would be jarring in a fully animated game – I decided that I will make them fade in gradually. To do that, I don’t draw the tiles on the background layer directly, but instead create “tile sprites”, that will gradually fade in and, at the end of their lives, draw themselves on the background layer. This works pretty fine, assuming the rooms are not too big.
There are however several problems caused by the fact, that the floor tiles are larger than the space they represent – they have a bottom part, that is covered by the tiles being closer to the eye. This makes an ugly effect when the tiles are still translucent, and the background can be seen, and also makes some problems when the sprites die and draw themselves on the background – because they will overwrite the tiles that are already there, but closer to the eye. A temporary solution is just drawing a part of the tile if it’s supposed to be covered, but I need to think of something better.
Another problem that I need to solve is that right now it’s impossible to tell if a room just ends, or if it’s just the end of your field of view – I will probably add some kind of an edge to the room tiles.
I might also decide to change the way the tiles are drawn altogether – use walls for “blocked” tiles, instead of the “chasm” I use now. However, the mockups I made so far don’t really look right...
Wow, making your source code available to the public early pays off after all. I just got my first bug report for JunkJungle. Turns out that Python had a change of generator function syntax at transition from 2.4 to 2.5, and my code didn’t work on Python 2.4 – I didn’t even think to check it. A little investigation reveals that generator functions have been changed into simple coroutines in Python 2.5, and allow things like a yield statement (it’s an expression now) without a value to “yield”. Just adding None to all empty yields made it work better.
What did I use generator functions for in such a simple game? Well, I consider two uses, one of them is already implemented, the other I still think about. The implemented use is for animations: the sprites can have custom animations that will move the sprite around and change its image – for example, when a creature is walking around or an item is pushed. Initially, they were written something like this:
def jump(self):
self.anim = self.anim_jump
self.step = 0
def update(self):
self.anim()
def anim_jump(self):
if self.step==0:
self.image = self.frames[self.facing][0]
elif self.step<=4:
self.height += 1
elif self.step>6:
self.heigth -= 1
self.step += 1
Of course, self.step has to be zeroed before switching to that animation. The animation function itself is called every frame, just before displaying the sprite. Using generator functions I can write it in a much simpler way:
def jump(self):
self.anim = self.anim_jump()
def update(self):
if self.anim:
try:
self.anim.next()
except StopIteration:
self.anim = None
def anim_jump(self):
self.image = self.frames[self.facing][0]
yield None
for step in range(4):
self.height += 1
yield None
for step in range(2):
yield None
for step in range(4):
self.height -= 1
yield None
You can see that I don’t have to keep and reset any internal state anymore, and the animation function actually describes what happens, in linear form, without any need for chained conditional statements and step counting. The update function looks more complicated because it now does what other functions around it used to do. Neat, eh?
Now, the second use is similar, but I fear that the simple coroutines that Python offers might not be enough. I want to use them to program monster behavior. Just like in ZZT, if you know what I mean – just program the steps the creature should perform, in the order it should od them, without implementing a complicated state machine. There are however two problems with this approach: first, I must yield in the generato function directly, not in any function it calls. This means I cannot make behavior templates. Second problem is that I cannot save a “running” generator iterator to disk – they cannot be pickled or anything. So, I can only save the game when the state of all the monsters is known – for example when changing levels. This could be ok with a different game. This particular game was however meant to be interrupted at any moment and then resumed easily. So I cannot use that.
I’m still torn between using a “traditional” behavior routines, for example checking the surroundings every step and deciding on a single next step only, and the cool “coroutine” approach. I consider using greenlets, a micro-threading library for CPython – I can implement a generator function that can yield from within called functions with them, and I’ve heard they can be pickled. But it’s an additional C library, that I would have to either require or bundle with my game.
I think I will stay with the simple behavior functions I have for now.
The development is going slowly but steadily. I’m doing a lot of refactoring and tweaking of the old code. Actually, for every new line of code I change or remove 5 old lines. I hope this is good. It’s surely fun.
I just added a robot monster graphics (only graphics, because monster behavior is not implemented yet). And items. So far they just lie around on the floor. I need to add picking up and dropping animations, then proceed to finally writing that ring menu thingy for the player inventory.
Ok, I have made up my mind, the mockups I’ve been making for the DungeonWeb project have stranded far away from the original idea to become a start for a separate game. The game that I’ve been meaning to write for several years now, the JunkJungle. I have created an initial description, a project page on the pygame.org site, and aggregated the relevant blog entries so far. I also forked the repository. Not much of a change – the work continues.
Yet another burst of activity in an old project – this time the DungeonWeb mockup. This time I have a lot of work done, looks like pyGame is much better thought out than I assumed at first – it’s fast and comfortable if you know it well enough and match your style to how it is built.
This is still just a demo, not a playable game, but it has quite a lot things working already. You can download it from Dev:dw (click on the “zip” or “gz” link at the top). You will need Python and pyGame installed. Things I got working so far:
I have digged out the DungeonWeb project and working on it again a little – I’m still distracted pretty much with all the issues of display, animation and user interface – my favorite areas – but I think there will be some progress after all. You can see the new graphics (again
) on the animated screen shot.
I’m still struggling with one design decision – can’t seem to get this right. The map is tile-based, meaning that it is divided into small squares. Every square has certain kind of terrain (floor, wall, door, etc.) and can also hold some objects and creatures. This is an approach popular in many computer role-playing games. The problem appears when you add animation – in particular, the player character “walks” from square to square when you press keys, and that walking takes some time, but the discrete nature of the map means that it cannot really be “in between” the squares. What should the game do if the player presses a key when the animation is still in progress and the player character seems to be between squares? There are several possibilities:
Update: I finally decided on a partial solution: I buffer a single, last keystroke and discard any other input during the animation. In addition, keys that are kept pressed continuously are handled specially. This way the animations are all smooth, the interface feels reasonably responsive, yet the user doesn’t suffer effects of key presses from 2 minutes ago. Increasing the movement speed (while keeping the actual animation frames the same) further improved the experience. I think I’m done with this part and can move on to the actual game.
You should keep it in buffer, like this:
Key is pressed:
Player variable “wantstogo” is set to direction
... Oh look, I’m free to move the player, and he wants to go somewhere, I’ll move him.
In the middle of movement, this “wantstogo” can be set, but will only take effect when he’s stopped.
Other times, it’ll be None.
I don’t get what you mean by “perfect timing” is needed, but I’m pretty sure this is what they did in games like Pokemon, where they moved in-between tiles (and this was achieved on GameBoy.)
Best of luck,
Jordan
– tgfcoder from Pygame.org 2008-01-15 11:28 UTC
Yes, that’s pretty much how it ended up, but it wasn’t immediately obvious.
– RadomirDopieralski 2008-01-15 16:31 UTC
I’ve decided to start writing about Junk Jungle – so that at least part of my ideas is not forgotten. Junk Jungle was initially the name of a computer game I envisioned when still in elementary school. A computer role playing/adventure game, of course. The idea changed and evolved, and I still don’t have a single line of code for it written – but instead I have ideas about a world, a town and the characters inhabiting it. I will try to describe them here, in parts.
I got from my parents’ home with a bag full of computer junk. I am going to try and make something useful out of it. Especially interesting is the baby-at
386 board, together with a small, 100W power supply unit and an old laptop hard disk drive I had in my box. Together they make a small, quiet and not very power-hungry computer. I only have to make an ATX to AT converter. What a shame that the 9" monitor is not extremely cheap, well maybe I’ll find an used one somewhere. It would fit perfectly.
Update: The converter is not going to work – I’d need some way of keeping the power on – and I need some circuitry for that. I just ended trading power supply units with a friend. I also have the 9” monitor.
Oh, I can’t wait to play!
– LionKimbro 2008-01-10 20:37 UTC
Add a comment