Category Archives: Game Programming

Shadow Casting for Field of View in 2D Games in Phaser JS

Kind of a long title, but that is exactly what this post is about. I will walk thru an implementation of a 2D shadow casting algorithm in JavaScript for a Phaser JS game.

What Is Shadow Casting For?

What is this useful for? Well in my case, I have a turn based tactics game where I want fog of war and limited visibility based on the terrain. The player’s field of view (or FoV) should only include squares that units that the player controls can see.

Some people also use this type of algorithm for determining which squares are “lit” in a dungeon by a light source.

How Does It Work?

The basic idea is you cast a “ray” down a diagonal and then sweep it across to another diagonal. Anytime you hit something that is marked as a wall (anything you can’t see thru), you stop. That object then casts a shadow along the ray that hit its edge behind it, blocking the view.

There is a somewhat more thorough explanation for it at this website, which is where I adapted my code from (the code there is in python).

The Code

class FovShadow {
    constructor(two_d_map, tile_size, map_offset) {
        this.map_tiles = {}
        this.map = two_d_map
        this.tile_size = tile_size
        this.map_offset = map_offset
    }
}

As you can see this is the constructor of the class. It takes in a two dimensional array representing the map tiles, how many pixels wide a tile is and an offset from the edge of the game screen where the map starts. The last two values are important for determining which tile a unit is on when running the algorithm against its position. The map_tiles variable will hole which tiles are visible to the player.

getVisibleTiles(unit, vision_reset) {
        if(vision_reset) {
            this.map_tiles = {}
        }
        let origin = { x: Math.floor((unit.x - this.map_offset.x) / this.tile_size),  y: Math.floor((unit.y - this.map_offset.y) / this.tile_size) }
        this.markVisible({depth: 0, column: 0}, origin, 0)
        for(let i = 0; i < 4; i++) {
            let first_row = new ScanRow(1, -1, 1)
            this.scanRowForVisibleTiles(first_row, unit.sight_range, origin, i)
        }
    }

This is the function that is called whenever we need to update which tiles are visible. If we moved a unit, we will need to reset the vision.

We determine which tile the unit is on based on their x and y position in the game, the tile size and the offset of the map. Then we mark the tile they are standing on as visible. Finally we begin scanning for visible tiles in each direction (up, right, down, and left).

class ScanRow {
    constructor(depth, start_slope, end_slope) {
        this.depth = depth
        this.start_slope = start_slope
        this.end_slope = end_slope
    }

    getTiles() {
        let row_tiles = []
        let min_column = Math.floor((this.depth * this.start_slope) + 0.5)
        let max_column = Math.ceil((this.depth * this.end_slope) - 0.5)
        for(let i = min_column; i <= max_column; i++) {
            row_tiles.push({ depth: this.depth, column: i })
        }
        return row_tiles
    }

    nextRow() {
        return new ScanRow(this.depth + 1, this.start_slope, this.end_slope)
    }
}

export default ScanRow

We take a short break from the FovShadow class to define the supporting class used by the code that scans for visible tiles, the ScanRow class. A ScanRow represents a row or column of tiles. It is represented by 3 values.

The depth is how many rows or columns removed from our starting point we are. The start and end slopes represent the slope (think geometry slope or algebra slope, its math time) of the rays that mark the diagonals of our field of view between obstacles or the edges of the quadrant we are scanning.

If you can see past a tile, you would call “nextRow()” to recursively begin looking at the next row. Now back to our Shadow Casting class.

    scanRowForVisibleTiles(row, max_depth, origin, direction) {

        if(row.depth > max_depth) {
            return
        }
        let prev_tile = null
        let tiles = row.getTiles()
        for(let i = 0; i < tiles.length; i++) {
            let cur_tile = tiles[i]
            if(this.isWall(cur_tile, origin, direction) || this.isSymetric(row, cur_tile)) {
                this.markVisible(cur_tile, origin, direction)
            }
            if(prev_tile && this.isWall(prev_tile, origin, direction) && this.isFloor(cur_tile, origin, direction)) {
                row.start_slope = this.getSlope(cur_tile)
            }
            if(prev_tile && this.isFloor(prev_tile, origin, direction) && this.isWall(cur_tile, origin, direction)) {
                let next_row = row.nextRow()
                next_row.end_slope = this.getSlope(cur_tile)
                this.scanRowForVisibleTiles(next_row, max_depth, origin, direction)
            }
            prev_tile = cur_tile
        }
        if(this.isFloor(prev_tile, origin, direction)) {
            this.scanRowForVisibleTiles(row.nextRow(), max_depth, origin, direction)
        }
    }

