Saturday, March 18, 2017
"My Enemy's Enemy is my Friend" was my solo entry for PyWeek 23 (February 2017), a twice yearly video game development competition that gets competitors to build a complete game from scratch in seven days using the python programming language. This competition's theme was "The Lesser of Two Evils" and I made a 3D tactical space shooter in which you work for an insurgency against an oppressive colonial government after your family is killed in a government attack; your character reflects on your retaliation against government civilians and is forced to question what action should be taken when faced with two evils. This comp I went solo for the first time since my first entry back in 2014 (my usual wingman for Team Chimera, Lucid, was taking a break).
The game was inspired by 1990's space shooters such as Colony Wars and X-Wing vs. Tie Fighter. Instead of using 3D hardware acceleration via graphics libraries such as opengl (or the pyopengl python bindings), I decided to experiment with the idea of doing 3D all on the CPU, in python. I was keen on this idea because installing 3D library dependancies in python can be a bit of a pain, with slightly unpredictable results ... also, I just got stuck on the idea early in the process, and I'm stubborn :). I managed to implement a basic engine for rendering 3D flat shaded graphics by performing camera projection and lighting calculations for triangular faced models using numpy and rendering faces using "pygame.draw.polygon". I added to this a basic horizon texture mapping (using numpy and "pygame.draw.surfarray" and rendering of circular particles using "pygame.draw.circle" and circle radius computed based on camera depth using numpy. The array of resulting 3D effects was pretty simple, but I think it worked pretty well: I had something that looked at about the same level (maybe a touch better?) as Starfox did on the SNES.
I ended up ranking second in the individual entries and third overall, a result I was very happy with. I received a fairly high "production" score (4.8 out of 5.0), so I was pretty miffed that players thought the graphics were decent enough.
Here you can find a full playthrough of the game, broken up into three parts (with a fourth showing an alternative ending to the game):
I'm currently working on getting a binary release/installer for OSX/Windows, but for now you can play them game by installing the source distribution (requires python, pygame and numpy) by downloading the game from the pyweek page: My Enemy's Enemy is my Friend.
Thursday, February 2, 2017
Global Game Jam is an annual event held at approximately 700 sites simultaneously across the world in which participants join teams to create a video game in 48 hours. I participated in the event for the first time this year in January at the North Sydney Institute of TAFE's site and had a blast! This year's theme was "Waves" and joining in an enthusiastic team with three other people I met at the event, we created our game "Jet Ski Jousting".
I was a little apprehensive when I turned up to the event on a Friday afternoon, not knowing anyone, and not being sure if I'd even be able to join a team. Luckily I ran into a pair who had done the event for at least the last three years and needed some audio. Overall there were about 60-70 people there (I guess) and everyone was super friendly and inclusive. There were a range of different games types and platforms, people doing standard PC games, tablet games, VR etc.
I did the sound and music for the game (and a little bit of UI art) which was a great experience, as I've mostly been involved in programming or game design in previous projects. I used Musescore to compose the background theme and a little victory jingle, and used Audacity to chop and post-process a range of open-licenced sounds I found online, mostly through Freesound.org for sound effects. I combined these all together and integrated them into our game (which was built in Unity) using FMod, which I had never used before; it was relatively easy to pick-up with a few pointers from one of our other team members who knew what he was doing.
Jet Ski Jousting runs on Android and PC/OSX (source distribution requires Unity) and can be downloaded here: Jet Ski Jousting (Global Game Jam 2017)
I had a bit of spare time on one of the mornings so I also worked on a little side-project game. I had brought along my Wii Balance Board that I picked up from an Op Shop a few weeks ago, and a Raspberry Pi 3. I previously got the Wii Balance Board to talk to the RPi over Bluetooth using python, so I worked off this to build a game in python/pygame called "waverider". It's a sort of motion-racing game where you have to accelerate a particle along a wave function by leaning left and right to steer the particle up or down the hills in the wave function, racing another particle to get to the end of the course.
Good thing about using the RPi was that I could plug it into a ceiling mounted TV hanging above a busy thoroughfare and leave the balance board on the ground nearby, so passerbys could casually play. People seemed to enjoy the game; a bit of physical movement was probably a nice respite from intense work in front of a computer screen.
The code for the game is available here: Waverider (Global Game Jam 2017)
Tuesday, January 31, 2017
A few weeks ago I walked into Vinnies to drop off some old clothes and knick-knacks we no longer needed. On display I found an old Wii console, complete with wiimote, nunchuk, a couple of games and a Wii Balance Board, all for $20 ... Bargain! I couldn't help myself so I went ahead and bought it, hence bringing home more junk to fill the space left by the clothes we gave away :).
The balance board is pretty cool: it has four weight sensors on the four corners of the board that allow for a measurement of the total weight of the person standing on the board and also data on their center of gravity in two dimensions. I found this cool app on osx for connecting to the balance board and providing a simple bathroom scales. Worked well on my Macbook so I decided to dig around for libraries that could connect to the board and provide the same data. I found two different solutions here and here, both of which were in python (which works for me), but unfortunately seemed to be Linux only. I decided that perhaps I would make this a Raspberry Pi project. I had trouble getting the first solution to work, as it was a patch to an existing svn repository that didn't seem to be there anymore (at least it seems like the project has switched to a different versioning system, and I wasn't so sure how to go ahead with the patch). I found I could get the second method (Gr8W8Upd8M8) working well.
I modified the code to create a daemon thread that would continuously read data from the balance board and provide it to a queue such that the sensor could be monitored continuously by a background process and interface with an application in the foreground.
The python code that provides the interface to the balance board can be found here: wii_balance_board.py
I ended up using the code in a Global Game Jam game I made using python and pygame (waverider). I'll talk more about that in my next post.
Wednesday, December 21, 2016
I made the models using OpenSCAD and by importing in arrays that contained the pixel values. I then created a separate 3D object for each colour set, matching up the colours to the available colours of filament I had. Like in my previous multi-colour print, I made the pixels 2 mm squares, but subtracted a small buffer in size of 0.25 mm for edges that adjoin onto different colours. Because some of the sprites were kind of complicated, and I didn't feel like having to glue any pieces, I designed a black border around the sprites, so everything would be nice and self contained and just snap into place. It took a fair bit of time to put them all together, but it was kinda fun, like doing a jigsaw puzzle.
You can download all the stl files here: Nintendo Christmas Tree Decorations (Thingiverse)
Sunday, December 18, 2016
I've had a mild interest in procedurally generated worlds for video games for a while now. I recently found this article by Amit Patel from Red Blob Games on building procedurally-generated game worlds using randomized voronoi diagrams and a graph-based approach to defining spatial attributes. I had played a bit previously using Perlin noise to define world elevations, and then basing spatial attributes on elevation contours, which is cool but always ends up with pretty random, unrealistic looking landscapes. This graph-based approach seemed to provide a bit more flexibility in how the landscape looked and how to add additional objects into the world (like rivers, roads etc) in a way that seemed to provide a bit more control. I also found this 8x8 pixel micro tileset, which sparked my interest to try and build up a random island overworld generator, based on the tiles here.
I wanted to generate landscapes that could form the overworld of an adventure style game. Basically something where the player has a constrained world to explore that contains a variety of different biomes (provided by the tileset), villages, roads and locations representing the entrance to caves or dungeons that would form the basis of the objective of the game (enter caves, defeat monsters, recover treasure etc).
Working in python, I started by defining random points in a 2D space using stratified random sampling, to make sure points were not too 'clumpy'. Instead of using voronoi diagrams to generate polygons, I went straight to using a Delaunay triangulation to define the local connectivity of points (using this nice pure-python implementation). The triangulations would later be used to rasterise landscape tiles and define paths and distances between locations. Following from Amit's approach, I created a series of points regularly spaced around the border of the map and defined them as water. I then selected a fixed number of the other random points and defined them as land. I then performed flood fills along the triangulated network to define all remaining points as land or water based on whichever predefined node is closest. I then incremented through triangles, and those that contained a majority of land nodes were rasterised into land tiles, where the remaining tiles were labelled as water/ocean.
This gave me a bulk of land surrounded by water, usually as one island but some times split into two. Each node on land is then labelled by it's distance to the closest water/ocean node, which helps define what type of tile is used (light/dark grass, light/dark dirt) and the probability it will be occupied by a trees or mountains. I then added a few lakes by defining random starting locations on land and seeded some water patches using region growing. I added some villages: one that appears on a random mountain tile, one in a forest (distance layer with probability of tree being 1.0) and two on the coast (distance to water less than one edge on the triangulated network). I then finally drew roads between the mountain village and the forest village and biggest coastal village using A* paths along the triangulated network.
The micro tileset has tiles for a temperate landscape and for mid-winter, as well as one devoid of trees (like a desert or devastated terrain). Originally I had wanted to integrate all of them into one map with different biomes, but they didn't really look that good together. So in the end I decided to make the rendering of the map seasonal, so for every map there is one rendering for summer and one for winter. For the winter rendering I also add in a few icebergs in the surrounding waters.
I'm not sure exactly what I'm going to do with this yet, but I'll probably end up expanding upon it to make an adventure game. I was thinking about also making a random dungeon generator that utilises the dungeon portion of the micro tileset.
For now, source code available here: mit-mit-randomprojectlab/RandomOverworldGenerator
Saturday, December 17, 2016
Thinkspace had a 24 hour 3D design and printing competition a few months ago for which the design brief/theme was jewellery. I only had a hour or two to spare so I did up a quick puzzle ring: its designed to be printed in four separate parts, in four separate colours, each a little ring with tabs that click into each other. The tabs are positioned so there is a unique way of arranging the pieces (like a puzzle). It's not that hard to solve or anything. I made it in OpenSCAD. It's pretty much just a series of cylinders with holes, and the tabs are just rectangular pegs and cutouts.
OpenSCAD script and stl model files available here.
Working on this design got me excited about making 3D prints that could be put together from multiple parts printed in different colours, so I played with this idea a bit more. I made a little pixel art model of Link holding up the triforce from the original NES Legend of Zelda. It's a cool sprite because it's a small number of pixels and only requires three colours. I used an OpenSCAD to build the models from an array that contained the sprite data.
Since the sprite is mostly green, I printed the green layer to have a backing that would support the rest of the design. The pieces from the orange and "skin" coloured layers sort of just snap into the remaining gaps. First time I did the print, I designed all parts to have a pixel size of 2mm. When I went to snap them in I found they wouldn't fit, as the printer seemed to have a tiny bit of 'bleed' such that a 2mm pixel that stuck out didn't exactly fit into a 2mm gap. I redesigned the parts such that any pixels that were designed to adjoin another layer were about 0.25mm smaller such that they would smoothly fit together. The resulting prints worked well, but some bits required glueing, as they otherwise would float off the side.
Continuing on the Legend of Zelda theme I made some little heart containers and Link's sword. I ended up turing them into pairs of earrings by supergluing some sterling silver earring posts and backs on the 3D prints.
The 3D parts for the earrings can be found here.
Wednesday, November 16, 2016
This is a continuation of a previous post on re-building the electronics for an old, broken electric piano keyboard. After getting the keyboard working as a MIDI device, I decided to add in a Raspberry Pi to act as a synthesiser to actually generate out different instrument sounds based on the MIDI data from a Teensy which is used to read the keyboard state.
I found some good instructions here for running Fluidsynth, an open-source, command-line based synth on the Pi that uses Soundfont sf2 files. The great thing about soundfont is that I can download and pick and choose form thousands of different instruments for free in the internets, which will make for a more interesting playing experience than the simple square waves that I can generate on the Teensy. After install Fluidsynth, I connected up the MIDI/USB connection from the Teensy into a Raspberry Pi 2 model B, and ran: "fluidsynth -a alsa /usr/share/sounds/sf2/FluidR3_GM.sf2". I then put Fluidsynth into the back ground using cmd-z and ran: "aconnect 20:0 128:0" to route the MIDI input from the Teensy (client 20, port 0) to Fluidsynth (client 128, port 0 on my machine). I could now hear a standard piano instrument when playing the piano, but the sound is fairly delayed through the HDMI audio (a latency of maybe 250 ms by my guess). Tried changing audio output to headphones using "amixer cset numid=3 1", and the delay is slightly less noticeable, but the sound quality is dreadful.
A bit of digging around on the internets regarding Raspberry Pi audio quality, and I came across this, an experimental firmware update for improved headphone jack audio quality, using a different DAC interface. I ran "sudo rpi_update" (I ran it on Oct 12th) and went and added two lines to my /boot/config.txt file: "audio_pwm_mode=2" and "dtparam=audio=on", rebooted and re-tested audio. The quality improvement is very noticeable; at least through my headphones, the quality of sound is now acceptable.
I turned my attention to trying to get amplified audio out of my setup. I connected up a class D audio amp I had left over from a previous project to the 3.5mm audio jack on the RPi, powered it using the 5V out from the RPi GPIO and connected it up to a 4 ohm 2.5W speaker that was inside the original piano keyboard. After configuring the RPi audio volume to 80% using "amixer cset numid=1 -- 80%" I was getting a reasonable sound coming out when playing. I ramped up the volume to 100% on the gain pot on the amplifier and played again: now quite loud and a little bit distorted. When I power cycled the RPi I was getting a fairly loud popping sound coming from the speaker, and also when the RPi booted up again. When I had the volume up to max I was getting a little lightning bolt brown out warning coming up on my screen too ... I decided to tune the volume down to a slightly more modest level of 80% again. Didn't notice the brown out issues again. I added a pot to the signal line coming out of the RPi and into the amplifier for volume control.
I did a bit of configuring in my /etc/rc.local file on the Pi to start everything up automatically:
amixer cset numid=3 1
amixer cset numid=1 -- 80%
fluidsynth -s -i -a alsa -z 256 -c 6 -f fs_config.txt /usr/share/sounds/sf2/FluidR3_GM.sf2
aconnect 20:0 128:0
The -c and -z flags are for controlling audio buffer sizes and number of buffers in fluidsynth/alsa: setting these I'm able to find an acceptable middle ground between low latency and infrequency of audio stuttering: I still sometimes get a little bit of stutter on instruments with complex, long lasting waveforms, but the latency is now at an acceptable level (I haven't figured out yet how to measure it precisely, but I mean from a playing/responsive feel perspective). I also setup a configuration file for Fluidsynth so I can automatically set the instruments for different channels (fs_config.txt).
Next thing I'm going to do is add some more controls onto the Teensy and then package the whole thing up in a new custom designed case. Stay tuned!