Flash Tutorial Links:
Play Games: HTML5 Tutorials:

How To Make Frogger

Navigate your cute little frogs across a crazy road and a swift flowing river back to their home sweet lotus leaves

History of Frogger

An arcade game that debuted in 1981 by Konami. Objective of the game is to guide frogs to their homes across hazardous terrains of car-filled roads and rapid rivers. The game is highly regarded as a classic from the golden age of video arcade games. The gameplay and theme was pretty novel back then.

Download the Game Files

The files you need to create this game can be downloaded from here.

Part 1

import flash.display.*;
import flash.events.*;
import flash.ui.*;

public class Frogger extends MovieClip
{
    private var life, timeElapsed, totalTimer:Number;
    private var p1speedX, p1speedY:Number;
    private var gotoWin, gotoLose, standingOnLog:Boolean;
    private var logs, cars, homes, logsYPos, carsYPos:Array;

    public function Frogger()
    {
        
    }

The 3 standard packages are imported. You will almost always need display and events. As for ui, it'll be useful to import it in too.

The following variables will be used for the game. life keeps track of how many lives the player has, timeElapsed is used to keep track of the time, and totalTimer will indicate how long the player has to play the game.

p1SpeedX indicates the player's intention to move horizontally. A value of 1 means the player wants to move right, and -1 means left. Likewise for p1SpeedY in the vertical axis.

gotoWin and gotoLose are standard boolean variables you'll see throughout the tutorials. Once they are set to true, the game jumps to the respective Win or Lose frames.

standingOnLog indicates whether the player is standing on the log in the upper half of the game (i.e. the river). This is needed because when it is true, the player should move together with the log to simulate the behaviour that he is standing on the log. Also, this will protect him from dying by being in the river zone as you'll see later.

logs, cars and homes are the array variables used to keep track of the respective objects in the game.

Part 2

    //All Start Functions
    public function startMenu()
    {
        stop();
        btnStartGame.addEventListener(MouseEvent.CLICK, gotoStartGame);
        btnHowToPlay.addEventListener(MouseEvent.CLICK, gotoHowToPlay);
    }
    
    public function startHowToPlay()
    {
        btnBack.addEventListener(MouseEvent.CLICK, gotoMenu);
    }
    
    public function startWin()
    {
        btnBack.addEventListener(MouseEvent.CLICK, gotoMenu);
    }
    
    public function startLose()
    {
        btnBack.addEventListener(MouseEvent.CLICK, gotoMenu);
    }

Take a look at the ActionScript embedded in the different frames in the FLA file. You will see that in the menu labelled frame, it has a single function call to startMenu. Likewise, the win labelled frame will call startWin.

All the functions here handle those calls. We often refrain from writing codes in the FLA file so that everything can be seen in the AS file, and it helps tremendously in debugging your codes.

Basically, the function calls here only set up the codes for the buttons in that frame to make them function.

Part 3

    public function startGame()
    {			
        timeElapsed = 0;
        totalTimer = 99;
        life = 3;
        p1speedX = 0;
        p1speedY = 0;
        gotoWin = false;
        gotoLose = false;
        standingOnLog = false;
        cars = new Array();
        logs = new Array();
        homes = new Array();
        logsYPos = new Array(115,165,215,265);
        carsYPos = new Array(365,415,465,515);
        
        setupGame();
        
        //Spawn Cars
        //Row 1
        for (var i=1; i<=2; i++)
        {
            var newCar = new Car();
            newCar.x = -300 * i;
            newCar.y = carsYPos[0];
            newCar.speedX = 5;
            cars.push(newCar);
            addChild(newCar);
        }
        
        //Row 2
        for (var i=1; i<=3; i++)
        {
            var newCar = new Car();
            newCar.x = (170 * i) + 500;
            newCar.y = carsYPos[1];
            newCar.speedX = -5;
            cars.push(newCar);
            addChild(newCar);
        }
        
        //Row 3
        for (var i=1; i<=3; i++)
        {
            var newCar = new Car();
            newCar.x = (-220 * i) + 100;
            newCar.y = carsYPos[2];
            newCar.speedX = 8;
            cars.push(newCar);
            addChild(newCar);
        }
        
        //Row 4
        for (var i=1; i<=3; i++)
        {
            var newCar = new Car();
            newCar.x = (200 * i) + 350;
            newCar.y = carsYPos[3];
            newCar.speedX = -5;
            cars.push(newCar);
            addChild(newCar);
        }
        
        //Spawn Logs
        //Row 1
        for (var i=1; i<=2; i++)
        {
            var newLog = new LogWood();
            newLog.x = -300 * i;
            newLog.y = logsYPos[0];
            newLog.speedX = 5;
            logs.push(newLog);
            addChild(newLog);
            swapChildren(mcP1,newLog);
        }
        
        //Row 2
        for (var i=1; i<=3; i++)
        {
            var newLog = new LogWood();
            newLog.x = (170 * i) + 500;
            newLog.y = logsYPos[1];
            newLog.speedX = -5;
            logs.push(newLog);
            addChild(newLog);
            swapChildren(mcP1,newLog);
        }
        
        //Row 3
        for (var i=1; i<=3; i++)
        {
            var newLog = new LogWood();
            newLog.x = (-220 * i) + 100;
            newLog.y = logsYPos[2];
            newLog.speedX = 8;
            logs.push(newLog);
            addChild(newLog);
            swapChildren(mcP1,newLog);
        }
        
        //Row 4
        for (var i=1; i<=3; i++)
        {
            var newLog = new LogWood();
            newLog.x = (250 * i) + 400;
            newLog.y = logsYPos[3];
            newLog.speedX = -5;
            logs.push(newLog);
            addChild(newLog);
            swapChildren(mcP1,newLog);
        }
        
        addEventListener(Event.ENTER_FRAME,update);
        stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDownHandler);
        stage.addEventListener(KeyboardEvent.KEY_UP,keyUpHandler);
        