This is our recursive function to go through rows and columns (all called “rows” here) and mark tiles that we can see. We only go as far as our unit can see (they have limited sight range), represented by “max_depth”.

For each tile in our ScanRow, we first check to see if the tile is a Wall, meaning we can’t see thru it. If it is, we mark it as visible. If this tile is a Floor tile and the previous tile we checked is a Wall tile, we need to reset our vision slope and go one step further away. Another way of saying this is, if the tile you are looking at is a Floor and the last tile you looked at is a Wall, look past the Wall. Check what is on the other side.

If the tile you are looking at now is a Wall and the previous tile was a Floor tile, we need to check the next row and limit it to the slope or angle from our origin to the Wall we hit.

If we finished scanning the row and the last tile we looked at was a Floor tile, look at the next row.

That is the basic logic of the Shadow Casting algorithm.

I will include the helper functions below in case you want to implement this yourself. As a note, this is all prototype code and was not written elegantly or efficiently.

    isWall(tile, origin, direction) {
        let coordinates = this.getMapXYCoordinates(tile, origin, direction)
        if(coordinates.y >= this.map.length || coordinates.y < 0) {
            return true
        }
        if(coordinates.x >= this.map[coordinates.y].length || coordinates.y < 0) {
            return true
        }
        return (this.map[coordinates.y][coordinates.x] === WALL || this.map[coordinates.y][coordinates.x] === DOOR_CLOSED)
    }

    isFloor(tile, origin, direction) {
        if(!tile) {
            return false
        }
        let coordinates = this.getMapXYCoordinates(tile, origin, direction)
        if(coordinates.y >= this.map.length || coordinates.y < 0) {
            return false
        }
        if(coordinates.x >= this.map[coordinates.y].length || coordinates.x < 0) {
            return false
        }
        return (this.map[coordinates.y][coordinates.x] !== WALL && this.map[coordinates.y][coordinates.x] !== DOOR_CLOSED)
    }

    isSymetric(row, tile) {
        return (tile.column >= row.depth * row.start_slope && 
            tile.column <= row.depth * row.end_slope)
    }

    markVisible(tile, origin, direction) {
        if(!tile) {
            return
        }
        let coordinates = this.getMapXYCoordinates(tile, origin, direction)
        if(coordinates.y >= this.map.length || coordinates.y < 0) {
            return
        }
        if(coordinates.x >= this.map[coordinates.y].length || coordinates.x < 0) {
            return
        }
        this.map_tiles[`${coordinates.x}_${coordinates.y}`] = { x: coordinates.x, y: coordinates.y, is_visible: true }
    }

    markAllHidden() {
        Object.keys(this.map_tiles).forEach(function(key) {
            this.map_tiles[key].is_visible = false
        }.bind(this))
    }

    getSlope(tile) {
        return new Fraction((2 * tile.column - 1), (2 * tile.depth))
    }

    getMapXYCoordinates(tile, origin, direction) {
        // UP
        if(direction === 0) {
            return { x: origin.x + tile.column, y: origin.y - tile.depth }
        }
        // RIGHT
        if(direction === 1) {
            return { x: origin.x + tile.depth, y: origin.y + tile.column }
        }
        // DOWN
        if(direction === 2) {
            return { x: origin.x + tile.column, y: origin.y + tile.depth }
        }
        // LEFT
        if(direction === 3) {
            return { x: origin.x - tile.depth, y: origin.y + tile.column }
        }

        return { x: 0, y: 0 }
    }

The getMapXYCoordinates helps translate from the ScanRow to the actual 2D map. The getSlope function returns a Fraction class value for a more accurate slope (rise over run or in this case column/row over depth). The isFloor and isWall is pretty self explanatory. However I did implement doors that close and change their values from “Floor” to “Wall” and vice versa.

The isSymetric function is of note. One of the properties of the shadow casting algorithm is that if tile A can see tile B, then tile B can see tile A. If the math doesn’t work out in one direction for some reason, but does in the other then the two tiles are visible to one another.

Wrap Up

I know the explanation is not all that detailed, this was more of a quick breakdown of the implementation in JavaScript for any other Phaser devs out there.

Again, for a thorough breakdown including some handy visuals, check out the post at albertford.com

Keep getting wiser, stronger, and better.

2D Pathfinding in Javascript for Phaser 3

