Slowbot

Play online

Help Slowbot activate the three lights in each level.
Slowbot’s power will gradually decrease over time.
Slowbot will become slower and headlight will dim as power decreases.
Moving makes Slowbot’s power decrease more rapidly.
Picking up a battery will restore some of Slowbot’s power.
Touching a mine will drastically lower Slowbot’s power.

20 levels total. What is your best time?

 

Post Mortem

One more Ludum Dare in the books.  Slowbot was my fourteenth consecutive entry in the 48 hour game development competition.

I’ve noticed that my ability to quickly make games in Unity has improved over the years.  What used to take two days to complete, I can now finish on the first day.  That leaves a lot of time left over to polish and add more levels.

The theme this time was Running out of Power.  Early on I had the idea of a robot that would get slower as its battery depleted, and the name Slowbot seemed catchy.

As with my previous entries, I made sure that the core gameplay was working first.  The objective of the game is fairly simplistic.  Collect the three colored lights to proceed to the next level.  Collecting a light originally made the entire room illuminate with the color of light collected, but I thought that gave too much away.  Therefore, I limited the size of the activated light.  The headlight provides some assistance in seeing nearby hazards, while the rest of the level remains dark.

I used a spotlight which projected forward from the player robot.  As the robot’s power declines, the luminosity of the light decreases.  Looking back, I probably would have set a minimum luminosity value greater than zero, because when the power gets below 10% it is too difficult to see anything, which does not seem fun.

The robot’s speed also decreases as the power decreases.  The pitch of the sound effect played as the robot moves is also lowered as it runs out of power.  Along with the headlight fading, it gives a realistic feeling of the robot running out of power.  The game is over when power reaches zero.  The only penalty for losing is having to restart the current level and reactivating the lights again.

There is only one hazard in the game, which are the mines.  The mines explode when you collide with them, which lowers the robot’s power by 25%.  I created an explosion prefab that briefly illuminates the area in orange, which gives the player feedback that they did something wrong.  I should have also added a particle effect to make it look like pieces of the ground were flying upwards, so maybe I will add that in a future release.  As I learned from developing previous games, it’s best to have the explosion as a separate prefab and not a child of the GameObject being destroyed.  That way you can go ahead remove the mine from the game world and not worry about the explosion being removed as well.  It is also important to set a lifetime variable for the explosion, and clean it up after a defined period of time so you don’t overload the game with numerous explosion objects hanging around.

By default, there are a few Unity settings which provide ambient lighting.  The Window > Lighting > Settings, Environmental Lighting value needs to be set to the color black.  Environmental Reflections also need to be set to None and Intensity Multiplier set to 0.  These settings had to be disabled, otherwise it would still be possible to see all of the walls and mines on the screen.  For this game, I really wanted the player to use the headlight like a real flashlight to search for mines and see the walls, which made having complete darkness a necessity.

Power can be restored by picking up batteries.  I used a picture of a standard AA battery as a guide when modeling it in Blender.  One difference I made was that I applied the texture that I created as the emissive material, so that the batteries can be seen in the dark.  I felt that not having the batteries always visible would make finding them too difficult.  It also allows me to use the batteries as a guide to show the player where to go, similar to how coins are sometimes used in Mario games.

One new technique I learned for this game jam was creating a meter for the UI.  For many of my previous games, I would just display health and things like that as a numerical value, which is not visually appealing.  Earlier in the week, I watched a tutorial on making health bars with different shapes using the sprite mask option.  This looked much better than the scaling approach that I had used in my other games.  I wish I had learned how to do the sprite mask technique a long time ago.

The level generation code that I wrote was similar to my Free the Frog and Ancient Adventure games, where I define the level layouts in text files, which are stored in the Resources folder and read as TextAsset objects.  Then I used a the Split method to read the lines which became the horizontal rows, and the ToCharArray method was used to get the character value of each cell.  Each type of object is represented by a unique ASCII character.  This made creating the levels really simple in my Notepad++ editor.  Walls are represented by # in the text file, and holes are represented by a period.  The player’s starting position is represented by @R, G, and B represent the three colors of lights.  Mines are represented by x.  A regular floor tile is just the space character.  I created twenty total levels using this format, each stored in its own text file.  The one trick of reading level data out of a text file is that you need to subtract the current row number from the total number of rows if you want the level laid out in the game like it is defined in the text file.  Otherwise, the first row will be closest to the camera and the last row will be furthest away from the camera.

