# Creating a Multiplayer Drawing Game with Phaser and MongoDB

When it comes to MongoDB, an often overlooked industry that it works amazingly well in is gaming. It works great in gaming because of its performance, but more importantly its ability to store whatever complex data the game throws at it.

Let's say you wanted to create a drawing game like Pictionary. I know what you're thinking: why would I ever want to create a Pictionary game with MongoDB integration? Well, what if you wanted to be able to play with friends remotely? In this scenario, you could store your brushstrokes in MongoDB and load those brushstrokes on your friend's device. These brushstrokes can be pretty much anything. They could be images, vector data, or something else entirely.

A drawing game is just one of many possible games that would pair well with MongoDB.

In this tutorial, we're going to create a drawing game using Phaser. The data will be stored and synced with MongoDB and be visible on everyone else's device whether that is desktop or mobile.

Take the following animated image for example:

In the above example, I have my MacBook as well as my iOS device in the display. I'm drawing on my iOS device, on the right, and after the brushstrokes are considered complete, they are sent to MongoDB and the other clients, such as the MacBook. This is why the strokes are not instantly available as the strokes are in progress.

## The Tutorial Requirements

There are a few requirements that must be met prior to starting this
tutorial:

-   A MongoDB Atlas free tier cluster or better must be available.
-   A MongoDB Realm application configured to use the Atlas cluster.

The heavy lifting of this example will be with Phaser, MongoDB
Atlas, and MongoDB Realm.

>MongoDB Atlas has a forever FREE tier that can be configured in the MongoDB Cloud.

There's no account requirement or downloads necessary when it comes to building Phaser games. These games are both web and mobile compatible.

## Drawing with Phaser, HTML, and Simple JavaScript

When it comes to Phaser, you can do everything within a single HTML
file. This file must be served rather than opened from the local
filesystem, but nothing extravagant needs to be done with the project.

Let's start by creating a project somewhere on your computer with an
**index.html** file and a **game.js** file. We're going to add some
boilerplate code to our **index.html** prior to adding our game logic to
the **game.js** file.

Within the **index.html** file, add the following:

``` xml

    
        
                
        
        
        
    
    
        
        
    

```

In the above HTML, we've added scripts for both Phaser and MongoDB
Realm. We've also defined an HTML container `
` element, as seen by
the `game` id, to hold our game when the time comes.

We could add all of our Phaser and MongoDB logic into the unused
`
```

In the above code, we're defining that our game should be rendered in the HTML element with the `game` id. We're also saying that it should take the full width and height that's available to us in the browser. This full width and height works for both computers and mobile devices.

Now we can take a look at each of our scenes in the **game.js** file, starting with the `initScene` function:

``` javascript
async initScene(data) {
    this.strokes = ];
    this.isDrawing = false;
}
```

For now, the `initScene` function will remain short. This is because we are not going to worry about initializing any database information yet. When it comes to `strokes`, this will represent independent collections of points. A brushstroke is just a series of connected points, so we want to maintain them. We need to be able to determine when a stroke starts and finishes, so we can use `isDrawing` to determine if we've lifted our cursor or pencil.

Now let's have a look at the `createScene` function:

``` javascript
async createScene() {
    this.graphics = this.add.graphics();
    this.graphics.lineStyle(4, 0x00aa00);
}
```

Like with the `initScene`, this function will change as we add the database functionality. For now, we're initializing the graphics layer in our scene and defining the line size and color that should be rendered. This is a simple game so all lines will be 4 pixels in size and the color green.

This brings us into the most extravagant of the scenes. Let's take a look at the `updateScene` function:

``` javascript
async updateScene() {
    if(!this.input.activePointer.isDown && this.isDrawing) {
        this.isDrawing = false;
    } else if(this.input.activePointer.isDown) {
        if(!this.isDrawing) {
            this.path = new Phaser.Curves.Path(this.input.activePointer.position.x - 2, this.input.activePointer.position.y - 2);
            this.isDrawing = true;
        } else {
            this.path.lineTo(this.input.activePointer.position.x - 2, this.input.activePointer.position.y - 2);
        }
        this.path.draw(this.graphics);
    }
}
```

The `updateScene` function is responsible for continuously rendering things to the screen. It is constantly run, unlike the `createScene` which is only ran once. When updating, we want to check to see if we are either drawing or not drawing.

If the `activePointer` is not down, it means we are not drawing. If we are not drawing, we probably want to indicate so with the `isDrawing` variable. This condition will get more advanced when we start adding database logic.

If the `activePointer` is down, it means we are drawing. In Phaser, to draw a line, we need a starting point and then a series of points we can render as a path. If we're starting the brushstroke, we should probably create a new path. Because we set our line to be 4 pixels, if we want the line to draw at the center of our cursor, we need to use half the size for the x and y position.

We're not ever clearing the canvas, so we don't actually need to draw the path unless the pointer is active. When the pointer is active, whatever was previously drawn will stay on the screen. This saves us some processing resources.

We're almost at a point where we can test our offline game!

The scenes are good, even though we haven't added MongoDB logic to them. We need to actually create the game so the scenes can be used. Within the **game.js** file, update the following function:

``` javascript
async createGame(id, authId) {
    this.game = new Phaser.Game(this.phaserConfig);
    this.game.scene.start("default", {});
}
```

The above code will take the Phaser configuration that we had set in the `constructor` method and start the `default` scene. As of right now we aren't passing any data to our scenes, but we will in the future.

With the `createGame` function available, we need to make use of it. Within the **index.html** file, add the following line to your `
        
        
    
    
        
            
                
                Create / Join
            

            
                Game ID: 
            
        
        
            Not in a game...
        
        
        
    