Working on one of the subgame prototypes for a larger game idea and needed to implement pathfinding on a 2D map.


One of the most common algorithms for this kind of pathfinding is called A*  (read as A Star).  The precursor is doing just a Breadth First Search.  I was going to make a full A* implementation but the breadth first search was more than fast enough for my small, 2D maps. And as this is a prototype, going with good enough while I flesh out ideas.


The Algorithm

To begin we want 4 things: the map, your starting point, your target point, and some way to keep track of where you are going next and where you have been.


The explanation I was following referred to the places you have to visit next as the “frontier” and I liked that name so we will use it here.


Begin by putting your starting point in your frontier.
Then we loop over the frontier like this:

  • Get the next location from the frontier
  • If this location is our target point
    • We are done. Exit the loop.
  • If we haven’t visited this location before
    • add it to our visited list
    • get all of its neighbors (up, down, left, right)
    • add them to the frontier if they are not walls
    • mark this location as their parent or previous tile.

Once this loop is complete, because either we hit the target or can’t get to it.  Take the last tile we visited and build a path by backtracking thru the parent or previous tile property until we get to our starting point.

Really a fairly straightforward algorithm that was actually kinda fun to implement and then see working.

This is the rough code I used for the prototype:

class Pathfinder {

    // map is a 2D array of integers, where 0 is a wall and anything else is walkable
    constructor(map) {
        this.map = map
        this.map_width = this.map[0].length
        this.map_height = this.map.length
    }

    findPath(start, end) {
        start.parent = null
        let frontier = [start]
        let neighbors = []
        let visited = []
        let current_point = start
        while (frontier.length > 0 && (current_point.x !== end.x || current_point.y !== end.y)) {
            current_point = frontier.shift()
            if(Object.keys(visited).includes(`${current_point.x}_${current_point.y}`)) {
                continue
            }
                
            neighbors = this.getTileNeighbors(current_point)
            neighbors.forEach(function (n_tile) {
                if(!Object.keys(visited).includes(`${n_tile.x}_${n_tile.y}`)) {
                    frontier.push(n_tile)
                }
            })
            visited[`${current_point.x}_${current_point.y}`] = current_point
        }

        let path = []
        while(current_point.x !== start.x || current_point.y !== start.y) {
            path.unshift(current_point)
            if(current_point.parent == null) {
                break
            }
            current_point = current_point.parent
        }

        return path
    }

    getTileNeighbors(tile) {
        let neighbors = []

        if(tile.x > 0 && this.isFreeTile(tile.x - 1, tile.y)) {
            neighbors.push({ x: tile.x - 1, y: tile.y, parent: tile })
        }
        if(tile.y > 0 && this.isFreeTile(tile.x, tile.y - 1)) {
            neighbors.push({ x: tile.x, y: tile.y - 1, parent: tile })
        }

        if(tile.y < this.map_height - 1 && this.isFreeTile(tile.x, tile.y + 1)) {
            neighbors.push({ x: tile.x, y: tile.y + 1, parent: tile})
        }
        if(tile.x < this.map_width - 1 && this.isFreeTile(tile.x + 1, tile.y)) {
            neighbors.push({ x: tile.x + 1, y: tile.y, parent: tile})
        }

        return neighbors
    }

    isFreeTile(x, y) {
        if(this.map[y][x] === 0) {
            return false
        }
        return true
    }
}

To make this into a more efficient pathfinding algorithm like A* you would need to change the Frontier into something like a Priority Queue that sorts the next tile to check based on a heuristic like distance to target.

You can find another good article comparing Breadth First, A*, and Dijkstra algorithms here.

Keep getting wiser, stronger, and better.

WebShipBattleChess Prototype: Part 1

Quick note about motivation and actually getting started and finishing your game. If you find yourself just not working on your game or having trouble getting started, you are probably trying to build something that is too far outside your skill set or with a tool you are not familiar enough with.

Tricks to try:

  1. To get started, follow a basic tutorial to get the boilerplate code of your game setup.
  2. Scope the complexity down even further, even if it is already pretty simple.
  3. Use boxes, circles, rectangles or other basic shapes as placeholder art.
  4. To finalize your game, polish one area at a time. Just like cleaning a house you don’t do everything at once. You clean just the floor or you clean just one room.
  5. To add complexity, do it step by step. Don’t try to add to many things at once and test things as you go.

As a practical example, I was having trouble getting myself started on this game. Part of the problem was trying to make it too complex to start even though it is only a sub game of a even more complex idea.

