PyWeek 29 challenge: “The butterfly effect”

Butterfly Destroyers! - Butterfly Destroyers Development Overview

Posted by flipcoder on 2020/04/12 23:24

Written by flipcoder (

Team PythonCoders was an open team started by MysteryCoder, a 13y/o programmer.  The team
was international:  Our team members were from USA, France, Finland, and UAE.

I myself have been in several gamejams in the past, including GitHub game-off,
where my game was one of the featured winners.

Pyweek would be the shortest jam I'd done so far.


Going into this jam we started with our project management using Trello.
We used a Kanban-like layout for tasks.  Our columns were:
Ideas, Queue, Doing, Fix, Test, and Done.

Trello Link:

Early Decisions

One of the early strategies I suggested was to just start going and not worry too much about
waiting on our decision-making, since we were slower at this at the start,
it was very important to me to just get a flow going.

From my experience with jams, especially with more than a couple members,
being bottlenecked by "analysis paralysis" is a common problem.  It's better
to do something and have to scrap or fix it than to discuss every detail before
you add it.  In this strategy, the most active people on the team can make the initial decisions
as they arise, This sometimes causes code redundancy, since we may
find different ways to do the same thing, but its often worth the cost of this,
since allowing coders to use the methods they are comfortable with increases
their development speed.

First Steps

Our team was light on the graphics end, so we initially thought of doing
something in curses, so I threw together a curses-like terminal in pygame (see game/base/
This would be come in handy later, considering our game's scripting would
make heavy use of character-by-character typing during the game.  

We had some ideas for games. I pitched the idea of doing something like Top Gun for NES, where it would
seem 3D, but we would try not to overdue it, knowing pygame's limitations were
going to become a problem. I imagined most teams would approach the butterfly theme as something gentle or light-hearted.
We went completely the other direction. I thought doing something comedic where you play
as the military hunting down butterflies for no apparent reason, while playing
it off as serious was quite funny.  We decided this would make a cool arcade-like game and went with it.


By the time we decided to move forward on the idea, we already had a basic game base.

I put together an Entity system as ddorn created a nice 2.5D/parallax Camera.
MysteryCoder implemented fog/distance-fading.

One of the core parts of the game's codebase is the signal/slot system, which was extended by both the timer system
(game/base/ and the scene entity list itself.  We implemented Scene
as a Signal containing entities as slots.  This is the first time I've tried this,
but it had some benefits.  The signal system was basically a safely-modifiable list,
where all changes to it during the list's iteration are queued until after
all iterations of it have stopped.  This iteration+modification is also reentrant.
This meant all objects could modify the scene without iterator invalidation,
and we don't need to copy the entire scene list every time we iterate.

The modes of the game were implemented as a basic state machine.
The intro, game, intermission, and credits are separate modes that
contain their own update(dt), and render methods.

And finally, one of my favorite parts of the project:  The scripting system.

We didn't want levels to be simply X,Y,Z object placements,  we wanted
them to be scripts that were stepped through, and could pause/resume/restart
as needed.  Multiple scripts could run at once and start/stop each other.

We used python's generators to script
both levels and entity behavior, allowing a single line of code  to turn
on and off behaviors of things in the game at any time.

We allowed scripts to be run not just for levels, but for entities and game states as well.

A 3rd use of signals were here: Scripts could yield a condition for when to resume,
very similar to async promises.  These were either timer events or fades.

I wrote a timer system called 'When', based on the signal system, that would
register timed events and fades and return a connection/slot that we could
yield from our scripts.

Scripts that needed user input would accumulate events that happened since last yield.

So we could script things like this:

yield script.sleep(5) # resume the coroutine after 5 game-seconds


yield lambda: not boss.alive # continue the script until the boss dies


# call faderfunc() with color values from black to white and resume script after
yield script.when.fade(2, (ncolor('black'), ncolor('white'), faderfunc, script.resume))

Look and Feel

Ddorn added ground tilting and parallax clouds.  I added a noise-gradient to
give the ground and sky some depth.

We wanted each level to look different, so we varied sky and ground colors, and later
added weather effects and rocks on the ground.

We made the ship sprite stretch based on movement direction to give it a nice 3d effect
and Ddorn used his input system to smooth the movement of the ship.  The player
handling felt really good.

Level Design

Ddorn developed an AI system, allowing spawning of batches
of enemies in different moving configurations and shapes.  In my opinion,
this is the most visually impressive parts of the project, and his
math work on this gave us lots of variety in the levels.

Ddorn's text narration during the levels was also great, and had exactly the
comedic tone I'd hoped for.

We did 3 different types of enemies and a boss.  AI varied within the
initial butterfly type as well.  We didn't get to fully use the Flyers and
ButtaBombers, and the ButtaBombers explosive behavior wasn't finished.

We didn't get everything we wanted to finished, but we were quite happy
with what we got considering the deadline.


With the scripting, we were able to start polishing the look of the game.
We took advantage of gradients, noise functions, weather effects, particles, etc.

I wanted the game to look solid from the start to give a good first impression.


All of the music was written quickly, probably within a half hour.
I wanted to spend my time on code, but the game needed a memorable soundtrack
to give it the first impression we needed.  I wrote 3 songs,
and I was quite happy with it considering the fast-approaching deadline.

Improving Performance

We had some concerns about frame rate near the end.  We had a memory bug which was a bit of a scare, since at one point the game was filling up
most of my RAM and causing bad fps.  We logged like crazy and couldn't find much.  This issue only
sometimes happened on certain computers, which made it harder to find.
I'm pretty sure that a different bug I fixed had removed the worst of it right before
causing it to not appear for us during testing, which was super confusing.  We added some last-minute optimizations and hoped that it ran well for others.

We added an fps stabilizer that would disable some of the
effects if the frame rate dipped too low.  We fixed the slow intro and score screen
by time-limiting the amount of effects updates, and improved the noise generation using caching.


I think everyone felt it was a fun project (except for the scary last-minute debugging),
and I'm happy we were selected as a winner.  I think everyone learned some new things during this.
I know I certainly did. We had some great competition,
but we gained an advantage due to the separation of the Team and Individual categories.

Thanks to my team for all the hard work, PyWeek for hosting this, and everyone who played our game.  We've discussed
expanding the game moving forward.  We've talked about moving the renderer to OpenGL and adding more levels.  A few of the systems we created during this can definitely be reused in the future as well.

Project GitHub:

If you want to contact me, my discord is: flipcoder#8604.  All of us are in the pyweek discord.

Hope to see you all next time!

Add a comment

Lightwing - Lightwing: Postmortem and Playthrough

Posted by Tee on 2020/04/12 17:46

Hi everyone,

Thanks for the feedback on my game!

First off, I can clearly see from the comments that this game is difficult. :) Here is a playthrough of the game:

I'm wondering if anyone actually beat all levels. Let me know if you did. I have trouble balancing games, so it's not surprising to hear that it's hard, but I never really know how hard it is until I read these comments. I tried to make the first few levels simpler, but the difficulty does ramp up pretty quickly in the middle.

Lately I've been alternating between puzzle and non-puzzle games, and this was puzzle time. I find it really fun to design a compact set of mechanics that generates interesting puzzle dynamics. A couple of comments about this one:

1. Level design

Designing levels for this puzzle took me a lot of time, more than my previous puzzle games. I spent so much time just creating each level that I didn't have time to polish the game further. Unfortunately a bug with the undo slipped through (sometimes it fails to undo enemy position).

This time I tried to write a solver to help iterate through the level creation more quickly, but unfortunately the solver was too slow to be of much help for anything a bit more complicated. It was a simple graph search through states, and being able to move the enemies around blows the size of the state space up very quickly. Maybe I could have spent some time improving it but I don't think it'd be worth it. It wasn't completely useless as it helped me understand some of the simpler dynamics of the game and analyze some smaller levels. So not only I spent time writing a solver I barely used, but also I spent many hours designing levels by hand, especially levels 7 through 10.

2. Puzzle mechanics design (minor spoilers ahead!)

This puzzle is surprisingly restrictive in the types of solutions you can have. In particular, you can't have a square loop! The entire goal of the puzzle is to form a cycle in some sense, and you can't even have a simple loop. I was considering adding a mechanic to break this property because it reduces the diversity of levels, but I prefer to minimize the number of mechanics so ultimately I decided against it. The reason why you can't have a loop is because, in a square, you're always going to attract the attention of two of the enemies, and you can't use the "back turned" mechanic because you want them in a 90 degree angle.

As a corollary, if you have three or more enemies, you'll always have at least three enemies in the same line, at least one of which will return fire (haven't quite written a proof but I believe this is true). My hope was that attentive players would notice this, but it's not easy to. I added a distilled version of this property in level 5, and level 7 returns to this exact same property but includes the attraction mechanics. If you did notice this and use this to your advantage, let me know, I'm curious. :)