There are drawbacks to using text files for defining a level.  Each cell can only contain one type of object.  For the lights, mines, and player position a floor object gets created as well.  There is no way to add additional information to an object in a cell, such as the initial direction an object is facing.  This could be resolved by using a format such as XML, but writing an XML parser takes up a lot of valuable time in a game jam.

I defined an empty Room GameObject, which holds all of the prefabs instantiated for the level.  This is done by using the SetParent method on the transform of the instantiated prefab.  I live dangerously and find the Room GameObject by using the Find method, but if for some reason the Room GameObject is inadvertently deleted, then the game will throw errors when it tries to set the parent to a null object.  The reason for using a Room is so that I can create method to reset the level, which is called when the player moves to the next level or when the player dies and everything needs to be reset.  The Room lets me loop through all of the child GameObjects and remove them all by calling Destroy on each.  If I didn’t have them setup this way, then I would have to check every object in the scene to see if it something that needs to be deleted when a level needs to be loaded.  For instance, the camera and LevelManager GameObjects are not deleted when a level is loaded.  One thing to look out for though is that you don’t just write a loop that keeps calling Destroy on the first index until the child count is zero, which will result in an infinite loop and crash Unity.  Actually, there’s a hacky way to get Unity out of an infinite loop, but I’m not going into that here.  Destory is actually a request to remove from the game and it isn’t actually gone until the next call of Update.  This has “bit” me in a few of my games, when I had expected a GameObject to be gone after calling Destroy, but something else was still able to reference it later in the code on the same call to Update.

I used a picture of a bomb diffusing robot as a guide when modeling the robot.  I made a freehand side outline in Gimp of the robot, using different colors for the tracks, body and arm.  Then I was able to use that outline in Blender as the background image guide.  I started with a plane and deleted all vertices except for one.  Then I kept extruding that vertex around the outline of the robot parts.  After the outline was complete, I joined all of the vertices to make a face.  Then I extruded that face for the body and tracks.  I duplicated the tracks so that there was one on each side of the body.  I used two cylinders for the arm, which I scaled and rotated until it matched my background image.  I took a picture of the metal on one of my shovels, which I used as the texture for the robot.

For the floor and wall textures, I took pictures around the house of concrete, bricks, fencing, and my hardwood floor.  I duplicated these and made normal maps from grayscale using the Unity texture options, while also modifying the bumpiness option.  By default, the texture looked too grainy in the light.  I did compare the lighting effects with and without the normal maps, I think it is a definite improvement, but I don’t think normal maps make it look that much better.  It would probably make more of a difference if I was using a normal map generated from a high-poly model, instead of just generating it from grayscale.  I’ve used normal maps before, but this is the first time I used them in a Ludum Dare competition.

The music for the game was composed in GarageBand on my MacBook Pro.  I used two electronic style instruments for the main melody.  Then I alternate between the two instruments, making slight modifications in the melody.  I used one of the electronic style drummers to generate the beat.  I really can’t say I did much different in the music composition process for this game, but I am happy with the results.  This time I used an A major scale, instead of the default C major scale.  According to some music sites, the A major scale has a more cheerful tone.  The music that I  composed does remind me of the victory melody of an old RPG.  I sort of feel like my music creation skill has maxed out, so I may need to look into new techniques.  Music seems to be one aspect of game development that has remained the same, at least since after the 16-bit era when real musical recording started being used.  I exported the mp3 and GarageBand project files  to my Windows system using the system connect in the Mac finder.  I’ve learned that it is important to transfer the music project files as well, so that I can check them into my source code repository, in case I need to change or regenerate the music files later.