The original idea was to make the game network multiplayer from the start with a python server and SocketIO. But writing the authentication, login, etc. was like a wall that I just did not want to deal with right now.

Once I realized this I changed the scope. Right now I am working on just getting the client working as a local multiplayer with no backend server and no authentication. A lot simpler which means I got started and made progress. The progress has me feeling good and more motivated to add more things.

To start with I found a nice set of boilerplate from https://phasergames.com which let me put the skeleton of a PhaserJS game in place and start adding features.

The first thing I added was a single scene with an image in it.

class SceneMain extends Phaser.Scene {
constructor() {
  super("SceneMain");
}

preload() {
  this.load.image("light_freighter", "images/light_freighter.svg");
}

create() {
  this.add.image(100, 100, "light_freighter");
}
}

For a nice free tool to make quick placeholder images, you can download https://inkscape.org

Keep getting wiser, stronger, and better.

Dipping My Toes Into UE4

Today I began working in Unreal Engine 4. I am following along a tutorial that was linked from the Unreal Engine launcher about making a turn based game.

The video seems a little fast paced and I have had to pause it frequently to follow along. Part of this is because I am working on one monitor and need to switch windows. The other part is I am familiarizing myself with how to use the interface. It is fairly intuitive but I did have to Google how to perform some actions.

The blueprint system for programming is actually pretty cool. Super powerful for people who have never written code by hand before.

Reducing Draw Calls

In this particular tutorial the instructor is using Instanced Static Meshes for floor tiles to reduce draw calls. One of my goals when I get to making games more full time is to have a good many of them be playable on average hardware so optimizations like this interest me.

I did a little research and as far as I can tell there are a lot of optimizations that can be used to cut down on the graphic work such as LODs (Level of Detail), Occlusion, and a few others.

When it comes to Static Mesh vs Instance Static Mesh there are a couple trade offs. If all the objects being drawn are on the screen, Instance Static Meshes win hands down. However, if just one of the meshes in the Instance Static Mesh group are on the screen they all get drawn. Because of this it is better to use Instance Static Meshes for piles of things that are likely to be drawn on the screen at once, like a pile of gems.

Looking forward to learning more about this powerful engine.

Keep getting wiser, stronger, and better.

Unreal Journey

This year I will be teaching myself about Unreal Engine and how to work with it. This is one of many goals for the year but an important one.

There was more than one project that I came across last year that I would have loved to be able to contribute or work with but every single one of them was using the Unreal Engine and my current experience with it is zilch.

Often we miss opportunity because we are not prepared for it. I certainly did.

I will be perfectly honest. This feels like a pretty big challenge I have given myself. I don’t even know where to begin right now other than with the tutorial videos on the Unreal Engine website.

But begin I will. And this time next year I may have even create a small game or 2 to show off my new skills.

What skills will you have this time next year that you didn’t before?

Keep getting wiser, stronger, and better.

Guide to Building a Monkey-X Game to Android

Step 1: Import the project to Android Studio

You will need to import the project to Android Studio as a Eclipse project. File – New – Import Project and select the folder with your Monkey-X android build. This is usually ../appname/main.buildv86e/android_new although your main.build folder may be named differently depending on which version of Monkey you are compiling against.

Step 2: Rename the package

The default package name is “com.monkeycoder.monkeygame”. This must be renamed as that is already taken on the Play store.

You will need to rename this in the Android Manifest, the main Java file, the res – layout – main.xml file, and the gradle build script. In the Java file you will need to rename the package at the top as well as the ANDROID_APP_PACKAGE variable (constant?).

I also recommend renaming the application and activity labels in your AndroidManifest.xml file from “Monkey Game” to your games title.

Step 3: Create an Icon

You will need to bring in your own icon for the app. This needs to be brought into the “res” folder and then referenced from the AndroidManifest.xml file.

Simply do a File – New – Image Asset and select the file you want to import. Android studio will generate various resolution versions of it for you.

I believe you will want at least a 512×512 pixel image. It kills 2 birds with one stone because Android will use it for the icon and 512×512 is the resolution needed to upload your icon to the Play store page.

Step 4: Test it on an actual device

After doing these steps, sometimes you miss something and the app will crash. Make sure to test your Android Studio build on an actual device now.

Step 5: Create a Signed APK

You will need to create a Signed APK file to upload. Android Studio makes this pretty easy. If you don’t have a key or a keystore already, it will walk you through setting one up. Then use the key to create a Signed build of your game that will be used to publish.