        stage.focus = this;
    }

startGame is called in the frame labelled game.

This will be the heaviest start-kind-of-function as you can see. Basically, the role of this function is to kick start all the necessary variables and states in the game. You can see that we set the timeElapsed to 0, because, well, at the start of the game, zero amount of time has passed.

life is set to 3 because we want the player to start with 3 lives.

There is a call to setupGame, which we'll examine later.

There are a lot of codes used to spawn the cars. In this Frogger game, the lower half of the game screen contains 4 rows of cars that the player must avoid. Hence, you can see 4 almost identical chunks of for-loop codes that creates those cars. In each row, 3 cars are spawned and they are dynamically given a speedX variable. (Note: You can dynamically give a new class, in this case Car, variables ONLY IF you have not declared an AS file for it)

There are 4 rows of logs in the upper half of the game screen, so again, you can see 4 chunks of for-loops that creates 2-3 logs each.

You may have noticed that I did a swapChildren function call each time I spawn a log. The reason why I did that is because any newly added MovieClip done with addChild will cause that object to be at the highest depth.

Meaning, if the player moves onto the log that was added, the log will actually cover the player. Since the player should be seen standing on the log, we use swapChildren to switch the depth order of the log and the player.

The game loop is created in the game by adding an event listener that listens to the ENTER_FRAME event. So, if you set the fps of the game to 30, this update function will run 30 times a second (assuming the computer is able to handle the complexity of each game loop).

We also add event listeners to listen to the key down event, which will be triggered when you hit a key. Likewise, the key up event triggers whenever you lift up a key.

What "stage.focus = this" does is to set the focus back into the game. When you clicked on a button to get from the Menu frame into the game, the focus is actually given to the button. Hence, if the game is played using the keys on the keyboard, they will not work unless you click on the game screen again to give it focus. You can try commenting this off to see what I mean.

Part 4

private function setupGame()
{
    //Check the classes of the movieclips and push them into the 
    //appropriate arrays
    for (var i=0; i< MovieClip(root).numChildren; i++)
    {
        var object = MovieClip(root).getChildAt(i);
        
        if (object is Home)
        {
            homes.push(object);
        }
    }
}

You will encounter this setupGame function very often in the tutorials here. The code is pretty straightforward. You do a for-loop across all the MovieClips on the stage. For each of them, you check if it is a Home object.

The is syntax checks if the object is a class of something. In this case, Home.

If it is, then the object is pushed into the homes array.

So, after the function has finished executing, you can be sure that the home array is populated with all the Home objects in the game.

This function is necessary because we added the Home objects in the game during design time by dragging them onto the stage from the library.

Part 5

//All Goto Functions
private function gotoStartGame(evt:MouseEvent)
{
    btnStartGame.removeEventListener(MouseEvent.CLICK, gotoStartGame);
    btnHowToPlay.removeEventListener(MouseEvent.CLICK, gotoHowToPlay);
    gotoAndStop("game");
}

private function gotoHowToPlay(evt:MouseEvent)
{
    btnStartGame.removeEventListener(MouseEvent.CLICK, gotoStartGame);
    btnHowToPlay.removeEventListener(MouseEvent.CLICK, gotoHowToPlay);
    gotoAndStop("howtoplay");
}

