If you don’t know the Open Pandora, then here’s a link:
I just received mine yesterday, and so far it seems to work which makes me quite happy. Now, it’s not a plug and play console, and I have a lot to learn in order to make things work.
However, I could easily install Apocalypse Bunny! The Pandora comes with python 2.6. Installing pygame was a one-liner:
sudo opkg install python-pygame
Then I just downloaded a tar.gz of apocalypse bunny and ran it. It runs out of the box ! Of course, the controls aren’t optimized, and it should be toggled to full screen. But it works.
With one drawback: it starts at 10–6 frames per seconds, and after about 20 seconds in the game (when foxes have appeared), it runs at 1 frame per second. This is way, waaaay below the interactivity threshold.
So… I should forget python when developing for the Pandora I think, or at least the way SDL is implemented/bound to python on Pandora. It’s way too slow. Now, what are the alternatives? Java or C#/mono? Sure they’re going to be faster than python but they still run the code in a virtual machine so I’ll run into the same performance problems later. I really, really, really don’t want to bother with C++. What do you guys think?
Working on Apocalypse Bunny was quite fun. The ambition was low, which makes the goal reachable and the progress quick. Also I got to touch many pieces of the software so there was no time to be bored. I even had to do some pixel art \o/.
Sure there’s no sound, and yes the world map doesn’t make sense, and of course the gameplay is limited. Don’t care.
It’s time to go back to Infiniworld, the big project. I learned some valuable lessons thanks to Apocalypse Bunny.
Let’s split our code in packages and modules as early as we can.
I know the zen of Python: “Flat is better than nested”, and I agree. Python is not java, and theses periods don’t come for free. The thing is that we do not need to have a flat code to have a flat interface. To the user, it does not matter that the constant infiniworld.NATURE_GRASS is actually a shortcut for infiniworld.models.tiles.NATURE_GRASS.
The more files we have, the less code they contain. The less code they contain, the less reasons we have to modify them. The less we modify, the less we break. It also helps us keeping our list of import statements short, which helps keeping track of dependencies.
I started by keeping all my modules in the root package but they grew very big. I got entangled in that mess and had to reorganize all the code in smaller pieces. That’s not a problem at all for Python, and not a problem for the user either if we make our __init__.py files properly, but that’s a problem for git or every version control system: I lost all my history. Git accepts that we move or rename a file, but it cannot handle the fact that one file became five or ten. There’s a big discontinuity in the tree now :(.
So if you know in advance that you will have to split your module, don’t write a module but write a package instead.
We need to make room for the special effects.
Until Apocalypse Bunny, all the EntityViews used to corresponding to an EntityModel. Apocalypse Bunny changed that with what I called “special effects”. The expanding circle drawn when we blast a psy-wave does NOT exist in the model, this is a purely visual artifact created only by the view. Same goes for the blood left on the floor when a creature dies, it’s only visual special effects.
A special effect has a position, a rendering method, a sprite, and listens to events like any other EntityView, but it does not have any corresponding entity in the model. It is so similar to an EntityView that I actually inherited the special effect classes from EntityView. I will reconsider the names here because that’s going to be confusing.
Because of the similarities, we want to process special effects like we process the views corresponding to real entities. So we want to put them in the same list/dictionary/set/group than the ‘real’ ones. But because of the difference, we need a special identifier that no real entity is using. I decided that since the entity models were having positive integers as identifiers, I would use negative integers for the special effects. And thanks to this tiny trick, I got all the special effect code working for free with absolutely no effort at all.
Disappearing entities break everything.
When a fox dies, or when a carrot is picked up, their entity disappears. That crashes the entire game. Imagine: the carrots are picked up when we collide with them. It means that the physics engine is in the middle of its iteration doing collision response when we pick up the carrot and it disappears: RuntimeError: dictionary changed size during iteration.
So you think: “okay, let’s not destroy the carrot entity now, instead let’s schedule it from destruction by posting a DestroyEntityRequest which will be processed when the physics has finished running”. Think again, because you end up with the following possibility: you touch the carrot, and your friend touch the carrot, so you both pick it up and then it’s scheduled for destruction twice. It may not crash (although I did ask the event manager to crash when something is unregistered twice) but it’s buggy: only one of you should get the carrot.
So we need: to remove the dead entity immediately, but also to keep it around, alive. At the same time. Schrödinger would have loved this.
Solution: add a boolean variable on our entity saying “I (don’t) exist”. Since the entity stays in memory the dictionaries don’t splode. And since we can tell that the entity doesn’t exist, we can skip it in the physics step. We must really get rid of the entity at some point though, so after setting this variable to False, we post a DestroyEntityRequest. Neat!
Now, the same is true for appearing entities, except that I do not see any reason to create new entities during a physics iteration. Yet. I’ll think about it tonight.
Not all entities are tangible.
Carrots are supposed to represent small things on the floor. They should not be obstacles, they should not be pushable. Sure they won’t block the bunny since the bunny picks them up when she touches them, but they should not be an obstacle for the foxes either. So we want an entity that detects collisions (so that they detect that a bunny touched them) but does not trigger any collision response code from the physics.
Easy: just add a “I’m (not) solid” boolean variable on the entity body and use it well. Neat, again!
I tried for fun to make the bunny non solid: she crosses the walls and everything, awesome! Now we can have ghosts \o/.
We need to pause the physics.
The physics engine should not run all the time: we may be on the title screen, or paused, or maybe there is not even a world yet. It was not easy to add a pause function to my game loop because of the way I wrote it, so a lot of code has changed in there. Surprisingly, the code got cleaner ô_O.
Partition space for entities.
My collision code for entity vs entity was brute force: test every entity against every other. This O(n2) algorithm is of course very poor, but I didn’t know how poor, so I chose not to optimize it early. Well, after having seen how the performance was crashing once I was reaching 50 entities (I’m talking about 5 FPS here), I HAD to optimize. I did not use any quadtree or cool stuff like that yet, I just cut my map in chunks of 8×8 tiles and query entities by chunks. 8 is a quite random number, I could probably reduce it to 4, 2 or maybe 1, but that creates more chunks containing nothing and therefore more overhead when updating or searching the chunk map. I need to test to find a good value. In any case, as soon as we don’t look at the entire map, we’re fine.
At least all I can think of right now.
The engine source files changed quite a lot ; there is no point in merging the two branches. I will branch out of Apocalypse Bunny and remove the code related to this specific game (it’s all in the bunny package!) to have my new codebase for infiniworld. I’ll be doing some refactoring in the next days. Then, new fun stuff!
You’ve waited for one week; now is your patience rewarded. Behold my first game, Apocalypse Bunny.
Misadventures of a bunny.
You play as a bunny in a post apocalyptic world. The only place where carrots still grow are in cemeteries because of their fertile soil. However, zombies grow in cemeteries too, especially zombie foxes. They are a bit slow and a bit dumb, but they are legion. You hop and run between your foes and try to gather all the carrots before too many zombie foxes hit you. Fortunately, the radioactive carrots give you strange abilities. With each carrot you eat, your super bunny mind can create a telekinetic shock wave that will repel and maybe even kill the zombie foxes.
Let’s see how you long you can survive ! Don’t become a zombie bunny.
How to download.
Here is a direct link to the zip archive: Apocalypse Bunny v1.0.1.
The Git users will find a new branch on the infiniworld repository called “apocalypse_bunny”, ready to be pulled.
How to play.
Deflate the zip archive or pull the git branch.
Run the solo.py script that’s in the src directory.
- [W][A][S][D] keys to move.
- [Space] key to blast a psy-wave.
- [M] key to take a screen shot (it’s close enough to the space key so that you don’t have to look for it too much).
- [Esc] key to quit.
If you’re using a French keyboard, then it’s ZQSD to move and [,] to take a screen shot. So it’s the position of the keys that matter, not what’s written on them.
Hey Taurus my friend, I have not merged your code for configuring the keys as you’ll notice that the code changed a LOT. On the good side, the new code makes configuring the keys easier. Little work is needed to adapt your code to the new codebase. I’ll take care of it :).
Survive as long as you can. Like with real life you cannot win, you can only delay the inevitable horrible end of your cute fluffy bunny. Who wouldn’t want to help her?
To survive, avoid the foxes (they bite you) and kill them with the psychic powers that the uranium-enriched carrots give you. Foxes appear every 3 seconds somewhere on the map (there’s a zombie counter on the screen). Carrots appear every 10 seconds.
Send me your screen shots!
We’ll see who survives the longest :D. Remember: pics or it didn’t happen.
If the game crashes, refuses to start, is horribly slow or jerky, maxes at 20 FPS or whatever, please tell me. I did spend time optimizing performances, and I tried to make sure the code was cross platform (Windows and Linux have totally different ways of measuring time even with python as a common language) but I don’t have that many computers to try the game.
No need to tell me the sound doesn’t work: I have not put sounds yet.
And if it works, tell me that too ! Along with a words telling what kind of operating system and CPU you use.
In the next post…
In the next post I’ll tell you what I learned when writing Apocalypse Bunny. Trying to make a complete working game out of the minimalistic game engine of Infiniworld took quite some effort, and I have a much better insight into what features the engine needs to support. Not much of the engine changed, which is relieving because it means I didn’t get too many things wrong. But many things should be added in. There shall be refactoring soon!
Until next time, folks, until next time.
Python 2.6 didn’t like the version 1.0.0 of the game: it complained about a syntax error. I corrected it and made it the version 1.0.1. The download link above is already updated. Thanks for the bug report, MilanFIN!
On the First day, Niriel created the Entities. And the Entities were floating in the empty void. And Niriel got bored and said “Let there be tiles all over the place”. And there were tiles, and the Entities could walk on the tiles, and everybody was pretty much happy, except Entity number 3 who’s always complaining.
- Go there : https://github.com/Niriel/Infiniworld,
- click on the big gray “Downloads” button (right of the page),
- choose “v0.0.3”.
Many many keys !
- Enter/Return: create a new entity.
- ; and ' (semicolon and quote): take control the previous/next entity
- \ (backslash): create a new area. But it’s useless in this version since the areas created that way don’t have tiles.
- [ and ] (left and right square brackets): move to the previous or the next area. Note that the game starts in the area None, which means that no area is displayed. You must press [ or ] to start seeing something.
- . and / (period and slash): teleport the selected entity to the previous or the next area.
- W, A, S and D: move the selected entity. Note that diagonal movements are allowed, and in that case you are not perfectly lined up with the tiles: you moved by square root of two in x and y.
- Esc: quit. You can also click on the “close” button of the game window.
The controls are not awesome yet. They’re hard-coded for a US keyboard, and not even US international: the quote key doesn’t work for me because it waits for another key after that in order to put an accent on it. But hey, that’s a start.
The little new details.
- The window has a title now. I think there may be a bug in my version of Ubuntu preventing me to set the title after creating the window (it only changes the name in the task bar). So I set the title before creating the window and hop, it works.
- FPS counter. Since the game loop authorizes a max of 60 FPS, that’s what I get. Actually the picture shows 59 FPS, it jitters between 60 and 59. Maybe because of the inaccuracy of time.sleep?
The big things.
There are two major modifications to the code:
- The world can have several areas.
- Areas have tiles.
When you run this demo, a world is generated. It contains three areas. Each area contains 16384 tiles (128 by 128). I chose this relatively high number to challenge myself and make sure I could render the area fast enough. If I couldn’t, I would give up on pygame and python. Fortunately I am still in my 60 FPS budget which makes me very happy. My first two implementations crashed me down to 5 FPS:
- The first implementation was the straight-forward brute force: “Ask pygame to display everything, pygame is smart enough to realize it has nothing to do 95 % of the time, right ?”. Actually it wasn’t that bad on small maps, as this method was faster than my second implementation.
- The second implementation just added a line to the first one with a little test in it: “For each tile, check if it intersects the view, and if it does then draw it”. The result was horrendous since I was processing 16k tiles. Looks like pygame.Rect.colliderect() is not super fast since I got a better performance without checking for smaller maps.
- I threw that away. Since I know in advance the min and max world coordinates of my View, I just have to look for the tiles that are within these coordinates. And it’s fast, and scales well. I could have done that in the first place but it’s a bit more code to write and maybe the other methods would have been okay.
Python does not have two-dimensional arrays so implementing the TileMap was not straightforward. The first idea that comes to mind is to use a list of lists. I didn’t like it because:
- it is a bit hard to guarantee that all the lists have the same length;
- it’s a mess to resize, you have to extend or shrink and shift every list;
- I don’t like to write my coordinates between brackets like tiles[x][y];
- I cannot have negative indices ! Negative indices will probably be very nice to have when the world is procedurally generated, as I do not want to impose arbitrary barriers on two sides of my map.
And then I realized we’re in the marvelous world of Python and that we do not actually need rigid two-dimensional arrays when we have dictionaries and hashable tuples! So a tile map is stored in a dictionary: keys are (x, y) tuples. It is fast, and has none of the drawbacks listed above. Sold !
The TileModel object is very simple, it just contains a nature for the floor, and a height for said floor. If the height is 0 it means you could walk on it. If it’s 1, it means it’s raised, like a cliff, and you can’t go. I should have named it “elevation” instead of height.It is not a 3D game, and I must resist the temptation of allowing the player to stand on high tiles. That will be for the next game. Also, tiles don’t block the movements of the entities yet, since there is no physics engine; the entities are even free to run out of the map.
Next step: Physics !
Stay tuned to learn how I include string theory into the physics engine!
Welcome back, fellow web traveler. Here is, as I promised you in my previous post, the very first screenshot of Infiniworld.
- Spawn your very own blocky creatures with a single key stroke (Enter/Return) !
- Take control of your creatures and tell them where to go (WASD keys to move) !
- A stunningly fluid 60 FPS refresh rate on any recent machine !
- Save your battery life: the game sleeps when it has nothing to do !
- Spy on yourself by logging everything you do into a file, and learn about what’s going on under the hood !
Game does not feature:
- Physics engine.
- Any kind of landscape.
- Boobies and explosions (Actually they are in game, but only allegorically, just use your imagination).
- Things to do.
Download and play this first interactive version of Infiniworld !
GitHub link: https://github.com/Niriel/Infiniworld/tree/v0.0.2
If you are a git user:
git clone email@example.com:Niriel/Infiniworld.git
If you just want a zip:
- Go there : https://github.com/Niriel/Infiniworld/
- Click on the big “Downloads” button on the right of the screen.
- A window appears. Under “Download Packages”, click on v0.0.2.
- That’s it, you have the zip !
Run the game.
Enter the src directory and type “python solo.py”.
How does it all work ? you ask. Here is how:
- Model classes: WorldModel, EntityModel.
- View classes: PygameView, AreaView, EntityView.
- Controller classes: PygameController, PlayerController, GameLoopController.
The world is extremely simple for now. There is no landscape, no map, no tiles. It contains nothing but some entities. I call “entity” anything that exists in the game world and can move: creatures, fireballs…
Entities are represented by an EntityModel class and have a position stored as a two-dimensional vector (see geometry.Vector). Each EntityModel has a unique entity_id. Every event regarding a given entity will carry that entity_id. Today, the role of the EntityModel is to change its position when it is asked to do so. Since there is no landscape and no physics engine, it always accept any order it receives. That means that any MoveEntityRequest results in a EntityMovedEvent.
For now, the only responsibilities of WorldModel are to create and destroy EntityModel instances, and keep a list of them.
The PygameView is the root of everything that is displayed on the screen. It opens the window and draws everything that needs to be drawn when it receives a RenderFrameEvent.
The AreaView displays a part of the world: landscape, entities. There is no landscape so it just displays entities for now. Each time AreaView receives an EntityCreatedEvent, it instantiates an EntityView object for representing it.
EntityView objects listen to the EntityMovedEvents to remain in sync with the EntityModel objects.
These views uses pygame.sprite for showing themselves on the screen. The sprites of the EntityViews objects are blitted onto the sprite of the AreaView object, which is blitted onto the display by PygameView. On the screenshot, the display is black, the AreaView is gray and the EntityViews are white.
The PygameController translates the pygame events (which are SDL events) into Infiniworld events. For example, it translates the fact that Escape key is pressed into a QuitEvent.
The PlayerController contains an entity_id: the entity being controlled by the player. When the PlayerController receives a PlayerMovedEvent from the PygameController, it responds by posting a MoveEntityRequest with the proper entity_id.
The last controller is the GameLoopController. The current implementation is pretty naive and simple. It posts a ProcessInputsEvent and a RenderFrameEvent 60 times per second. ProcessInputEvent wakes up the PygameController and RenderFrameEvent wakes up the PygameView. Note that the GameLoopController does not use any pygame function, so it does not call pygame.time.clock for running at 60 FPS. Instead, it uses the time module from the standard library. The game loop stops when the GameLoopController receives a QuitEvent.
What next ?
The goal of this demo was to have something interactive as quickly as I could. It works: I can create entities and move them around. But it’s not pretty: the entities are teleported, it is not fluid at all. It would be nice to have a physics engine. However, a physics engine should manage collisions, and I do not have much to collide against for now. I need a landscape. So I think that the next step is to implement tiles, maps, areas, etc..
The WorldModel will hold many AreaModel instances in memory: dungeons floors, cities, overworld, etc.. The EntityModel needs to be extended to accomodate the fact that there are now several areas. The AreaView should show one area only. Entities should be able to move from one area to another. Lots of work to do !
In the previous article I explained how I implemented the event management. Now, our software bricks can talk to each other. Let’s get them to talk ! Well… let’s get them, first. We need bricks. The good ones.
Too often I started similar projects, focusing on an aspect such as the procedural landscape generation. It worked ! But I got stuck. I ended up with an ASCII dump of something representing a world, but I couldn’t do anything with it. There was no game built to explore that world. This time I want to start making the game interactive in the first place: we need to be able to explore before having something to explore.
I chose pygame for managing the input and the output. The input being the player’s fingers on a keyboard/mouse/joypad, and output being the screen and loud speakers reaching the player’s eyes and ears. Because I apply some kind of Model-View-Controller (I always say “some kind” because everyone has his own MVC), I will wrap the input in a Controller, and the output in a View.
We will have a PygameController and a PygameView class. I don’t want to use anything related to pygame in the Model classes, to make sure that when I decide to use a different rendering engine I only have to rewrite the input and output without touching the game logic at all. There will probably many other pygame-related views, probably one view per character to be displayed, for example. These views will be managed by the main PygameView. It would be wise to group everything pygame-related in one single package so that it can be replaced. Other views, like a console view for the server, would be kept in another package.
And, finally, we’ll need a game loop. I’ll go for the simplest game loop ever, but in a near future I shall write a whole entry on the subject.
Let’s get to work ! Very soon, the first screenshot ! I bet you can’t wait :).
The time to code is near. I lay before you the tools we shall use to design and build our magnificent game.In short:
- Python : because it’s fast to code with it.
- pygame : because I need to display 2D graphics and handle mouse/keyboard events.
- eclipse : because I kind of got used to it by now even though you probably prefer pico/emacs/vi/notepad/idle.
- PyDev : because I want eclipse to speak Python, not Java.
- Git: for controlling versions.
- GitHub: for sharing the source with you guys and gals.
I wish people would agree on how to capitalize these words… pygame, Pygame, PyGame ? I have to look it up every time. You’d think programmers would become case-sensitive after a while.
Now, there are one gazillion ways of doing it, all the combinations of programming languages and libraries you can come up with. The choice is quickly made though: I’m going to code in Python. I want to code fast and efficiently even if that means sacrificing some performance. Python is open source, cross-platform (mostly), comes with many delicious libraries, and takes care of all the boring stuff to let me focus with the cool things. Yes, python is slow, and I would get so much better results in C ANSI. Except that I’ll spend four months displaying a pixel on a 64 bits Linux laptop, and then I’ll have to redo the same for a 32 bits Vista machine. No thanks. Plus I hate C ; I like Pascal more. Also we’re doing a 2D game, of the kind that ran smoothly on a Nintendo Game Boy, so I think our over-powered machines won’t have too much troubles running a few more layers of software if that makes things easy for me.
Python and its standard library are not enough though. I want something that is able to display sprites and poll the mouse and keyboard events. SDL is going to be enough for me so I’ll go for pygame. pygame is a relatively thin wrapper around SDL and comes with a nifty pair of classes: Rect and Sprite. If I want windows, buttons and all, I’ll have to code them myself.
I really don’t want to use anything more. I don’t want to track bugs in someone else’s code. I don’t want anybody to steal my main loop (Twisted, tk, I’m talking to you). I don’t want to make my game a nightmare to install. I just want to keep control of what I’m doing.
That being said, I must jump on my bike and say hello to my hamster I haven’t seen in two weeks !