Step 6: Fill out information on Google Play and Upload the Signed APK

You now need to go through all of the questions and info for the Google Play store. This includes uploading your icon as well as a feature image that at time of this writing is 1024×500 pixels. You can put whatever you want here but probably should make it related to your game. Additionally it asks for screenshots. Most phones can do this although it could be difficult with an action game, or you could do it with an emulator.

There is also an area to put a link to a youtube video if you want to cut a short promotional video for your game.

Other steps include filling out a content rating questionnaire, linking to a privacy policy, and setting up purchases and adds (to name a few).

Quick and Dirty

This is a brief overview of the whole process. Mostly just notes to myself with reminders about changing things in Android Studio for the next time I release a game like this.

Hope you find this helpful.

First Monkey-X Game: Final Boss and Game State for Winning

The game is getting pretty playable, but like all good things it needs to come to an end.

If you remember from our little design exercise, we have a goal that will end the game. When we defeat the Big Bad Boss.

In this step, we will add the Boss into the game and create a Game Won state for when the player defeats the Boss.

Getting to the Big Bad Boss

The boss is going to be another character that we add to the game. We want him to be tougher than any of our enemies so far and we want him to shoot at the player to make it challenging.

Let’s start by creating the Boss character.

So now we have a field where we can put our Boss and we have a method that we can call to create him, but we don’t want to call it right away because we only want to have our Boss show up when we get far enough into the game.

So we will need 2 more things. First we need something to track whether or not it is time for the Boss to show up, and once this tracker is satisfied we don’t want to create another Boss.

Ok, at this point when you run your game, if you survive for 15 seconds or so you should get to a point where the small enemies stop getting created and you see a large purple shaded box representing the boss.

Boss Behavior

Now that we can get to the Boss, we want an EPIC BOSS FIGHT! Ok, maybe it won’t exactly be epic at first, but we can start by giving him some basic behavior. We also need to add the ability to shoot the Boss.

To start with we want to add some simple AI to the Boss’ movement. Nothing fancy, just moving the Boss up and down.

Now your Boss should go up and down the screen. Really, really simple pattern for now. We can get fancy and create more complicated logic for the movement later.

Damaging the Boss

Now that the Boss is moving, we need to be able to destroy it so we can win the game. This means we need to wire up the collision detection for our player’s projectiles and the Boss hit box. We also need to make sure the Boss takes damage when it gets hit.

Now projectiles will collide with the Boss but our current Character code won’t handle this correctly after the Boss gets hit. So we need to make a few tweaks to the Character class.

Now you will need to go into your main Game class and update the constructors for your Player and the Final Boss. The regular enemy constructor is fine since it just uses the default. There is also a check when firing projectiles that needs updating.

Now your projectiles should hit the Boss and damage it. However you should notice that once the Boss’ health drops to 0, the Boss is still there and the projectiles just fly past.

We still need to make the OnUpdate method a little smarter about what happens when the Final Boss is destroyed.

Destroying the Boss and Next Level

Destroying the Final Boss is simple enough. In the OnUpdate code, simply add a check to remove the Boss from the game when his health reaches 0. But what we really want to do is change scenes to a show that we beat the boss.

In order to give us some room to give the game more depth, we are not actually going to have the player win the game after beating the first boss. Instead we are going to have the game transition to a Next Level scene.

Now we have a scene we can transition to, lets transition to it when our Boss dies.

And we don’t want to get stuck there so lets add a method to allow the player to continue.

Now when you play the game, after you destroy the Final Boss, you should get to the Next Level scene. Then if you hit enter it should start play again but your score will remain the same.

Actually Winning

What we really want to do in this chapter is give the player the ability to beat the game. In order to do that we need to add 1 more scene, the Game Won scene.

If you have been following along, you have already gotten pretty familiar with adding scenes to the game by now, and could probably do this yourself. Just in case here is the code for the Game Won scene.

Now we need to be able to actually get to this scene. Defeating the Boss doesn’t get us there, it only gets us to the next level, and right now we have infinite levels.

First let’s put a way to track what level we are on and set a maximum number of level. Then we can use those 2 values to tell us when to end the game.

Now whenever we get to the Next Level, lets update our current level and see if it is the final level.

Now when the player beats the boss on the last level, they have beat the game.

In the next section we will make the Boss a little tougher and let him shoot back.

First Monkey-X Game: Restarting the Game