private function gotoMenu(evt:MouseEvent)
{
    btnBack.removeEventListener(MouseEvent.CLICK, gotoMenu);
    gotoAndStop("menu");
}

These functions handle the interaction when the player clicks on the buttons in the menu, how to play section, etc.

Before it jumps to the right frame, it removes the event listener from the button first. This is just good programming practice.

Part 6

private function keyDownHandler(evt:KeyboardEvent)
{
    if (evt.keyCode == Keyboard.A) 
    {
        //1st Player Left Key
        p1speedX = -1;
    }
    else if (evt.keyCode == Keyboard.D)
    {
        //1st Player Right Key
        p1speedX = 1;
    }
    
    if (evt.keyCode == Keyboard.W) 
    {
        //1st Player forward
        p1speedY = -1;
    }
    else if (evt.keyCode == Keyboard.S)
    {
        //1st Player back Key
        p1speedY = 1;
    }
}

private function keyUpHandler(evt:KeyboardEvent)
{
    if ((evt.keyCode == Keyboard.A) || (evt.keyCode == Keyboard.D))
    {
        p1speedX = 0;
    }
    if ((evt.keyCode == Keyboard.W) || (evt.keyCode == Keyboard.S))
    {
        p1speedY = 0;
    }
}

The keys A and D are used to move the player left and right. I avoid using the actual Left and Right keys because sometimes, browsers do not trap the keys very well and they ended up moving the scroll bars of the browsers instead.

You should also notice that I did not move the player directly inside the key down handler function. Instead, I just set the player's intention, which is denoted by the variable p1speedX or p1speedY to 1 or-1.

This is important because all actual game changing codes like moving the player will be better placed inside the game loop itself (i.e. the update function). In future when we want to implement a pause function to the game, this coding practice will make our lives much easier too.

Part 7

public function update(evt:Event)
{
    handleUserInput();
    handleGameLogic();
    handleDraw();
    
    if (gotoWin)
        triggerGoToWin();
    else if (gotoLose)
        triggerGoToLose();
}

This is how the game loop will look like. I've further broken down what the game loop should be doing by creating 3 other functions.

handleUserInput will take care of the player's inputs, be it mouse, or keyboard.

handleGameLogic will almost always be the biggest function. It handles everything from updating the player's movements, bullets' movements if any, to even more fanciful stuff like calculating advanced enemy AI.

handleDraw does little, but it is important in presenting the game states to the player by updating the UI. For example, if the player gains score, the Score textfield will be updated here to reflect the correct score.

After these 3 functions have executed, the gotoWin and gotoLose variables are checked to see if they are true. If so, the respective functions will be called, and the game will end thereafter.

Part 8

    private function handleUserInput()
    {
        //Handle player 1 position
        if (p1speedX > 0)
        {
            if (mcP1.x + 50 < 800)
                mcP1.x += 50;
            
            p1speedX = 0;
            mcP1.rotation = 90;
            mcP1.play();
        }
        else if (p1speedX < 0)
        {
            if (mcP1.x - 50 > 0)
                mcP1.x -= 50;	
            p1speedX = 0;
            mcP1.rotation = -90;
            mcP1.play();
        }
        
        if (p1speedY < 0)
        {
            mcP1.y -= 50;
            
            p1speedY = 0;
            mcP1.rotation = 0;
            mcP1.play();
        }
        else if (p1speedY > 0)
        {
            if (mcP1.y + 50 < 600)
                mcP1.y += 50;	
            p1speedY = 0;
            mcP1.rotation = -180;
            mcP1.play();
        }
    }

First, we check if the player intends to move the Frog right. The player's intention is always denoted by the variables such as p1speedX, which we set earlier in the key down handler function.

So, if p1speedX is greater than 0, it means the player wants to move right. In that case, we do a further check to see if the player will exceed the boundary of the game screen (800 in this case because the game is 800x600 large).

If not, we advance the Frog, which has an instance name mcP1, by 50 pixels to the right.

Thereafter, we reset p1speedX back to 0.

To make it more convincing, we also rotate the Frog by 90 degrees so that it faces right. A simple animation is played by calling mcP1.play(). You can open up the Frog MovieClip to see it. The legs are animated to move slightly.

The rest of the codes handle the exact same logic in the left, and vertical movements too.

Part 9

