Thursday 11 June 2015

Game # 7 : Frogger

Frogger is now completed and can be played here. Source code is on the github (see URL on right).

All these things are pretty much beta standard, incidentally. They pretty much work, but there may well be bugs in them I haven't found, things I haven't implemented (this doesn't have the Snake for example) and so on.

The title screen is fairly straightforward. It does use LocalStorage to maintain the high score table, though. I note some people are having problems with the asynchronous nature of LS.

Event 1 checks for the "FROGGER_KEY" - a key that is a Unique ID for the hi score table array and stores those scores in JSON.

Event 3 handles it missing - it initialises the array in Event 4, then puts any current score in the sixth element of the array and calls refreshScoreboard which sorts the array and writes it back out to LocalStorage.

If it exists, Event 5 requests the value  - it is a two step process, and when that is retrieved it copies the JSON string into the array and refreshes the scoreboard again.

I wonder if the mistake people make is thinking if you check for a key you can immediately retrieve it - you can't, you have to check it's there, then retrieve it, both with callbacks.

The GameInitialise sets everything up. Event 2 resets to a new game if the level global is -1 - this is because levels are started just by restarting the layout, and Event 3 initialises the time, score and lives part.

Event 6 just sets the speed and direction of motion.

Events 7 through 11 were fiddly. I wanted groups of things - the turtles and the logs (which are built up from a left bit, middle bit and a right bit) to be useable as such so (say) all the turtles in a group dived at the same time. I gave them all a group ID. Doing this automatically is a little fiddly, it picks up all "wrappers" (anything that moves on the screen not including the player) that don't have anything on their left in event 8 (e.g. the left most part of every group). Event 9 marks them as a "first Object" - kind of a leading role, and saves their position. Event 10 reselects all wrappers, and the While loop in 11 keeps going right and assigning objects a group ID while it the position has a wrapper there, sort of scanning to the right.

Event 12 through 19 remove some objects from the screen, so the river is harder at higher levels (the vehicles just go faster). To do this it maintains a count of how many "first Objects" there are in each row. This is required because if you remove every object in a row, you can't get to the home area. (There's probably a better way of doing this !). Events 13 and 14 count this.

Event 15 defines the number to remove. Event 16 picks a wrapper in the top half (e.g. not the cars) that hasn't already been deleted (group ID is +ve). It then picks a random wrapper instance where there are at least two of them in the row, saves the groupID in "groupToKill" and adjusts the count in the array (Event 17). Finally 18 and 19 delete all objects with that Group ID, by setting it to -1 and destroying it (I'm not sure if it is destroyed immediately or it is queued for later ?)

Events 20-25 adjust the start positions. The logs etc. are drawn on so this shifts a randomly chosen line (event 22) 10 times (event 21), wrapping them round (event 24,25). Effectively this means the starting position of everything varies.

Events 26 and 27 set up the turtle animations. You want them to all sink at different times, but turtles in the same group should all sink together. So all turtles are initially non sinking, then all "lead Turtles" have a timer with a random time. When this fires (28,29,30) all turtles in that group have their animation set to sinking which means they will dip under the river from time to time.

Events 31-33 set up the already home frogs, which is checked in a global array HomeFrogOccupied - when a 'slot' is filled, then it stays filled until you have reached the next level. The object of Frogger is to fill all the home slots. If it is not occupied, the home frog is still there, it is just invisible. It is required for collision detection.

Events 35-36 solved a bug which I tried to reproduce but couldn't. I originally used 'wrap' to make the various screen objects wrap, but it didn't quite work with the logs, which were built up from 64x64 parts. Occasionally a pixel wide line appeared between them. I suspect some sort of rounding, but the manual wrapping at 35 and 36 fixes this.

Event 37 updates the timer bar 4 times a second and the remaining events just update the timer, score and lives

Actually that bit was the most difficult to get right, the Player section which follows was much easier. Some of those sections involved quite a lot of experimenting. I knew what I wanted to do, but wasn't quite sure about how C2 actually did it best. So a useful experience.

In the Player section, events 2 and 3 set the level up, play some music and tun the main tune on when the into twiddle has finished.

Sections 5 through 23 handle the movement. This is hard coded, as it is odd.  The frog jumps about in 64 pixel squares from oldX,newX to oldY,newY. If it isn't jumping at present, events 5-8 set these values up dependent on the state of the keyboard controls. In event 10, if we are about to jump it sets a whole load of stuff - there is a time value which controls the jump, we aren't riding anything in a jump (ridingUID identifies the object we are riding on), it's now jumping, and we add 10 to the score.

In event 12, if we are riding an object, we update the player position from that offset (if the log moves and you are on the log, you move). An offset is kept from the origin of the log (or turtle).

Events 13 to 16 are the actual jump. If the jump time has expired, the position is updated for accuracy and we are back in non jumping mode. Event 15 handles landing on a turtle or a log by setting ridingUID and the offsets. Alternatively, we are in mid jump, and we lerp the position between the start and end of the jump and use a sine value to make it look as if its jumping by tweaking the scale.

Events 17 to 23 deal with the consequences of colliding with a Home Frog - the visible or invisible marker we created earlier. If its visible, then we've jumped into an occupied slot - which is terminal. Event 18 also checks that we've jumped reasonably accurately, not just that there is some collision.

Event 19 is fired on success - we mark the slot occupied in the HomeFrogOccupied array (note, the home frog knows which slot it is in). and add scoring for thatt and unused time. We also set isLevelComplete to true, which we clear in 20,21 if we find an empty slot in the HomeFrogOccupied array (e.g. we haven't done all five home slots). Finally we restart the layout.

Event 24 to 29 deal with death. 24 is outside layout, or off the bottom of the screen. Event 25 is time out. Event 26 handles landing in the river. Events 27 and 28 handle being on a turtle that has sunk, and 29 handles being hit by a car.

I cheated in event 30,31. If we are dead, we use the sound effect being played (tagged "dead") as timer control, which is probably bad practice. Sorry :) While it is paying 31 spins and shrinks the player.

Event 32 detects the end of that sound effect, and decrements the life count. Depending on whether you have any lives left, the game either restarts the layout, or goes back to the title screen, with a score for this game in the score global.

So onto Game#8 which is, I think, Pacman.

No comments:

Post a Comment