I'm not too confident that one could take this puzzle and easily expand it further without substantially changing it or adding to it. The restricted level structure is not great for level diversity, and scaling up the puzzle is difficult here. This puzzle requires guesswork from the player on what the final solution could be, and player progress could be nullified if their guess is completely wrong, which could easily be frustrating if the level is too large. I think this can also happen in the current levels, but hopefully their small size limits this issue.

Anyway, I had a lot of fun designing this one. I spent a large fraction of the time on the mechanics and level design. I wish I had reserved more time for polishing and bug fixing, but it's very hard to find the time.

Fortunately I was able to rate all games this time. This was a great batch, I've enjoyed playing all your games. Congratulations to the winners and thanks mauve for organizing! See you next time!

Add a comment

Miranda the Lepidopterist - Full playthrough video

Posted by Cosmologicon on 2020/04/10 06:06

Here it is, in case you get stuck on a level, or you just want to see the ending without playing the whole thing. The timestamps of the different levels are in the video description if you want to skip to a level you're stuck on.

Add a comment

Nothing Suspicious Here - Mac and Linux File Path Issue fix

Posted by chrisyan2000 on 2020/04/06 04:16

encukou just let me know that the source code doesn't work on Linux or mac because of the file path!

I just uploaded another version that replaced every path with os.path.join on google drive here:

