My personal almost-game-jam: Hours 1-24 of 48
- I want to see how fast I can make one by myself using only Unity and third-party fonts and music (I'll make all the other assets myself)
- I want to jump on the bandwagon of people who have made side-scrollers
- I want to make just one game in under three months
- I want to release one multi-player action game in my life that people might enjoy.
I decided during a recent hiking trip to do just that; I would try to make my own eight-player Sunset Riders type of game. As I'm still committed to finishing my other in-development game Dominoze, I'm putting a cap of a total of 48 hours on this project before I stop and re-evaluate my goals. The actual time will be spaced out for as long as I want (and I'm guessing I'll be done in mid-June). It's like my own personal Game Jam without the Jam part.
I'm developing the game using the Unity game engine.
Decided to do the easy part first: The main menu. I created a new Unity project with Photon networking support. I grabbed the main menu script and the game script from my other project Dominoze and stripped them down to the essentials. Found a font on 007fonts for the western text (but I might replace it later), and set the buttons and game list up. Also drew the sun, a cactus and some mountains in MS Paint.
There are two background layers; one for far-away stuff (mountains, forests), and one for closer objects like cactii.
Got the background to animate by using simple texture coordinate translations and repeating textures. The mountains move slowly and the cactii move faster. Also found a good song for the main menu on AudioJungle, and put together a horse animation by grabbing the first gif off the Internet I could find as a reference, and hand drawing every frame. Also spent five minutes playing with my cat.
Cleaned up the main menu (except for the lack of options menu which is another ball of wax). Then I created a new desert background and the basic character animation frames; after that started pondering level 1's design. Just shooting some random baddies in the desert would be rather boring, so I decided I'll need to start drawing some old western buildings; including some foreground elements between the player and the camera. I also need to finish designing the player controls. One thing I liked was the concept of an "Input Focus" interface where the game manager decides what screen element gets your keystrokes. Before I add new scenery, I should get the player walking around and also able to aim and shoot the basic gun.
The player can now spawn in single player-mode and walk forward and backward. The character has a rigidbody so I can leverage Unity's physics engines to handle jumping and falling. I also added the "gun arm" that will ultimately aim toward where the mouse is pointing, but that part doesn't work yet. I also spent a fair bit of time deciding where and when the player is going to spawn. I'll finish that up in an upcoming hour.
The player can now fire bullets from their gun which eventually fall to the floor. It's not much in an hour, but I had to go back and recalibrate the player animation frames so that they were properly centered; and then I replaced all of the Unity "Plane" objects with my own square mesh where a scale of (1,1,1) truly is (1,1,1) and with just four vertices instead of a ton. Next I need to tweak the bullets, big time. They look very small and they may need to glow with the player's color; in fact I'm thinking the player and gun need to be scaled up a little bit as well.
I looked back at why it took 5 hours just to get to a player shooting, and my answer is: 1. I made the title screen 2. I did all of the art from scratch (it still would have taken a while to import someone else's art though) 3. I needed time to think through an easily maintainable way to make things work. Adding new weapons and items should be very easy to do, and I should almost never have to change existing code to support new items after the first time. 4. This is not a waterfall development model. 5. I'm not going to get everything right the first time.
Cleaned up the bullet appearance and also got it working with a physics collider; problem is it collides with the player so it never gets anywhere. For now I'm making it a trigger until I implement enemies and physics layers. I also made it so the camera can't go backwards to keep things very forward-moving (though I have yet to force the player to not go behind the limit as well). I also added a crosshair to replace the mouse cursor. Finally, the player is more elevated on the screen so that I can properly render a dirt trail under them.
I'll probably use the next hour to add support for jumping and ducking...then I can finally start bringing in the enemies.
I made a crouching texture, and got jumping and ducking to work. The tricky part was trying to make it work smoothly...in the end I ended up constantly polling for keys being up or down rather than just watching for key state change events. Based on all the key states in a frame together (and the player's upward velocity), I would update the player's appearance. It could be optimized, but I'll do that later. I also made the camera Y fixed for now...I may later decide to have levels were players move vertical like on a mountain climb.
Now I need to figure out how the enemies will spawn...and respawn?
I spent a good 20 minutes trying to figure out the enemy spawning system (Should they just appear from the sides of the screen? Should they continually spawn from central points?) until I came to the realization that I was overengineering it. Deciding there was always room for improvement later, I ended up simply going with the "Spawn once from a pre-defined point" model. Then there's the matter of the fact it's a network game; a poor design could easily cause the same enemy to get spawned multiple times by multiple players. Thankfully there is a variable in the photon library that defines a "master client," so I can be sure that one player is managing the enemies at a time.
My plan is this: If any player gets close enough to a spawn point, it will broadcast a "buffered" (meaning even new players get it) message that the spawn point was tripped. The player will also notify the master client in a non-"buffered" message that they need to spawn the enemy. The master player gets it, and the enemy is spawned only once. If the master client ever gets the message again, nothing will happen because they know the spawn point was already tripped....at least that's how it should work...
In this hour I created a basic enemy prefab, an Enemy class and a "ThugWithGun" class (which is the simplest enemy); and got part of the way through writing the trigger logic.
I also added a few more cactii in the background...because I'm using a projection view, putting them at different distances from the camera gives an altogether cool effect as you're walking.
Finished the code to make the enemy spawn, and the enemy is stationary for now. There will be time later on to do more advanced stuff (like leaping in from the side of the screen, appearing from windows, maybe initally sitting down playing cards...) and I'd like to keep things simple for now. I also got the enemy to fire to the left in regular intervals, and made the bullets properly pass through other objects of their kind (enemy bullets pass through enemies, player bullets fly through players).
The next natural step is to implement the player and enemy dying; but I'm far behind in network testing. Of course it works fine for one player, but I need to try getting a second and third player in and make sure everything is synchronized.
Got a little accomplished in a long time. Everything in "offline" mode was hunky dorey, but as soon as I tried to play on a server, things went downhill fast. For one thing, I couldn't even host my own game. Turns out my anti-malware software was blocking my connection thinking it was malicious. Then I found that things that used to work "offline" don't work "online," such as calling PhotonNetwork.Instantiate with non-atomic variables (like passing in a component instead of a string or a long integer). I also learned that I was going about observers / synchronizations incorrectly...for example if I didn't own a rigidbody, I needed to make it kinematic so that nobody else's physics engine but mine would try to act on it locally.
I'm close to getting it working; I just need to fix the enemy bullet color.
I studied some Photon scripts to learn how to handle player movements. It gave me a new perspective on things -- you have to design around components that may and may not be yours depending on the instance. You also can't bind key commands and player appearance/animation in the same function anymore because now you're at the mercy of the network engine for updating player orientation. A lot of the work in this hour was trying different things and then cleaning up some existing code to make it easier to read and build on. What I have right now kinda works (except for aiming; haven't figured that part out) but I might do a little more cleanup to make it player smoother; I'm disappointed at the latency between the time I move a player and the time I see it move on another screen, but the game should still be playable in these conditions.
After eating a "keep at it until it works" cake with commitment frosting and frustration sprinkles, I'm much happier now because now the players and enemy properly spawn and sync, and I've started work on making enemies and players die. Lesson to the reader: If you're developing a network game, make your own little network sandbox game first, heavily comment it, and keep it around. You will learn a ton! I also made the title screen look a little better.
I can't believe I'm already 25% toward my deadline...I'd better get in gear and slam down a few sodas if I plan on getting level 1 playable with scoring and loot drops within 12 more hours!
I cleaned up some existing code, and then added support for enemies and players dying (and apparently I need to hide the crosshair in the next hour). Now I just need to do respawning.
Got respawning to work. I made it so when the spawn timer expires, the player asks the master client to tell them where to respawn. If the master client is dead, then the player will respawn where the master client was when they died. Otherwise the dead player will respawn where the master client's character is. The reason for that is I don't want players to get left behind as everyone else is moving forward (of course the design fails when the master client is dead; but I'll revisit that). I also did a little more code cleanup / odds and ends, and made a new building. All my efforts are now for screenshot saturday on Reddit.
Got the second building done; and I even added a barrel as an obstacle that you can shoot! Also fixed a few little bugs I found along the way, and started working on letting the player jump onto ledges and started a third building.
There are now four buildings and the ledge jumping works despite being real slow and clunky. Right now you just have to press the Up command to get onto a ledge; but Down is already deserved for crouching so I need to re-evaluate the jumping key commands. I also brought a few more enemies and barrel obstacles into the scene although they're quite easy to kill if you just button mash your way around. Having enemies respawn might not be a bad idea.
Good progress in this hour. I made the enemies aim toward the closest player, fixed the ledge jumping, increased gravity, made enemy bullets slower, fixed the bug where the buildings were not scrolling as fast as the player, and added enemies throughout the map.
I also started on the fun stuff: Options menu (player name, color, volume controls), scoring; and after those are done...ITEM DROPS! Sound effects will come later.
Got the options menu finished (name, color, sfx volume, music volume), and moved the active game list into its own menu so it wouldn't fight the other buttons for space. I also looked up how to send player properties (such as color) to other players and was in the middle of writing code to do that when my hour expired. I think the main menu won't go through many more changes before time expires.
I finished getting the player attributes (name, color) to sync with all players, as well as scoring to work (which was rather tricky). I then tried a two-player playthrough (where one player just stands there doing nothing) and experienced some oddities where, sometimes after shooting an enemy, several warnings would appear in my Console window suggesting child objects of the enemy would be missing. There's also a problem where a new player will never spawn if the master player is dead at the time.
As badly as I want to work on adding drops and new weapons, I spent the rest of the hour investigating these bugs which still remain unsolved.
I believe the errors from the previous hour are now fixed; it was a combination of the barrel trying to act like an animated character, and all the players trying to control everyone else's bullets. Like I mentioned before, you have to think carefully about what your game components do in a networked environment! Now I can begin adding new weapons and limiting the rate of fire for the existing one.
I also pondered the enemy behavior a little more; and if I have time, I may change the enemy dynamics to what I call the "whack-a-mole" model.
Right now I make just one enemy appear at each spawn point as soon as it comes into screen range. Players can therefore advance through the game by incessantly shooting in the forward direction since their bullets will inevitably strike an enemy shortly after they appear on screen.
With whack-a-mole, there will be pre-defined indestructable spawn points where an endless (or limited depending on the setup) slow stream of enemies will come out at random times to fire on players. Enemies may also partially come out just to trick a player into shooting at them, and then wait until the player finally stops before they come out to fire back.
The advantage of whack-a-mole is that a stream of enemies can come from all corners of the screen, and that requires players to be more evasive and coordinated.
Of course this is all just thinking out loud; I need to start working on new weapons now. I'll go back to the enemy design later on after I do some playthrough testing.
I burned through the first 20 minutes fixing some earlier bugs that I discovered while thinking about the project as I fell asleep the night before. I also managed to get two new weapons fully functional:
- The Winchesta: Fires armor piercing (it won't disappear when it hits something) bullets that do 1.5x damage. No, it's not a Winchester....it's THE WINCHESTA! Say it out loud!
- The Shotgun: Fires four shells that do 2x damage at a time with a reduced range.
In the next hour I need to control the rates of fire, create ammo limits for non-pistol weapons, finish the rocket launcher; and make a weapon selection HUD that shows your weapons and ammo and your weapon hotkeys. If I have time, I'll do more network testing followed by starting on random weapon and ammo drops.
Well I refactored the player weapon class hierarchy a bit, put a limit on rate of fire, ammo, and threw together a HUD that lets you choose weapons (just barely in the nick of time). I also got the startings of the rocket launcher, but it's not done. I pretty much have two hours left to get the rocket launcher working, weapon drops, and a level 1 boss. I'm pretty sure that's not going to happen, so I need to prioritize:
I'll finish the boss, and then the rocket launcher.
I totally don't know where the time went. I did a little bit of refactoring, made it so you can't go past the end of the level, added gizmos so you could see the start and end points in the Unity editor, worked on the boss...and before I knew it, an hour had passed! This is it...I've got one more hour to go before the half-way point...can I get the boss done in time?
I finished the boss, got the rocket launcher rockets to unleash some smoke particles and got some play testing in with a good friend of mine who goes by the alias NullSoldier. I consider the test successful because the player elements kept in sync and the network-related bugs should be easy to fix.
You can see the video on YouTube.
I'm at the half-way point, so I decided to sit back and assess things.
Features I wanted to get done by now:
- Rocket launcher. It can fire rockets; but they don't explode, have smoke trails, or shake the camera yet.
- Item drops including weapons, hats, ammo, belts for increased capacity, and cigarettes for fun.
- If a player leaves and rejoins, then all the spawn points respawn enemies!
- If you play from Chrome, keyboard inputs may get ignored (it happened to NullSoldier; I couldn't duplicate it)
- Barrel don't fall in sync between players
- Sometimes you cease to be able to jump
- When your friend dies, they don't get thrown in a direction like enemies do
- No Music for level 1
- "Respawning in" should be your player color, not black
- Too much GUI screen clutter in boss fights; you can hardly even see the boss
- The horses in the title screen don't have tails
- Physics! I noticed while testing that fun with physics could be an untapped gold mine for this game...for example maybe a player could shoot a support pole that falls and unleashes a bunch of falling barrels; or maybe rocket-shoot a floor and the enemies on it all fall down. This game could be especially noteworthy if I come up with fun, clever and frequent ways to exploit the physics engine.
- Item drops including weapons, hats, ammo, belts for increased capacity, and cigarettes for fun.
- There needs to be explosions and fanfare when you beat a boss
- There needs to be a visual cue that shows you when you can jump
- More weapons (maybe a flamethrower if I have time)
- Hats and scarves
- Sound effects
- "Whack-a-mole" enemies
- Two more levels; one of them on horseback
- Weapon display at the top and score list needs cleanup
- Player hues need to be a little darker; it's hard to see certain colors.