For this game, I decided to play Foley artist again and create all of my own sound effects instead of using a program like BFXR.  The battery pickup sound was made with my power drill.  The moving sound of the robot was made with my electric can opener.  The mine blast was me crumpling paper with the pitch lowered in Audacity.  The light switch sound was actually an old battery powered remote light switch held up to my microphone.

As with my Expand-O-Ray game, I did a voice over to give tips and explanation of the game.  Each tip is about 5 seconds long, and they are played at the start of the game sequentially with a slight delay between tips.  This could be improved by only playing the tips when they are relevant.  For instance, play the tip about mines reducing power after the player actually hits a mine.  This would require an additional boolean for each tip to track if the tips has been played, to prevent the tip being played every time a mine is hit.

I added the level times in the last few hours of the competition.  The times are held in a List of floats, which is sized to match the numbers of levels.  I just kept adding Time.deltaTime to the current array index while the player is active.   This prevents the time from being increased during the “Level Complete” message and “Game Over” message, and much easier than trying to keep track of start and end times.  It also allows me to continue adding time to the current level after the player respawns.  After the player completes all twenty levels, all of the level times and the total time is displayed.  I tried displaying the total time while the game is playing, but looping over an array of twenty floats on every frame to get the sum caused a significant reduction in frame rate.  It probably wouldn’t be too difficult to save level times to a leaderboard, using my Unity/PHP/MySQL leaderboard system.

There are a few improvements I would like to make with the game.  I had originally planned on having a drone type enemy, which would patrol and chase the player when close.  The robot model needs a lot of work, so that the tracks and wheels look more realistic.  I would like to have different types of robots with different properties, along with purchasable upgrades.  I think it would be neat to also have a movable arm on the robot that the player would need to operate to diffuse bombs to pass.

I was happy with how this game plays and feels.  Some people have told me that it’s one of my better games.  If I  release this game on major platforms, I would probably change the name, because Slowbot (while catchy) doesn’t sound like a very exciting game.  There has been evidence of really good games not selling well just because they had a bad name.  I also want to do more research on how real bomb diffusing robots work, which may give me additional ideas for this game.  I think I would be neat if a game like this could be used to train people how to use real bomb diffusing robots.

Gameplay video

 

 

Released

Sliding Socket

Updated ResistorKit to detect LT and RT presses.  These triggers are different than other buttons, because they return a value representing how far the trigger is pulled, instead of an pressed/not pressed value like the other buttons.  It functions similarly to one axis of the thumbstick, by returning a float between 0.0 and 1.0.  Methods for trigger pressed and trigger released for both triggers were added to the RessitorKit screen class.  Therefore, I had to update all of the classes in the game to implement those four methods.  I also added a property in the ResistorKit menu to draw the text with a small drop shadow.

With the LT and RT now implemented, I was able to update the gun shoot code to allow the player to aim up and aim down.  When aiming up, the player will shoot their gun at a 45 degree angle upward in the direction that they are facing.  The same goes for aiming down, except that they are shooting at a 45 degree angle downward.  Holding both LT and RT will cause the player to shoot directly upward.  One issue that I’ve noticed is that if the player is standing next to a block  on the right and shoots upward, then the projectile will immediately collide with the block.  So to fix this I moved the starting position of the projectile to the center of the player horizontally.  Now shooting upward will not collide with blocks to the left or right.

For the Health sockets, I noticed that it was only using the lowest equipped health socket.    This is because the lowest health socket was being checked first in the if/else/elseif statement.  I reversed the order of the health socket check so now it looks for the highest socket first.  This also prevents stacking of the health sockets, but stacking could be implemented by keeping a counter of the sum of the values of the health sockets.  Ideally, there will be a  warning message that will prevent the player from equipping more than one health socket.  It would warn the player that any other health socket will be removed if a health socket is already equipped.

screen061

Added new sockets to the game, which are the sliding sockets.  The player can no longer slide by default, so they now have to equip one of the sliding sockets to get into areas only one block high.  The higher level sliding sockets will allow the player to slide longer.  There may be obstacles such as overhead spikes which will require the player to slide longer to pass.  The text next to the “A” display has also been update to not display if the player can not slide.