private function handleGameLogic()
{
    timeElapsed++;
    
    //update the cars
    for (var i=cars.length-1; i>= 0; i--)
    {
        cars[i].x += cars[i].speedX;
        
        //Check for collision with frog
        if (cars[i].hitTestPoint(mcP1.x,mcP1.y))
        {
            life--;
            resetGame();
        }
        
        if (cars[i].speedX < 0 && cars[i].x <= -50)
        {
            cars[i].x = 850;
        }
        else if (cars[i].speedX > 0 && cars[i].x >= 850)
        {
            cars[i].x = -50;
        }
    }

At the start, we increase timeElapsed by 1. Note that the way we count time here is by frames. We assume that there is no lag in handling the complexity of each frame, such that if timeElapsed is 30, we know that 1 second has passed, since we set the fps to be 30.

On slower machines, this might be a problem because each frame may take longer to draw and update. But, for the sake of ease of explanation and coding, this is how it will be done in these tutorials.

The first for-loop handles the cars. Each of the car has its own speedX property which we initialised in the startGame function. So, their own x position is updated by adding speedX to it.

hitTestPoint is used to check if the cars hit the Frog, and if so, the player loses a life, and the game is reset.

Then, we need to check if the cars have exited the game screen. So, we check if the car is moving left (speedX < 0) and if its x position is less than or equal to -50, we teleport it back to a position of 850. This effectively creates the looping effect of the car moving from right to left endlessly.

Likewise for the looping of the cars moving from left to right.

Part 10

//update the logs
var standingOnLog = false;
for (var i=logs.length-1; i>= 0; i--)
{
    logs[i].x += logs[i].speedX;
    
    //Check for collision with frog
    if (logs[i].hitTestPoint(mcP1.x,mcP1.y))
    {
        standingOnLog = true;
        mcP1.x += logs[i].speedX;
    }
    
    if (logs[i].speedX < 0 && logs[i].x <= -50)
    {
        logs[i].x = 850;
    }
    else if (logs[i].speedX > 0 && logs[i].x >= 850)
    {
        logs[i].x = -50;
    }
}

We first set the variable standingOnLog to be false. As we loop through the logs array which contains each and every log, we update each of the log by moving it according to its speedX.

Then, we check whether the current log has hit the player. The logic for the cars hitting the player and the logs hitting the player is very different. The cars kill the Frog, but the logs carry it.

So over here, if we detect a collision between the log and the player, we set standingOnLog to be true, and we move the Frog according to the speedX of the log it is standing on. This simulates the effect of the Frog being carried across the river.

The rest of the codes here handle the looping effect of the logs, similar to how the cars are looped from one edge of the screen to the other.

Part 11

//check if frog reaches homes
for (var i in homes)
{
    if (homes[i].hitTestObject(mcP1))
    {
        homes[i].gotoAndStop("occupied");
        
        mcP1.x = 400;
        mcP1.y = 565;
    }
}

The ultimate goal of the game is for all the frogs to reach all the homes. Over here, we loop through the homes array which contain all the Home objects and see if they collide with the current Frog.

If so, we set that current Home to be at its occupied state (open up the Home MovieClip to take a look). The occupied state has a picture of the Frog sitting on it.

We then teleport the player back to its rightful starting position. This simulates the effect that the current frog is resting on the home, and a new frog has appeared to carry on the journey again. But in actual fact, we keep on reusing the same frog.

Part 12

//Check if frog is in water
if (mcP1.y < 290) //in water zone
{
    if (!standingOnLog)
    {
        life--;
        resetGame();
    }
}

The Frog will die if it is in the water zone AND if it's not standing on a log. To check if the Frog is in the water zone, we simply check if its y position is less than 290.

If so, we check if the Frog is currently standing on a log (by checking the standingOnLog variable).

Part 13

//Check if timeElapsed or life lost
if ((totalTimer - Math.floor(timeElapsed/30) <= 0) ||
    (life <= 0))
    gotoLose = true;

//check if all homes are occupied
var allOccupied = true;
for (var i in homes)
{
    if (homes[i].currentLabel == "empty")
        allOccupied = false;
}
if (allOccupied)
    gotoWin = true;
}

The player will lose the game if it has zero lives, or if its time has run out.

 

The player wins the game if all the frogs have reached all the Home objects. To check that, we simply loop through the home array and check if each of the Home objects have their states set to "occupied".

Part 14

private function handleDraw()
{
    //Handle display
    txtTime.text = String(totalTimer - Math.floor(timeElapsed/30));
    txtLife.text = String(life);
}

Typically, we use the handleDraw function to update the UI displays. In this case, we update the text field that shows the time (txtTime) and the text field that shows the lives that the player has left (txtLife).

Part 15

private function clearGame()
{
    //clear away screen items
    for (var i=cars.length-1; i>= 0; i--)
    {
        removeChild(cars[i]);
        cars.splice(i,1);
    }
    
    for (var i=logs.length-1; i>= 0; i--)
    {
        removeChild(logs[i]);
        logs.splice(i,1);
    }
}