You can also watch the youtube video here:

Sorry for the trouble!

Add a comment

Magnetic Pictures - If you get stuck in 'Tech magnets are adjustable'

Posted by DR0ID on 2020/04/04 17:53

This is a bit of a miss leading level. Hint: you have to place the Techmagnet at the right place and adjust its strength to fix it. Hint: Just try some higher values.

Add a comment

Caterpillar Effect - Windows fix

Posted by encukou on 2020/04/03 23:19

I don't have a Windows machine available, so I didn't test there. Turns out that it doesn't work there.
As the rules say – if it crashes, it's on me, so you can mark it "Failed to run". But if you'd still like to play the game on Windows, a fixed version is available on GitHub (link to archive).

Add a comment

Miranda the Lepidopterist - The Three-Act Structure and game jam games

Posted by Cosmologicon on 2020/04/02 19:19

Here's some thoughts I had while writing the story for my entry. I'm no expert on this topic so take it for what it's worth. And please correct me or elaborate if you know better!

The three-act structure is a classic storytelling technique most common in motion pictures. I think it also works great for games with a narrative that are, roughly speaking, less than 4 hours long. (Although some Zelda games manage it while being much longer.)

I certainly don't think that PyWeek games need a narrative, but if you want to include one, this structure matches up well with how the player typically encounters mechanics. Essentially, Act 1 is the tutorial or the introduction of the major mechanics, Act 2 is the main gameplay where you introduce variations on the mechanics or more involved challenges, and Act 3 is the final dungeon, boss, challenge, etc. Act 2 can be open and have separate branches, but typically you bring everything together for Act 3. richard's PyWeek 12 entry came with an excellent plot diagram that shows this off visually.