Noticed a few issues with some of the sockets.  The level 3 fire socket didn’t work properly, which was due to an incorrect socket ID used in the shoot gun code.  The radiant plus 4 socket didn’t work, because I had not added that level yet.  Also, the weapon list would not populate the first the the player goes to the equipment menu.  This is because the setPlayer method was being set after the activeScreen method is called on the Equipment screen.  Moving the setPlayer before fixed this problem, which ensures that the EquipmentScreen has a reference to the Player to access the Player’s weapons.

 

Sliding and Enemy Update

http://youtu.be/r13UWoifors

The player can now slide to get through narrow areas.  Enemy graphics have been updated and collision detection improved.

Sliding

Modified the controls a little, so that pessing down now causes the player the crouch instead of crawl.  When crouching, the player cannot move.  I also went ahead and added 48 to the player’s Y position when crouching, so it doesn’t rely on the automatic fall to hit the ground, which previously made it look like the player was hopping.  Pressing the jump button while crouching will now cause the player to slide, which accomplishes the same thing as crawling to get into one tile high holes.  The difference is that the player doesn’t need to press the directional stick to move in a slide.

Keeping track of all the states when sliding can get confusing.  If the player is crouching and presses the jump button, then the slide flag gets set to true, but is the player still crouching?  If the player is not still crouching, then the crouching flag gets sets to false, but that means the player will have to press down again to crouch and slide, which doesn’t feel right.  Therefore, the crouching flag has to be kept set to true while sliding.  Normally, when the player is crouching, they are not allowed to move, so I had to write a special case for not crouching and sliding.  This way the player can hold down, and then repeatedly press A to keep sliding without having to press down again.  After some testing, the slide is now working correctly when the slide ends under a block that would cause a collision when standing again.  The player simply keeps sliding until they are not under a block.  The player can also press left or right to keep the slide going indefinitely as long as they are under a block.

One other slight annoyance is that the player cannot go to the crouching state while falling.  I will have to figure out how I want to handle that later.

Came across another problem, when pressing down again while sliding.  This would count as a second crouch, which would push the player into the ground.  I just had to add a check for not sliding in the set crouching method.

Also noticed an issue where the player could get themselves stuck in a block by continually crouching and then walking towards the block.  This only happened when next to a block one tile high.  I found that I was setting the player’s size in the update method, instead of in the set crouching method.  Setting it in the crouching (and sliding) method next to the line which updates the player’s position resolved the problem.

Enemy Update

Created a simple enemy model in Blender, which is a sphere with a cylinder through it.  I created a armature just like I did for the player, so that I was able to generate an animation of the model spinning.  After that was done, I generated all of the PNG file for the animation.  This time I used 30 frames, so it should rotate once for every half second.

I went through the same scaling and cropping process in Gimp.  This time I set the height to 48 pixels, which makes the sprite wider than the bounding box.  I think this is okay, and I just offset the sprite by the cylinder arm size when drawing it, so that the cylinders are not considered part of the hit box.

Modified the Gimp python script a little more to give the exported PNGs more meaningful names (than 0001.png).  Unfortunately, this means I have to update the script each time I want to perpend a name to the file which is a real hassle.  If I had the time, I would figure out how to make an options dialog box for the script to allow the user to enter the file name.

I created a new folder in the content folder area of the project specifically for holding the enemy animation images.  Again, I’m not using a sprite sheet because I don’t think it really improves performance that much.  However, it is something I can look into doing in the future.

To keep the enemies from floating in the air, I wrote some code in the enemy update method to make the enemy fall if they are not colliding with the ground if moved down by one pixel.  Unfortunately, this means I now have to crouch to shoot the enemies now.  I can balance that out later, or make enemies taller than one tile.  Also, I made it so that if an enemy collides with a block in the X direction, then it’s X velocity is flipped (multiplied by -1).  I also increased the range that the enemy can move in the room.  However, one issue I noticed is that if an enemy does move to the next room, then it will fall through the floor (since no block collision is performed outside the enemy’s own room), resulting in the stage becoming uncompletable, since all enemies have to be defeated to move to the next level.