This function does the proper clean up job. It is usually called when we need to switch to another frame. Because the car and log objects were added with addChild, we must remember to remove them before we go to another frame.

The two for-loops loop from the last element of the array to the first, and removes the current element from the display hierarchy (by calling removeChild) and then splicing it away.

Whenever we're removing elements of an array during a for-loop around it, we must go from the back to the front.

Part 16

private function triggerGoToWin()
{
    clearGame();
    
    removeEventListener(Event.ENTER_FRAME, update);
    gotoAndStop("win");
}

private function triggerGoToLose()
{
    clearGame();
    
    removeEventListener(Event.ENTER_FRAME, update);
    gotoAndStop("lose");
}

//Misc Functions
private function resetGame()
{
    mcP1.x = 400;
    mcP1.y = 565;
}

}//end class

The two functions triggerGoToWin and triggerGoToLose are called whenever the respective win or lose states happen. Before we jump to the win or lose frame, we do need to call the clearGame function to clear away the objects we added manually, remove the game loop so the game doesn't continue running, before we jump to the correct frame.

The resetGame function just puts the Frog back to its starting position.

Download the Game Files

The files you need to create this game can be downloaded from here.

The Game

Now, let's help some frogs get back to their home. It's Frogger time!




How To Play

Objective: Move the frogs onto the 5 green patches at the top. Avoid the cars, and step on the logs on the river.

Controls: W - Move Frog Forward, S - Move Frog Backward, A - Move Frog Left, D - Move Frog Right


Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player

Flash Resources
Preloader FPS Display Sounds & Music
Keycodes Name Generator
Game Development Resources
Sprite Sheets


Posted by NgocHaiMK

on 2014-11-15 18:48:12

Can you show me Where the property "speedX" of newCar from,pls?


Posted by NgocHaiMK

on 2014-11-11 00:44:07

Why not declare for the buttons, but you still attached event for that button is?
At part 2:
btnStartGame.addEventListener(MouseEvent.CLICK, gotoStartGame);


Posted by MK

on 2014-11-09 10:11:00

hello.
pls help me....
when i type your code again
i had many error 1061:Call to a possibly undefined method addEventListener through a reference with static type class.
line error:
public function startMenu(){
stop();
btnStartGame.addEventListener(MouseEvent.CLICK,gotoStartGame);//line error
btnHowToPlay.addEventListener(MouseEvent.CLICK,gotoHowToPlay);//line error
}


Posted by emma

on 2014-09-15 07:39:03

When i applied it to your files it works correctly, but when i create on my own it doesn't work. Why does it find difficulty in accepting it?


Posted by emma

on 2014-09-15 07:06:22

hello. I have been applying the new functions of this game to the space invadors and each button works perfectly but when i press start the game the game wont start :/.
The background and the player shows but the bullets and the enemy dont seem to come up. Also the program doesnt show any problem and its getting difficult for me to find it.
Do you have any idea?


Posted by jason

on 2014-09-08 18:00:58

Thank you your tutorial is very nice. I want to ask if i want add level 2 how to write the code or goto where find ? Thank You very much.


Posted by Alawi Fitri

on 2014-05-29 17:13:30

Joseph, i tried to copy the coding for main menu. but it wont start. my game is basically the same as jovia like yours, but i inserted 3 food plus one bomb which is the destructor for the game. but that went fine except the part to create the main menu page which i created a button named btnStartGame in the AS layer. how should i declare in the "game controller" actionscript in jovia game? i posted here because i tried to learn the main menu page on this page


Posted by Joseph

on 2014-04-18 14:28:30

Thanks Geoff!

Overlooked that point. The main player, mcP1, will still be visible. You are right.
Can "get rid of it" in this way:

Under startGame function, add in a line of:
mcP1.visible = true;

Under clearGame function, add in a line of code:
mcP1.visible = false;


Posted by Geoff

on 2014-04-18 06:46:32

So from what I can tell, the reason you can't see frogs on the lose page here is because they're white on a white background. When the background or the frog is a different colour, the frogs show up.


Posted by Joseph

on 2014-03-23 15:30:54

Hrmm… I don't really see the frogs in the Lose page.


Posted by Jeff

on 2014-03-18 19:13:56

hello there.. i have been studying your game. I notice that the character or frog is still in the lose page. how can i remove it so it cannot see by everyone. thanks.


Flash Tutorial Links:
Play Games: HTML5 Tutorials:
gaming tools download on app store now!
Home | About Us | Contact Us
Powered by Make Flash Games. All Rights Reserved.
By Joseph Tan