```

The above code has a little more going on now, but don't forget to use your own application ids, database names, and collections. You'll start by probably noticing the following markup:

``` xml

    
        
        Create / Join
    
    
        Game ID: 
    

    Not in a game...

```

Not all of it was absolutely necessary, but it does give our game a better look and feel. Essentially now we have an input field. When the input field is submitted, whether that be with keypress or click, the `joinOrCreateGame` function is called The `keyCode == 13` represents that the enter key was pressed. The function isn't called directly, but the wrapper functions call it. The game id is extracted from the input, and the HTML components are transformed based on the information about the game.

To summarize what happens, the user submits a game id. The game id floats on top of the game scene as well as information regarding if you're the owner of the game or not.

The markup looks worse than it is.

Now that we can create or join games both from a UX perspective and a logic perspective, we need to change what happens when it comes to interacting with the game itself. We need to be able to store our brush strokes in MongoDB. To do this, we're going to revisit the `updateScene` function:

``` javascript
updateScene() {
    if(this.authId == this.ownerId) {
        if(!this.input.activePointer.isDown && this.isDrawing) {
            this.collection.updateOne(
                { 
                    "owner_id": this.authId,
                    "_id": this.gameId
                },
                {
                    "$push": {
                        "strokes": this.path.toJSON()
                    }
                }
            ).then(result => console.log(result));
            this.isDrawing = false;
        } else if(this.input.activePointer.isDown) {
            if(!this.isDrawing) {
                this.path = new Phaser.Curves.Path(this.input.activePointer.position.x - 2, this.input.activePointer.position.y - 2);
                this.isDrawing = true;
            } else {
                this.path.lineTo(this.input.activePointer.position.x - 2, this.input.activePointer.position.y - 2);
            }
            this.path.draw(this.graphics);
        }
    }
}
```

Remember, this time around we have access to the game id and the owner id information. It was passed into the scene when we created or joined a game.

When it comes to actually drawing, nothing is going to change. However, when we aren't drawing, we want to update the game document to push our new strokes. Phaser makes it easy to convert our line information to JSON which inserts very easily into MongoDB. Remember earlier when I said accepting flexible data was a huge benefit for gaming?

So we are pushing these brushstrokes to MongoDB. We need to be able to load them from MongoDB.

Let's update our `createScene` function:

``` javascript
async createScene() {
    this.graphics = this.add.graphics();
    this.graphics.lineStyle(4, 0x00aa00);
    this.strokes.forEach(stroke => {
        this.path = new Phaser.Curves.Path();
        this.path.fromJSON(stroke);
        this.path.draw(this.graphics);
    });
}
```

When the `createScene` function executes, we are taking the `strokes` array that was provided by the `createGame` and `joinGame` functions and looping over it. Remember, in the `updateScene` function we are storing the exact path. This means we can load the exact path and draw it.

This is great, but the users on the other end will only see the brush strokes when they first launch the game. We need to make it so they get new brushstrokes as they are pushed into our document. We can do this with [change streams in Realm.

Let's update our `createScene` function once more:

``` javascript
async createScene() {
    this.graphics = this.add.graphics();
    this.graphics.lineStyle(4, 0x00aa00);
    this.strokes.forEach(stroke => {
        this.path = new Phaser.Curves.Path();
        this.path.fromJSON(stroke);
        this.path.draw(this.graphics);
    });
    const stream = await this.collection.watch({ "fullDocument._id": this.gameId });
    stream.onNext(event => {
        let updatedFields = event.updateDescription.updatedFields;
        if(updatedFields.hasOwnProperty("strokes")) {
            updatedFields = [updatedFields.strokes["0"]];
        }
        for(let strokeNumber in updatedFields) {
            let changeStreamPath = new Phaser.Curves.Path();
            changeStreamPath.fromJSON(updatedFields[strokeNumber]);
            changeStreamPath.draw(this.graphics);
        }
    });
}
```

We're now watching our collection for documents that have an `_id` field that matches our game id. Remember, we're in a game, we don't need to watch documents that are not our game. When a new document comes in, we can look at the updated fields and render the new strokes to the scene.

So why are we not using `path` like all the other areas of the code?

You don't know when new strokes are going to come in. If you're using the same global variable between the active drawing canvas and the change stream, there's a potential for the strokes to merge together given certain race conditions. It's just easier to let the change stream make its own path.

At this point in time, assuming your cluster is available and the configurations were made correctly, any drawing you do will be added to MongoDB and essentially synchronized to other computers and devices watching the document.

## Conclusion

You just saw how to make a simple drawing game with Phaser and MongoDB. Given the nature of Phaser, this game is compatible on desktops as well as mobile devices, and, given the nature of MongoDB and Realm, anything you add to the game will sync across devices and platforms as well.

This is just one of many possible gaming examples that could use MongoDB, and these interactive applications don't even need to be a game. You could be creating the next Photoshop application and you want every brushstroke, every layer, etc., to be synchronized to MongoDB. What you can do is limitless.