In terms of the narrative, there's a plot shift, also known as a plot point or a reversal, between Act 1 and Act 2, and another between Act 2 and Act 3. These can be twists, where some secret is revealed (e.g. the Act 1 reversal in The Matrix is a twist), but they don't have to be. Within each of the three acts, the protagonists have a different immediate goal. The Act 1 goal can be low stakes, vague, abstract, or mysterious, but Act 2's goal should be clearer, and Act 3's goal should be very clear and concrete (e.g. escape or defeat the boss). Often the act change coincides with a change in location (e.g. every Star Wars movie).

You'll often see it said that Act 1 needs an early inciting incident or call to adventure (e.g. The Wizard of Oz, Jurassic Park), and that's a good idea but IMHO it's okay to just use Act 1 to introduce the status quo of the world and characters, and let the plot events really start with the shift at the end of Act 1 (e.g. The Lion King, Shaun of the Dead, Back to the Future).

Some more general guidelines. Act 2 is the longest, about twice as long as each of the other acts. In my experience this means moving as much exposition as possible out of Act 1 into Act 2. Introduce characters early, ideally in Act 1, even if they're not important to the plot until later. Acts 1 and 2 can take place over days or years, but Act 3 generally happens in a single day, or even in real time.

Again, that's only one way to do things, but I've found it useful, especially this time.

Add a comment

Flutter - Game Submitted

Posted by mit-mit on 2020/04/01 03:46

Oops, just realised I didn't make a post for this :). Please reply if you notice any bugs or have any feedback!

Also, I've recorded a playthrough the game if interested and want to see the whole experience in one go :)

Add a comment

pollen. - retrospective.

Posted by rdb on 2020/03/31 10:35

Well, this is it.  Not what we intended it to be, but that's how these things go.  We couldn't really come up with a good idea about the butterfly effect, so we instead chose to make a game about butterflies, taking inspiration from the PS3 game Flower.

As-is, the game has no failure state, so arguably isn't really a "game".  The original plan was to have additional mechanics, such as being chased by a swarm of wasps, needing to avoid birds and spider webs, and having different flower colours give different effects.  These were ultimately not implemented; I did not have much energy this week and felt overwhelmed with the challenge of implementing all those elements well.  In the end, we decided to simply make this about introducing spring and "waking up" nature by having the vegetation come to life through the gameplay.  Unfortunately, that means a lot of sound effects, models and animations ended up going unused.  Some others, like the chase music, were reused as "fast music".

Nonetheless, I'm happy about the things we did produce.  In particular, the grass, which I think is visually effective and probably the highlight of this game's visual style.  I may end up making a library to render this type of grass, so that others can use it in their games in the future.

Performance is a bit of an issue.  I suspect that most computers will be able to run the shaders, but the game does require a decent CPU.  This is due to lack of time for optimization.  I hope we won't end up getting too many DNW's because of this.

I'm grateful to my teammate, who created all the models, music and soundtrack while also working on his own entry.  I think the soundtrack is amazing and I love the UI sound effects in particular.  I'm also grateful to the authors of the wecs and panda3d-simplepbr libraries.  And of course, to all other PyWeek participants for another great challenge, and lordmauve for organising it.

I'm looking forward to the next PyWeek!  I'm excited about trying out all the other entries, I see some really cool ones among them.

Add a comment

puggu - Issue with sound on ubuntu ffmpeg

Posted by pillitoka on 2020/03/29 23:33

Some of you reported an error with sound files on ubuntu related to ffmpeg.

If the error is only with game-over sound, please try commenting out the following lines.

game/ line 148 

gameModules/ line 57. 

If the trouble is with all sound files, comment out the following. 

game/ lines 71 to 75 and line 148 

gameModules/ line 20 

gameModules/ lines 29, 35 

gameModules/ lines 93, 98, 107, 110 

gameModules/ line28 

gameModules/ lines 52 to 59 

We apologise for the inconvenience.

Add a comment