Wednesday, 17 June 2015

Pacman now complete

Pacman is now completed - pretty much anyway and can be played here

Quite pleased with this one, it's nice. I've even coded the first intermission :)

I will write up the "how it works" part tomorrow.

Monday, 15 June 2015

Pacman (continued)

Progress on Pacman, I have implemented the moving and AI code. This is, tbh, more programming than Construct 2 ; the moving is handled by an event that acts on a family, and indirect functions (calls where the function is a variable not a constant) are used to get movement stuff and update animation. It's pretty much the same as subclassing a family. Sort of.

Also used Pathfinders for the first time. When Ghosts are eaten (you can't do this yet, everything just moves around) they turn into Eyes and return to the home base. This is done using Pathfinding behaviour.

A lot of the code is written now. The Ghosts especially behave like objects - they have a state (whether they are being chased or not) and operate fairly independently by changing their instance variables. I will probably have a controller object in this game that handles all the sequencing and so on, rather than have it implicit as part of Construct 2's event system.

Sunday, 14 June 2015

Game#8 : Pacman

Some progress with Game#8, Pacman. I have most of the graphics, and the layout. I think the walls might benefit from being fatter.

A couple of tricks I have done with this are : that the walls are only approximately positioned and sized, the final positions and size are sharpened up in code. This means making the maze walls fatter or thinner is very easy.

I have taken advantage of the symmetry of the playfield - only half of it is drawn on the C2 editor, the other half is created on the fly when the layout is loaded.

I have been away the last couple of days, and I'm going away for a week next Saturday, but I hope to get a completed Pacman before then.

Then there will be a short break before the next game, Berzerk.

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.

Tuesday, 9 June 2015

Frogger (continued)

Busy couple of days with one thing and another, so not much time spent on "Frogger". I have managed to get it as far as the game core being complete, save for if you die, you just stop - there's no lives down and restart code.

This won't take long, then there's some sort of high score table, and then it's ready to go out.

Sunday, 7 June 2015

Game#7 : Frogger

Game#7 is under way. This is Frogger, an old game where you have to dodge the traffic and jump across the river on the logs and (diving turtles).

Some oddities with this. Much of what you can see was almost trivially easy. It's basically just wrapping objects. Though the wrap doesn't quite work, as the logs are made up out of chunks, and using Wrap they became just a little seperated leaving black lines.

The interesting stuff comes with the adjustments for levels. I wanted to give all the objects a "groupID" - so the logs are built up of 2-4 bits of log, all of which have the same ID, and the turtles in the same groups all have the same ID. Allocating these automatically was quite tricky.

Then I wanted to remove some of the river objects on higher levels, again programatically so this involved picking a log/turtle group, checking it wasn't the only one (otherwise you couldn't get across ....) and deleting everything.

Then I wanted the turtles not to all dive at the same time. The dive animation was done with animations, so I had to delay the starting of the animation using a timer so they are all mixed up.

All of these things could have been done just by allocating instance variables "start it now" "drop this on level 4" but it was a better learning experience to do it the harder way.

Friday, 5 June 2015

Game # 6 : Jetpac Complete

Jetpac is now complete and is in the repository and can be run here.

As you can see it does look like the original, unsurprisingly :) It also has a title screen and a fake Spectrum loading screen (and no, you cannot select "Kempston Joystick")

I've split the events up into sections in this one, so I'll comment on them one at a time.

System

This controls general flow and set up. Event 1 resets score and lives if the level has been set to -1, and calls "SetupLevel". This function (Event 6 and 7) sets the rocket number (there are four), the enemy type (there are eight) and the game state which is either -3 (about to land) or -2 (construction). You can see -2 above, it's where the rocket needs assembling. -3 animates the rocket landing.

The game state is "How far you are through the process" -2 and -1 are rocket building, 0 to 5 adding fuel. When it reaches 6 the rocket takes off. The state is changed by dropping the correct objects (fuel bars and rocket bits) on the rocket.

Function 8 simply updates the scores.

Function 9 controls the set up of the screen. It selects the animation in the rocket object (there are four, one for each rocket) and stops it, because the rocket is not animated, it uses animation frames to display fuel states (the gradual colouring of the rocket). It creates two "components" - the two rocket bits on the platforms, positions them, selects the appropriate animation, and sets their carryID. This is compared to the game status, e.g. when game status is -1 this means you get the top of the rocket, and the top of this rocket has a carryID of -1 - this is how it knows you've picked up the right bit. If yo've already done the middle bit event 11 moves it onto the rocket and turns its physics off.

Event 12 deals with the fuel levels. Event 13 triggers a rocket landing (flight direction = 1 = down), which is handled by events 3 to 5, which moves the rocket until it is off the top or bottom, depending on which way it is going, only if this flight direction is set. Event 14 corresponds to game state 6 (the rocket is full of fuel) and makes it take off the other way (e.g. flight direction = -1)

Controllers

Controllers just modularises (is this a word ?) the controls. Doing it this way means it is easy to convert for different types of control. Different instance variables in the player maintain values depending on left/right/thrust and fire.

Enemies

Controls the enemies. Event 1 is a testing hack. Events 2 and 3 keep the numbers up to 4 by creating a new one every second if there aren't four. Event 4 handles the set up of the enemy colour, position and type, and Event 5 tweaks that if it is going right to left.

Objects are update every 0.25 seconds by calling "controlEnemy" with the enemy's UID. This allows us to control their behaviour. Event 7 does this, allowing the rate of change of that behaviour to be modified by "adjustRate". Events 9-11 handle platform collisions, enemies either die or bounce depending on an instance variable. Event 11 handles being shot by a laser, and event 12 destroys consequent explosions.

Event 14 is where the behaviour starts. It picks the correct instance and then varies the behaviour depending on the switch statement. I borrowed this description of how enemies move and this is roughly the same as that.

Events 19 through 23 are more complex - an enemy can either be stuck at the edge in which case it moves up and down (20-23) but it can be released to charge across 1 time in 10 (event 22 and 23).

Carriables

Carriables are things the player carries or collects. The current carried object is identified by an instance variable carriedUID in the player, which is cleared in Event 1. Event 2 stops dropping objects from accelerating.

Event 3 is the main object pick up - if the player hits a carriable object, and its the right one (game State = carryID) and its not carrying anything (carriedUID = 0) then pick it up. We make it stick in Event 7, and make it fall in Event 8 if it is approximately over the rocket. It also sets a "yTerminal" value for the carried object, this fires event 11 when it is dropped to a particular height and sets the game to the next status.

Events 12 and 13 launch fuel bars from the top of the screen when required (after the rocket is built if it needs building, e.g. gameState > 0, if it hasn't finished, and only one at a time. Event 14 similarly launches collectables which are objects that are just extra points, and are randomly coloured, these are picked up and scored by Event 15.

Player Movement

Handles player moving and firing, and is all conditional on the Player visibility. The player is driven using physics, so Events 2-4 apply forces for left, right and thrust. Events 5,6 and 7 set the animation depending on what the player is doing (note event 6 also reduces the velocity when walking).

Event 8 detects firing and creates a laser of quasi random colours, event 9 flips it if the player is facing left, and 10 and 11 change the colours.

Event 12 deals with player death (there is a cheat mode here), the number of collisions is reduced to 1 by "Pick Enemy Instance 0", and it reduces lives and spawns an explosion where the player was.

Event 14 handles the consequences of this. When that explosion finishes, it either restarts the layout (if there are lives left) or returns to the title page.