So day 4 ended up being especially busy for me, I was only able to get back-end improvements done to the project. The code was restructured to better allow for multiple level appearances and characters, since currently I was editing the prefab’s inspector variables manually to see change.
Over both days, here’s what I got done:
Created some more character and environment sprites
Improved shader code
Rubble spawns from broken rocks, coins can also be obtained this way
Game states are back, and it’s now possible to lose the game
There are a couple of things I was asked to go in to detail on, I’ll try to do so on a break, that’s all for now. Next goal is to implement a high-score and total-score counter in order to unlock the extra content.
I took a break yesterday, then got back to work in the evening to add coins and our character’s “super move” which bursts through all the obstacles. Despite not putting so much time in to the project yesterday, a lot of interesting things went on to make all of this happen.
Making it Rain
The coins themselves were easy to implement with what I already had, the sprite is animated using my SpriteAnimation script, and Unity’s collisions take care of collecting the coin, but having the coin just disappear on you is boring. I wanted the coins you collect to fly in to the top-left corner where the score counter is going to be.
There are two coin prefabs in use: one to be collected by the player, and one “post-collection” coin whose only purpose is to curve to its destination and then vanish.
To curve the coin I wrote a BezierCurve script, you set it up with the coin’s starting position, a destination and some random point to the right of the screen
So this worked, the next issue was when to remove the collected coin from the scene. If you’re familiar with Unity you’ll notice that the coins in the gif used Trail Renderers to create the yellow streak that follows their path. Removing a GameObject that has a TrailRenderer component attached will remove the trail abruptly.
So instead the collected coins has a configurable value for their ‘end lifespan’, when they reach the end of the path, they will go invisible for this amount of time giving the trail time to catch up and finish gracefully, at which point the GameObject is then removed.
In addition to collecting the coins, I want for a special powerup to exist in the game, when collected it sends the player in to an over the top rainbow-charged frenzy that allows them to charge full speed through the game, destroying all the blocks in their path.
This was again done with the use of a TrailRenderer and ParticleSystem, probably the most challenging part of this for me was tackling shaders, something I’m not familiar with yet.
The Unlit/Cutout shader was my first test run with Unity shaders, since it was bugging me that I had to use a lit shader in order to preserve the transparency on my sprites. If you’re looking to get started quickly with shaders in Unity, Dan Moran’s video tutorial offers a very fast review of the basics, more than enough to get started (and then I’m sure there’s plenty of more advanced documentation of writing shaders out there)
The Unlit/RainbowPower shader was the result of plugging random numbers in and experimenting with the _Time and _Sin or _CosTime variables (Unity docs on built-in shader variables, a great resource)
I plan to experiment more with shaders as I’m starting to realise just how powerful they can be. One simple tweak I would like to make is having the rainbow shader transition through the colours more frequently.
All that was left with Super Move effect after this was to attach the shader to the trails, particles and player, then activate it at the right moment.
Breaking the Terrain
This was probably the toughest to get right, the approach is straightforward but it’s easy to make a mistake on some of the lower level parts.
We find the tiles surrounding the player
If they are solid, the Wave needs to be updated to set them to non-solid.
The TileMap for that Wave then needs to update the appropriate tiles to match the changes.
That last point is where a few attempts were made to get it right, remembering that I wrote the TileMap so that it would auto-tile: When a block is removed, the surrounding tiles must also be updated to no longer connect to it.
On to Day 4
I was actually up until 4am finishing off most of the stuff above, and I managed to optimise a few of the texture handling scripts while I was fixing the terrain breaking mechanic. That said I’m now ready to continue!
Today’s goals are to add rubble for when a block is destroyed, a get game states reimplemented (the skeleton project we were given to start us off has this set up already, but I disabled the original scripts while I got my concept to work.)
As always, you can direct your feedback or comments to my Twitter @Jindont.
After yesterday’s work, I ended the day with something playable! The level generator adds waves as the player progresses, and removes waves that have strayed off-screen. The player must tap to swap surfaces, avoiding all walls and future obstacles.
I’m going back to work today as I’m ahead of schedule with this project, so I expect to get less done this time, but my next two goals are 1) Make it look nicer, and 2) add obstacles and collectibles!
The Level Class
The Level class is responsible for managing the waves, and for retrieving tile data for the player. It iterates through the existing waves (which maxes out at 3 before they start getting removed) until it finds the wave that the player is on.
Waves have a width, height, starting X position and an array containing their collision data. So checking that the player is on a specific wave is as simple as making sure they’re between startX and startX+width. If the Player wants a tile, it calls level’s GetTileAt(x,y) function, which then calls the appropriate wave’s GetTileAt(x,y) function, and returns that result.
Currently, the player only requests 3 tiles from above or below its current position (depending on the direction it’s falling). If a resulting tile is solid, an AABB collision test occurs between two rectangles formed from the player and the tile positions and scales.
Coming from a Flash background, I’m not a huge fan of Unity’s animation tools. I find it very slow and overkill when trying to animate a simple 2D sprite, I’m assuming it’s meant for bigger tasks than that but it remains a painful process for cases like this.
So I wrote an AnimatedSprite script, it takes a Texture in the form of a spritesheet like the one above, and an array of animation data (name, frames used, and speed of the animation). The object can then be called on to set its animation to whatever you gave it, it will select the appropriate frame from the spritesheet and draw that on to a Quad.
I’m quite pleased with the result of this script, and a change of parameters is all it takes to implement a new sprite. When I have more time I’ll probably polish it and release it here.
This is all I have time to write for now, later today I hope to make a start on collectibles but we’ll see how that goes. For queries and feedback, reach out to me on Twitter @Jindont!
My 2 weeks started yesterday for the second round of the Search for a Star competition. The competition is a nationwide programming challenge for students and graduates that attempts to mimic an application process.
The second round gives contestants 2 weeks to make a game in Unity and publish it to the Windows Phone store. Last year my project got me a ticket to the final round interview, which was a miracle in itself given how restricted I was working on a slow, near broken laptop. I even had to take it to lectures to make progress as it was the only way I’d have time to finish everything. It was also one of my first times really using Unity.
Upgrade Your Workflow
This year, my placement has afforded me a proper setup, complete with a high-end desktop and multiple monitors, in addition I’ve booked time off work to focus on the project for its duration!
Having a faster machine has also allowed me to spend time looking at workflow improvements before going ahead with implementation. In my first blog entry I was looking at creating Tilemaps out of meshes, and said that I’d like to create an editor to allow for user created tilemaps.
So that’s what I focused on for Day 1, I rewrote the tilemap scripts and scriptable objects infrastructure, then proceeded to write an EditorWindow with very basic functionality for creating a level (or “Wave” as it’s called in this project)
Draft 2D Level Editor
So how is it used? First you set the dimensions of your wave, and the window will resize itself to accommodate a grid which fits your given size. Left-click dragging will create black ‘walls’, while right-click dragging will erase them. All that’s left then is to enter a name and click ‘Save Wave’, Unity then creates an asset out of your drawing.
Under the hood, the window itself works by calling DrawRect() and referring to a 2D array of integers to determine which colour to use for each rectangle, the window automatically resizes as you change the dimensions of your level as well.
Since my tilemap script attempts to stitch edges and corners together algorithmically, I have no need for a tileset display or the ability to see what tile you’re placing in this version, though it wouldn’t be too tricky to implement and certainly something I would add before releasing any of it.
On to Day 2
Today’s work will involve getting a factory in place to generate the level, the level is made up of ‘Waves’ which my editor is used to create. A Wave is just a segment of the level, I’ll create a large pool of preset waves which the factory will then choose between when it comes to generate the level.
Having an editor allows me to ensure that the waves are surpassable, and also allows me to do things such as pick viable locations for collectibles such that they will never be unreachable.
Hopefully my post tomorrow will give more of an idea of the actual gameplay as I wrap up on this workflow business.
If you’re interested to see more about the Tilemap solution, see my previous blog post. If you have questions or feedback, feel free to reach out to me on Twitter @Jindont!