Now that the player can get to a game over state, we need to let them restart the game without having to refresh the web page.

Hit ENTER

We will add a new check for after the player has been destroyed for the ENTER key to be pressed to try again. Also we will add a message in the game over screen to tell players what they need to do.

Let’s start by adding the instructions to the Game Over screen on how to restart.

Now we want a way to reset the game whenever ENTER is pressed from the Game Over scene. We will do this by creating a small method in our game that goes through and deletes all of our enemies and bullets and recreates the Player.

You will notice that we have a new method here called CreatePlayer. Our next step is to refactor the code from our OnCreate method into this method so we can recreate our player when we reset the game.

The final step is to add the code to call the GameReset method when the ENTER key is pressed from the Game Over state. This will make its home in the Game’s OnUpdate method.

And now our player can easily start over when their little character gets destroyed.

Our next step will be creating a boss character and an end state to our game.

First Monkey-X Game: Player Destruction and Game State for Losing

Our player can fire projectiles and destroy enemies. He can run into enemies and take damage. But right now the player does not get destroyed.

In this part of the tutorial, we will move to a Game Over state whenever the player gets destroyed. This will cover some of the basics of FantomEngine’s layers and scenes.

Game Over State

Right now, we only have the default layer and scene which we are playing our game in.

In order to be able to move to a Game Over state, we need to create a new scene that the game can transition too.

Now we have a play scene that we will run the game in, and a game over scene that we will show when the player gets destroyed.

Right now the game over scene doesn’t have anything in it. In the game’s OnCreate method, we will build our game over scene by adding some text that says “GAME OVER”.

It is important to remember with FantomEngine that whatever layer is the current default layer will be the one that things get added to. This is why we need to change the default layer to the game over layer when adding the Text object and switch back when we are done.

NOTE: The font system is not particularly well documented for FantomEngine. It supports FontMachine and EZGui style fonts. You will need a PNG file and a TXT file describing the layout in your project_name.data folder for it to work. I was able to use a tool called Hiero to convert Google font VT323 to this format. There will be an appendix section covering this. There is a sample font in the examples that come with FantomEngine which you can use in the meantime. I will also try to make the converted font I did available.

Switching Scenes

Our final step will be to add a check to the OnUpdate method to switch the scenes if the player is destroyed.

And now we have a playable and basic version of our game where the player can move, shoot enemies, and be destroyed which ends the game.

First Monkey-X Game: Enemies and Collision

Now that the player can move around the screen, let’s give him some enemies to avoid.

Start by adding a collection to hold our enemy characters.

Now we want to modify the Character class just a little so we can have enemy characters and all of them don’t move when the player touches the controls.

Since we are only going to create 1 player character and we are going to be creating multiple enemies, we are going to give the is_player variable a default of ‘False’ so we can make all our enemies by only passing the first parameter.

Now we need to go and change our player creation code to tell the game that it is a player

Make Some Enemies

Now it is time to make some enemies. We are going to start by making a little generator that creates enemies every 3 seconds or so in our OnUpdate method. We also need to initialize our enemies list.

Now when you run this, you should have some little yellow boxes floating across the middle of the screen every 3 seconds.

Randomize the Enemies

Enemies that always come straight down the middle of the screen every 3 seconds are not that exiting. Let’s spice it up and improve our enemy creator code.

We are going to do 4 things. First we want to randomize where the enemies start. Next we want to randomize how fast they are going. We also want to randomize how often they appear. Finally we want to randomize how big they are and what color they are.

Running into enemies

Now that we have some enemies flying around the screen, lets add some code that checks if we ran into one of them or not.

This adds the checks, but right now we don’t have a way to tell the engine to do anything about them.

What we want to do is extend the engine so we can override its OnObjectCollision method. To do this we are going to create a CustomEngine class that extends Fantom Engine. Create a new custom_engine.monkey file and put in the following code.

One of the ways of passing information back and forth with the engine is by setting Tags and Text on the object. In this case we are going to tell the engine that whenever a PLAYER object and an ENEMY object collided, mark the ENEMY object as DESTROYED and make sure it cannot collide with the PLAYER again.

Now we need to go back and add the text to the player and enemy boxes. Also, in our OnUpdate method, we will remove all the enemies that get destroyed.

Now when you run the game, every time your little character runs into one of the little enemy squares, the enemy should disappear.

Now the Game Truly Begins

With collision working, we can start adding projectiles for our hero to stop the enemies before they even touch his little box.

Tutorial Part 6
Tutorial Part 8