Oral  Brekke

Oral Brekke

1670244120

How to Manage Game User Profiles with MongoDB, Phaser, & JavaScript

When it comes to game development, you’re almost always going to need to store information about your player. This information could be around how many health points you currently have in the game or it can extend beyond the game-play experience and into details such as the billing information for the person playing the game. When we talk about this type of data, we’re talking about a user profile store.

The user profile has everything about the user or player and doesn’t end at health points or billing information.

In this tutorial, we’re going to look at creating user profiles in a game that leverages the Phaser game development framework, JavaScript, and MongoDB.

To get a better idea of what we’re going to accomplish, take a look at the following animated image:

Phaser User Profile Store with MongoDB

Alright, so we’re not making a very exciting game. However, many modern games let you design your character either before the game starts or at some point as you progress through the game. These customizations that you add to your character are stored within the user profile. In our example, we’re going to customize our player and then send the information to our back end for storing in MongoDB.

The Requirements

There are a few components to this game, but we’ll be leveraging a JavaScript stack throughout. To be successful with this tutorial, you’ll need the following:

  • Node.js 12+
  • A properly configured MongoDB cluster with a gamedev database and a profiles collection

We’ll be using Node.js to create a back end API that will communicate with our MongoDB cluster. The game, which will be our front end, will communicate with our Node.js back end. We won’t go into the details on how to configure and deploy a MongoDB instance, but you’ll need one with a database and collection available for use.

If you want to quickly get up and running with MongoDB, consider deploying a MongoDB Atlas M0 cluster, which is FREE forever!

If you need help with configuring MongoDB, check out this previous tutorial on the subject.

To create a Phaser game, you don’t need anything beyond basic HTML, CSS, and JavaScript, so there aren’t any prior requirements for the game component.

Defining a Data Model for a User Profile Store in a Phaser Game

Before we get into the code, it is probably a good idea to explore what our user profile should contain. These aren’t blanket rules for all user profiles because often, games are not the same.

Based on the animated image toward the beginning of this tutorial, we know we have the following JSON:

{
    "_id": "nraboy",
    "cosmetics": {
        "eyes": "player-eyes-1",
        "mouth": "player-mouth-1"
    },
    "hp": 100,
    "mp": 30
}

The depth of the above profile is hardly anything, but we know for this particular player that they’ve decided to give their character a certain appearance and that character has a particular set of attributes such as health points.

If you want to see a more aggressive user profile, check out my previous tutorial, which is focused on a Unity game that I’m building with Adrienne Tacke.

You might consider adding a hashed password, player position within the game, stored items, or any other relevant field to your user profile.

What makes MongoDB particularly powerful when it comes to gaming and user profiles is how flexible the data model can be. If we release an update to our game that includes customizations to the color of the diamond, we could easily add that field to our NoSQL documents. Adding and removing fields requires no special migrations or coding, which translates to faster development and better code.

Developing the User Profile Store Back End API with Express Framework and MongoDB

We have a rough idea of what our user profile should look like for this particular game. Now we need to be able to add it to MongoDB and query for it. To do this, we’re going to create a back end with two simple API endpoints: an endpoint for creating a document with the profile and an endpoint for retrieving it.

For this particular tutorial, we are not going to worry about authentication. In other words, we are only going to be creating user profiles and not caring who they truly belong to.

Within a new directory on your computer, execute the following commands:

npm init -y
npm install cors express body-parser mongodb --save

The above commands will create a new package.json file and download the dependencies that we plan to use. We’re using the cors package because we plan to run our game locally within a web browser. If we don’t manage cross-origin resource sharing (CORS), we’re going to get errors. The express and body-parser packages will be for our development framework and the mongodb package will be for interaction with MongoDB.

Within the same project, create a main.js file with the following code:

const { MongoClient, ObjectID } = require("mongodb");
const Express = require("express");
const BodyParser = require("body-parser");
const Cors = require("cors");

const server = Express();

server.use(BodyParser.json());
server.use(BodyParser.urlencoded({ extended: true }));
server.use(Cors());

const client = new MongoClient(process.env["ATLAS_URI"]);

var collection;

server.post("/profile", async (request, response, next) => {});
server.get("/profile/:username", async (request, response, next) => {});

server.listen("3000", async () => {
    try {
        await client.connect();
        collection = client.db("gamedev").collection("profiles");
        console.log("Listening at :3000...");
    } catch (e) {
        console.error(e);
    }
});

The core of what is happening in the above code is we are laying the foundation to our Express Framework application by importing the dependencies and initializing the framework. When we start listening for connections, then we connect to MongoDB and define which database and collection we want to use. In this example, we are using a gamedev database and a profiles collection. You can use whatever makes sense to you.

The actual connection information for MongoDB is defined with an ATLAS_URI environment variable. You could easily use a configuration file or hard-code this value, but environment variables tend to work out the best for security and deployment reasons.

The connection string to be used would look something like this:

mongodb+srv://<username>:<password>@plummeting-us-east-1.hrrxc.mongodb.net/<dbname>

You can gather the information from your MongoDB Atlas dashboard after creating the appropriate user credentials for your database and collection.

When I add an environment variable, I generally do something like the following:

export ATLAS_URI="mongodb+srv://<username>:<password>@plummeting-us-east-1.hrrxc.mongodb.net/<dbname>"

There are numerous ways to do this and it may vary depending on your operating system.

So let’s focus on those endpoints.

When it comes to creating a user profile within MongoDB, we might have an endpoint that looks like the following:

server.post("/profile", async (request, response, next) => {
    try {
        let result = await collection.insertOne(request.body);
        response.send(result);
    } catch (e) {
        response.status(500).send({ message: e.message });
    }
});

The above endpoint is probably the most basic endpoint you can create. We are accepting a payload from the front end and we are inserting it into the database. Then we are returning the response that the database gives us.

In a production game, you’re probably going to want to validate your data to prevent cheating or other malicious activity. You can look into Schema Validation with MongoDB or use a client-facing package like joi instead.

With data going into MongoDB, now we can create our endpoint for retrieving it:

server.get("/profile/:username", async (request, response, next) => {
    try {
        let result = await collection.findOne({ "_id": request.params.username });
        response.send(result);
    } catch (e) {
        response.status(500).send({ message: e.message });
    }
});

After supplying this endpoint a username, which in our example is the _id field, we use it to find one particular document. That one document is returned to the game or whatever tried to retrieve it.

Again, we are not using any data validation or authentication for this tutorial as it is outside the scope of what we want to accomplish.

We now have an API to work with.

Building a Character Creation Game with Phaser and JavaScript

With the API in place, we can focus on the game itself. Remember, the depth of our game is quite shallow, but it offers a feature that a lot of games offer. That feature is customizing the character.

I’m not going to share my graphic assets because I want you to use some imagination for creating your own. Will you create something better? Probably, but it’s on you!

Create an index.html file on your computer with the following HTML markup:

<!DOCTYPE html>
<html>
    <body>
        <div id="game"></div>
        <script src="//cdn.jsdelivr.net/npm/phaser@3.51.0/dist/phaser.min.js"></script>
        <script>

            const phaserConfig = {
                type: Phaser.AUTO,
                parent: "game",
                width: 1280,
                height: 720,
                scene: {
                    init: initScene,
                    preload: preloadScene,
                    create: createScene
                }
            };

            const game = new Phaser.Game(phaserConfig);

            function initScene() {}
            function preloadScene() {}
            function createScene() {}

        </script>
    </body>
</html>

The above markup is standard Phaser boilerplate. We define the container where the game should be loaded, import the JavaScript dependency, and initialize the game while telling it to use the container.

The real magic is going to happen in the scene lifecycle functions. The initScene function is where we’ll initialize our variables, the preloadScene function is where we’ll load our media assets, and the createScene function is where we’ll do our first render.

Starting with the initScene function, add the following:

function initScene() {

    this.player = {
        "_id": "nraboy",
        "cosmetics": {
            "eyes": "",
            "mouth": ""
        },
        "hp": 100,
        "mp": 30
    }
    this.eyes = [];
    this.mouth = [];

}

For our example, the player object will represent our locally stored user profile object. It will look exactly like what you’d find stored in the database. This object could be as simple or as complex as you want to make it depending on your game needs. The _id field must be unique, so if you’d prefer to let MongoDB generate it, remove the field and create a username or similar field instead. It’s totally up to you.

The eyes and mouth arrays will represent all possible options for character customizations. The chosen customization will be stored in the user profile.

The next step is to look at the preloadScene function:

function preloadScene() {

    this.load.image("background", "game-background.png");
    this.load.image("player-base", "player-base.png");
    this.load.image("player-eyes-1", "player-eyes-1.png");
    this.load.image("player-eyes-2", "player-eyes-2.png");
    this.load.image("player-mouth-1", "player-mouth-1.png");
    this.load.image("player-mouth-2", "player-mouth-2.png");

}

In the above function, we are loading our image files and giving them a reference name to be used throughout the game. In the above example, the reference name is a pretty close name to the filename itself, but it doesn’t have to be.

I didn’t provide the image assets, so feel free to be adventurous.

The magic happens in the createScene function:

function createScene() {

    this.add.image(640, 360, "background");
    this.add.image(800, 250, "player-base");
    this.eyesActive = this.add.image(800, 250, "player-eyes-1");
    this.mouthActive = this.add.image(800, 250, "player-mouth-1");

    for (let i = 1; i <= 2; i++) {
        this.eyes.push(
            this.add.image(75, 90 * i, "player-eyes-" + i)
                .setCrop(150, 115, 100, 50)
                .setScale(0.5)
                .setInteractive()
                .on("pointerdown", () => {
                    this.eyesActive.setTexture("player-eyes-" + i);
                })
        );
        this.eyes[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 115, 100, 50);
    }
    
    for (let i = 1; i <= 2; i++) {
        this.mouth.push(
            this.add.image(215, 90 * i - 40, "player-mouth-" + i)
                .setCrop(150, 195, 100, 50)
                .setScale(0.5)
                .setInteractive()
                .on("pointerdown", () => {
                    this.mouthActive.setTexture("player-mouth-" + i);
                })
        );
        this.mouth[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 195, 100, 50);
    }

    this.add.text(289, 525, "PROFILE:", { fontSize: 36, color: "#FFFFFF", fontStyle: "bold" })
    this.profileData = this.add.text(289, 575, "", { fontSize: 16, color: "#FFFFFF" })

    this.startButton = this.add.text(1125, 640, "START", { fontSize: 40, color: '#000000', fontStyle: "bold", backgroundColor: "#FFFFFF", padding: 10 });
    this.startButton.setInteractive();
    this.startButton.on("pointerdown", () => {
        this.player.cosmetics.mouth = this.mouthActive.texture.key;
        this.player.cosmetics.eyes = this.eyesActive.texture.key;
        fetch("http://localhost:3000/profile", {
            "method": "POST",
            "headers": {
                "content-type": "application/json"
            },
            "body": JSON.stringify(this.player)
        })
        .then(response => response.json())
        .then(response => {
            this.profileData.setText(JSON.stringify(this.player));
        }, error => {
            console.error(error);
        });
    }, this);

}

Let me start by saying a few things first.

  • The game in my example is 1280x720 and the image assets reflect this.
  • The base image for the character and any cosmetic attachment image has the same resolution.

Rather than creating perfectly sized cosmetic attachment images, in my example, I just removed the base image but left the attachment images the same size but with transparency. Trying to figure out how to position small images could turn into more work than I wanted. Layering the images worked better.

With that said, we have the following:

this.add.image(640, 360, "background");
this.add.image(800, 250, "player-base");
this.eyesActive = this.add.image(800, 250, "player-eyes-1");
this.mouthActive = this.add.image(800, 250, "player-mouth-1");

The initialize images are placed on the screen with varying positions. The eyesActive and mouthActive represent the initial attachments for the character. We are only setting those to a variable because we plan to change them later.

Based on the naming convention of the images and the sizes we chose to use, we can display the attachment options for eyes on the screen:

for (let i = 1; i <= 2; i++) {
    this.eyes.push(
        this.add.image(75, 90 * i, "player-eyes-" + i)
            .setCrop(150, 115, 100, 50)
            .setScale(0.5)
            .setInteractive()
            .on("pointerdown", () => {
                this.eyesActive.setTexture("player-eyes-" + i);
            })
    );
    this.eyes[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 115, 100, 50);
}

In this example, we have two different possibilities for eyes. When pushing them into our array, we are specifying the position, but we are also changing a few things in regards to the image. For example, we know the images for attachments are mostly transparent. We can crop them and scale them to show only what we want. This is for selection purposes only, not displaying on the actual character.

We also want to respond to click events. When a click event happens, we want the appropriate image to be used as the active image. However, because we scaled and cropped our image, we need to redefine the hitArea, which is the area that should be clickable on the image.

Doing most of these steps is only a requirement if you are using images of the same size and planning to layer them like I am.

We can reproduce our steps for the mouth options:

for (let i = 1; i <= 2; i++) {
    this.mouth.push(
        this.add.image(215, 90 * i - 40, "player-mouth-" + i)
            .setCrop(150, 195, 100, 50)
            .setScale(0.5)
            .setInteractive()
            .on("pointerdown", () => {
                this.mouthActive.setTexture("player-mouth-" + i);
            })
    );
    this.mouth[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 195, 100, 50);
}

The steps are the same, but we are using different positions.

Remember, these two steps are for defining the thumbnails of our images and specifying a click region for them. While the active image is what we show on the character, the lead up is related to the thumbnails.

With the thumbnails out of the way, we can add some placeholder text of what profile we want to send:

this.add.text(289, 525, "PROFILE:", { fontSize: 36, color: "#FFFFFF", fontStyle: "bold" })
this.profileData = this.add.text(289, 575, "", { fontSize: 16, color: "#FFFFFF" })

The goal with this text is to only show what our player object looks like on the screen. This will give us a good idea of what was sent to our MongoDB database.

Finally, we have the button that will submit the data to our API:

this.startButton = this.add.text(1125, 640, "START", { fontSize: 40, color: '#000000', fontStyle: "bold", backgroundColor: "#FFFFFF", padding: 10 });
this.startButton.setInteractive();
this.startButton.on("pointerdown", () => {
    this.player.cosmetics.mouth = this.mouthActive.texture.key;
    this.player.cosmetics.eyes = this.eyesActive.texture.key;
    fetch("http://localhost:3000/profile", {
        "method": "POST",
        "headers": {
            "content-type": "application/json"
        },
        "body": JSON.stringify(this.player)
    })
    .then(response => response.json())
    .then(response => {
        this.profileData.setText(JSON.stringify(this.player));
    }, error => {
        console.error(error);
    });
}, this);

When the button is clicked, the image reference is added to the player object. Using a fetch operation, we can send the player object to our back end which will store it in MongoDB. Since we aren’t doing any data validation, this will succeed as long as the _id field is unique.

Phaser User Profiles in MongoDB

If you’re using MongoDB Atlas and you look at the data explorer, the documents should look something like the above image.

Conclusion

You just saw how to store user profiles for a Phaser game as NoSQL documents in MongoDB. Depending on the game you’re creating, the user profile documents could be significantly more complex in comparison to what was seen in this example. At the end of the day, you’re storing information about your game-play experience and that information could include information regarding the user such as password, billing information, or similar, and you’re also storing information about the game, which might include outfit customizations, item inventory, player location, or something else game-related.

If you are interested in seeing more Phaser examples with MongoDB, check out some of my previous tutorials on the subject. If you’re a Unity developer, I also have some content on that subject as well.

Got questions regarding this tutorial or MongoDB? Visit the MongoDB Community Forums and connect with us!

This content first appeared on MongoDB.

Original article source at: https://www.thepolyglotdeveloper.com/

#javascript #mongodb #phaser 

What is GEEK

Buddha Community

How to Manage Game User Profiles with MongoDB, Phaser, & JavaScript
Oral  Brekke

Oral Brekke

1670244120

How to Manage Game User Profiles with MongoDB, Phaser, & JavaScript

When it comes to game development, you’re almost always going to need to store information about your player. This information could be around how many health points you currently have in the game or it can extend beyond the game-play experience and into details such as the billing information for the person playing the game. When we talk about this type of data, we’re talking about a user profile store.

The user profile has everything about the user or player and doesn’t end at health points or billing information.

In this tutorial, we’re going to look at creating user profiles in a game that leverages the Phaser game development framework, JavaScript, and MongoDB.

To get a better idea of what we’re going to accomplish, take a look at the following animated image:

Phaser User Profile Store with MongoDB

Alright, so we’re not making a very exciting game. However, many modern games let you design your character either before the game starts or at some point as you progress through the game. These customizations that you add to your character are stored within the user profile. In our example, we’re going to customize our player and then send the information to our back end for storing in MongoDB.

The Requirements

There are a few components to this game, but we’ll be leveraging a JavaScript stack throughout. To be successful with this tutorial, you’ll need the following:

  • Node.js 12+
  • A properly configured MongoDB cluster with a gamedev database and a profiles collection

We’ll be using Node.js to create a back end API that will communicate with our MongoDB cluster. The game, which will be our front end, will communicate with our Node.js back end. We won’t go into the details on how to configure and deploy a MongoDB instance, but you’ll need one with a database and collection available for use.

If you want to quickly get up and running with MongoDB, consider deploying a MongoDB Atlas M0 cluster, which is FREE forever!

If you need help with configuring MongoDB, check out this previous tutorial on the subject.

To create a Phaser game, you don’t need anything beyond basic HTML, CSS, and JavaScript, so there aren’t any prior requirements for the game component.

Defining a Data Model for a User Profile Store in a Phaser Game

Before we get into the code, it is probably a good idea to explore what our user profile should contain. These aren’t blanket rules for all user profiles because often, games are not the same.

Based on the animated image toward the beginning of this tutorial, we know we have the following JSON:

{
    "_id": "nraboy",
    "cosmetics": {
        "eyes": "player-eyes-1",
        "mouth": "player-mouth-1"
    },
    "hp": 100,
    "mp": 30
}

The depth of the above profile is hardly anything, but we know for this particular player that they’ve decided to give their character a certain appearance and that character has a particular set of attributes such as health points.

If you want to see a more aggressive user profile, check out my previous tutorial, which is focused on a Unity game that I’m building with Adrienne Tacke.

You might consider adding a hashed password, player position within the game, stored items, or any other relevant field to your user profile.

What makes MongoDB particularly powerful when it comes to gaming and user profiles is how flexible the data model can be. If we release an update to our game that includes customizations to the color of the diamond, we could easily add that field to our NoSQL documents. Adding and removing fields requires no special migrations or coding, which translates to faster development and better code.

Developing the User Profile Store Back End API with Express Framework and MongoDB

We have a rough idea of what our user profile should look like for this particular game. Now we need to be able to add it to MongoDB and query for it. To do this, we’re going to create a back end with two simple API endpoints: an endpoint for creating a document with the profile and an endpoint for retrieving it.

For this particular tutorial, we are not going to worry about authentication. In other words, we are only going to be creating user profiles and not caring who they truly belong to.

Within a new directory on your computer, execute the following commands:

npm init -y
npm install cors express body-parser mongodb --save

The above commands will create a new package.json file and download the dependencies that we plan to use. We’re using the cors package because we plan to run our game locally within a web browser. If we don’t manage cross-origin resource sharing (CORS), we’re going to get errors. The express and body-parser packages will be for our development framework and the mongodb package will be for interaction with MongoDB.

Within the same project, create a main.js file with the following code:

const { MongoClient, ObjectID } = require("mongodb");
const Express = require("express");
const BodyParser = require("body-parser");
const Cors = require("cors");

const server = Express();

server.use(BodyParser.json());
server.use(BodyParser.urlencoded({ extended: true }));
server.use(Cors());

const client = new MongoClient(process.env["ATLAS_URI"]);

var collection;

server.post("/profile", async (request, response, next) => {});
server.get("/profile/:username", async (request, response, next) => {});

server.listen("3000", async () => {
    try {
        await client.connect();
        collection = client.db("gamedev").collection("profiles");
        console.log("Listening at :3000...");
    } catch (e) {
        console.error(e);
    }
});

The core of what is happening in the above code is we are laying the foundation to our Express Framework application by importing the dependencies and initializing the framework. When we start listening for connections, then we connect to MongoDB and define which database and collection we want to use. In this example, we are using a gamedev database and a profiles collection. You can use whatever makes sense to you.

The actual connection information for MongoDB is defined with an ATLAS_URI environment variable. You could easily use a configuration file or hard-code this value, but environment variables tend to work out the best for security and deployment reasons.

The connection string to be used would look something like this:

mongodb+srv://<username>:<password>@plummeting-us-east-1.hrrxc.mongodb.net/<dbname>

You can gather the information from your MongoDB Atlas dashboard after creating the appropriate user credentials for your database and collection.

When I add an environment variable, I generally do something like the following:

export ATLAS_URI="mongodb+srv://<username>:<password>@plummeting-us-east-1.hrrxc.mongodb.net/<dbname>"

There are numerous ways to do this and it may vary depending on your operating system.

So let’s focus on those endpoints.

When it comes to creating a user profile within MongoDB, we might have an endpoint that looks like the following:

server.post("/profile", async (request, response, next) => {
    try {
        let result = await collection.insertOne(request.body);
        response.send(result);
    } catch (e) {
        response.status(500).send({ message: e.message });
    }
});

The above endpoint is probably the most basic endpoint you can create. We are accepting a payload from the front end and we are inserting it into the database. Then we are returning the response that the database gives us.

In a production game, you’re probably going to want to validate your data to prevent cheating or other malicious activity. You can look into Schema Validation with MongoDB or use a client-facing package like joi instead.

With data going into MongoDB, now we can create our endpoint for retrieving it:

server.get("/profile/:username", async (request, response, next) => {
    try {
        let result = await collection.findOne({ "_id": request.params.username });
        response.send(result);
    } catch (e) {
        response.status(500).send({ message: e.message });
    }
});

After supplying this endpoint a username, which in our example is the _id field, we use it to find one particular document. That one document is returned to the game or whatever tried to retrieve it.

Again, we are not using any data validation or authentication for this tutorial as it is outside the scope of what we want to accomplish.

We now have an API to work with.

Building a Character Creation Game with Phaser and JavaScript

With the API in place, we can focus on the game itself. Remember, the depth of our game is quite shallow, but it offers a feature that a lot of games offer. That feature is customizing the character.

I’m not going to share my graphic assets because I want you to use some imagination for creating your own. Will you create something better? Probably, but it’s on you!

Create an index.html file on your computer with the following HTML markup:

<!DOCTYPE html>
<html>
    <body>
        <div id="game"></div>
        <script src="//cdn.jsdelivr.net/npm/phaser@3.51.0/dist/phaser.min.js"></script>
        <script>

            const phaserConfig = {
                type: Phaser.AUTO,
                parent: "game",
                width: 1280,
                height: 720,
                scene: {
                    init: initScene,
                    preload: preloadScene,
                    create: createScene
                }
            };

            const game = new Phaser.Game(phaserConfig);

            function initScene() {}
            function preloadScene() {}
            function createScene() {}

        </script>
    </body>
</html>

The above markup is standard Phaser boilerplate. We define the container where the game should be loaded, import the JavaScript dependency, and initialize the game while telling it to use the container.

The real magic is going to happen in the scene lifecycle functions. The initScene function is where we’ll initialize our variables, the preloadScene function is where we’ll load our media assets, and the createScene function is where we’ll do our first render.

Starting with the initScene function, add the following:

function initScene() {

    this.player = {
        "_id": "nraboy",
        "cosmetics": {
            "eyes": "",
            "mouth": ""
        },
        "hp": 100,
        "mp": 30
    }
    this.eyes = [];
    this.mouth = [];

}

For our example, the player object will represent our locally stored user profile object. It will look exactly like what you’d find stored in the database. This object could be as simple or as complex as you want to make it depending on your game needs. The _id field must be unique, so if you’d prefer to let MongoDB generate it, remove the field and create a username or similar field instead. It’s totally up to you.

The eyes and mouth arrays will represent all possible options for character customizations. The chosen customization will be stored in the user profile.

The next step is to look at the preloadScene function:

function preloadScene() {

    this.load.image("background", "game-background.png");
    this.load.image("player-base", "player-base.png");
    this.load.image("player-eyes-1", "player-eyes-1.png");
    this.load.image("player-eyes-2", "player-eyes-2.png");
    this.load.image("player-mouth-1", "player-mouth-1.png");
    this.load.image("player-mouth-2", "player-mouth-2.png");

}

In the above function, we are loading our image files and giving them a reference name to be used throughout the game. In the above example, the reference name is a pretty close name to the filename itself, but it doesn’t have to be.

I didn’t provide the image assets, so feel free to be adventurous.

The magic happens in the createScene function:

function createScene() {

    this.add.image(640, 360, "background");
    this.add.image(800, 250, "player-base");
    this.eyesActive = this.add.image(800, 250, "player-eyes-1");
    this.mouthActive = this.add.image(800, 250, "player-mouth-1");

    for (let i = 1; i <= 2; i++) {
        this.eyes.push(
            this.add.image(75, 90 * i, "player-eyes-" + i)
                .setCrop(150, 115, 100, 50)
                .setScale(0.5)
                .setInteractive()
                .on("pointerdown", () => {
                    this.eyesActive.setTexture("player-eyes-" + i);
                })
        );
        this.eyes[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 115, 100, 50);
    }
    
    for (let i = 1; i <= 2; i++) {
        this.mouth.push(
            this.add.image(215, 90 * i - 40, "player-mouth-" + i)
                .setCrop(150, 195, 100, 50)
                .setScale(0.5)
                .setInteractive()
                .on("pointerdown", () => {
                    this.mouthActive.setTexture("player-mouth-" + i);
                })
        );
        this.mouth[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 195, 100, 50);
    }

    this.add.text(289, 525, "PROFILE:", { fontSize: 36, color: "#FFFFFF", fontStyle: "bold" })
    this.profileData = this.add.text(289, 575, "", { fontSize: 16, color: "#FFFFFF" })

    this.startButton = this.add.text(1125, 640, "START", { fontSize: 40, color: '#000000', fontStyle: "bold", backgroundColor: "#FFFFFF", padding: 10 });
    this.startButton.setInteractive();
    this.startButton.on("pointerdown", () => {
        this.player.cosmetics.mouth = this.mouthActive.texture.key;
        this.player.cosmetics.eyes = this.eyesActive.texture.key;
        fetch("http://localhost:3000/profile", {
            "method": "POST",
            "headers": {
                "content-type": "application/json"
            },
            "body": JSON.stringify(this.player)
        })
        .then(response => response.json())
        .then(response => {
            this.profileData.setText(JSON.stringify(this.player));
        }, error => {
            console.error(error);
        });
    }, this);

}

Let me start by saying a few things first.

  • The game in my example is 1280x720 and the image assets reflect this.
  • The base image for the character and any cosmetic attachment image has the same resolution.

Rather than creating perfectly sized cosmetic attachment images, in my example, I just removed the base image but left the attachment images the same size but with transparency. Trying to figure out how to position small images could turn into more work than I wanted. Layering the images worked better.

With that said, we have the following:

this.add.image(640, 360, "background");
this.add.image(800, 250, "player-base");
this.eyesActive = this.add.image(800, 250, "player-eyes-1");
this.mouthActive = this.add.image(800, 250, "player-mouth-1");

The initialize images are placed on the screen with varying positions. The eyesActive and mouthActive represent the initial attachments for the character. We are only setting those to a variable because we plan to change them later.

Based on the naming convention of the images and the sizes we chose to use, we can display the attachment options for eyes on the screen:

for (let i = 1; i <= 2; i++) {
    this.eyes.push(
        this.add.image(75, 90 * i, "player-eyes-" + i)
            .setCrop(150, 115, 100, 50)
            .setScale(0.5)
            .setInteractive()
            .on("pointerdown", () => {
                this.eyesActive.setTexture("player-eyes-" + i);
            })
    );
    this.eyes[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 115, 100, 50);
}

In this example, we have two different possibilities for eyes. When pushing them into our array, we are specifying the position, but we are also changing a few things in regards to the image. For example, we know the images for attachments are mostly transparent. We can crop them and scale them to show only what we want. This is for selection purposes only, not displaying on the actual character.

We also want to respond to click events. When a click event happens, we want the appropriate image to be used as the active image. However, because we scaled and cropped our image, we need to redefine the hitArea, which is the area that should be clickable on the image.

Doing most of these steps is only a requirement if you are using images of the same size and planning to layer them like I am.

We can reproduce our steps for the mouth options:

for (let i = 1; i <= 2; i++) {
    this.mouth.push(
        this.add.image(215, 90 * i - 40, "player-mouth-" + i)
            .setCrop(150, 195, 100, 50)
            .setScale(0.5)
            .setInteractive()
            .on("pointerdown", () => {
                this.mouthActive.setTexture("player-mouth-" + i);
            })
    );
    this.mouth[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 195, 100, 50);
}

The steps are the same, but we are using different positions.

Remember, these two steps are for defining the thumbnails of our images and specifying a click region for them. While the active image is what we show on the character, the lead up is related to the thumbnails.

With the thumbnails out of the way, we can add some placeholder text of what profile we want to send:

this.add.text(289, 525, "PROFILE:", { fontSize: 36, color: "#FFFFFF", fontStyle: "bold" })
this.profileData = this.add.text(289, 575, "", { fontSize: 16, color: "#FFFFFF" })

The goal with this text is to only show what our player object looks like on the screen. This will give us a good idea of what was sent to our MongoDB database.

Finally, we have the button that will submit the data to our API:

this.startButton = this.add.text(1125, 640, "START", { fontSize: 40, color: '#000000', fontStyle: "bold", backgroundColor: "#FFFFFF", padding: 10 });
this.startButton.setInteractive();
this.startButton.on("pointerdown", () => {
    this.player.cosmetics.mouth = this.mouthActive.texture.key;
    this.player.cosmetics.eyes = this.eyesActive.texture.key;
    fetch("http://localhost:3000/profile", {
        "method": "POST",
        "headers": {
            "content-type": "application/json"
        },
        "body": JSON.stringify(this.player)
    })
    .then(response => response.json())
    .then(response => {
        this.profileData.setText(JSON.stringify(this.player));
    }, error => {
        console.error(error);
    });
}, this);

When the button is clicked, the image reference is added to the player object. Using a fetch operation, we can send the player object to our back end which will store it in MongoDB. Since we aren’t doing any data validation, this will succeed as long as the _id field is unique.

Phaser User Profiles in MongoDB

If you’re using MongoDB Atlas and you look at the data explorer, the documents should look something like the above image.

Conclusion

You just saw how to store user profiles for a Phaser game as NoSQL documents in MongoDB. Depending on the game you’re creating, the user profile documents could be significantly more complex in comparison to what was seen in this example. At the end of the day, you’re storing information about your game-play experience and that information could include information regarding the user such as password, billing information, or similar, and you’re also storing information about the game, which might include outfit customizations, item inventory, player location, or something else game-related.

If you are interested in seeing more Phaser examples with MongoDB, check out some of my previous tutorials on the subject. If you’re a Unity developer, I also have some content on that subject as well.

Got questions regarding this tutorial or MongoDB? Visit the MongoDB Community Forums and connect with us!

This content first appeared on MongoDB.

Original article source at: https://www.thepolyglotdeveloper.com/

#javascript #mongodb #phaser 

Game Development with Phaser, JavaScript, and MongoDB

Video games are great, but they’re a whole lot better when you can play them with friends, remotely.

So what goes into creating a multiplayer game, or any game for that matter?

In this session, we’re going to learn about Phaser, the HTML5 and JavaScript game development framework, and MongoDB for data storage and synchronization. More specifically, we’re going to see how simple it is to create a game where the data is transferred in near real-time between clients and database to give a multiplayer experience.

Speakers

Adrienne Tacke - Senior Developer Advocate at MongoDB

Nic Raboy - Senior Developer Advocate at MongoDB

About Adrienne Tacke

Currently a Senior Developer Advocate for MongoDB, Adrienne Tacke is also a Filipina software engineer, speaker, published author of the book Coding for Kids: Python, and a LinkedIn Learning instructor who specializes in Cloud Development courses. Perhaps most important, however, is that she spends way too much money on desserts and playing Borderlands 3.

About Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

#game-development #phaser #javascript #mongodb

Nat  Grady

Nat Grady

1670062320

How to Use Zapier with MongoDB

I’m a huge fan of automation when the scenario allows for it. Maybe you need to keep track of guest information when they RSVP to your event, or maybe you need to monitor and react to feeds of data. These are two of many possible scenarios where you probably wouldn’t want to do things manually.

There are quite a few tools that are designed to automate your life. Some of the popular tools include IFTTT, Zapier, and Automate. The idea behind these services is that given a trigger, you can do a series of events.

In this tutorial, we’re going to see how to collect Twitter data with Zapier, store it in MongoDB using a Realm webhook function, and then run aggregations on it using the MongoDB query language (MQL).

The Requirements

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

  • A paid tier of Zapier with access to premium automations
  • A properly configured MongoDB Atlas cluster
  • A Twitter account

There is a Zapier free tier, but because we plan to use webhooks, which are premium in Zapier, a paid account is necessary. To consume data from Twitter in Zapier, a Twitter account is necessary, even if we plan to consume data that isn’t related to our account. This data will be stored in MongoDB, so a cluster with properly configured IP access and user permissions is required.

You can get started with MongoDB Atlas by launching a free M0 cluster, no credit card required.

While not necessary to create a database and collection prior to use, we’ll be using a zapier database and a tweets collection throughout the scope of this tutorial.

Understanding the Twitter Data Model Within Zapier

Since the plan is to store tweets from Twitter within MongoDB and then create queries to make sense of it, we should probably get an understanding of the data prior to trying to work with it.

We’ll be using the “Search Mention” functionality within Zapier for Twitter. Essentially, it allows us to provide a Twitter query and trigger an automation when the data is found. More on that soon.

As a result, we’ll end up with the following raw data:

{
    "created_at": "Tue Feb 02 20:31:58 +0000 2021",
    "id": "1356701917603238000",
    "id_str": "1356701917603237888",
    "full_text": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript",
    "truncated": false,
    "display_text_range": [0, 188],
    "metadata": {
        "iso_language_code": "en",
        "result_type": "recent"
    },
    "source": "<a href='https://about.twitter.com/products/tweetdeck' rel='nofollow'>TweetDeck</a>",
    "in_reply_to_status_id": null,
    "in_reply_to_status_id_str": null,
    "in_reply_to_user_id": null,
    "in_reply_to_user_id_str": null,
    "in_reply_to_screen_name": null,
    "user": {
        "id": "227546834",
        "id_str": "227546834",
        "name": "Nic Raboy",
        "screen_name": "nraboy",
        "location": "Tracy, CA",
        "description": "Advocate of modern web and mobile development technologies. I write tutorials and speak at events to make app development easier to understand. I work @MongoDB.",
        "url": "https://t.co/mRqzaKrmvm",
        "entities": {
            "url": {
                "urls": [
                    {
                        "url": "https://t.co/mRqzaKrmvm",
                        "expanded_url": "https://www.thepolyglotdeveloper.com",
                        "display_url": "thepolyglotdeveloper.com",
                        "indices": [0, 23]
                    }
                ]
            },
            "description": {
                "urls": ""
            }
        },
        "protected": false,
        "followers_count": 4599,
        "friends_count": 551,
        "listed_count": 265,
        "created_at": "Fri Dec 17 03:33:03 +0000 2010",
        "favourites_count": 4550,
        "verified": false
    },
    "lang": "en",
    "url": "https://twitter.com/227546834/status/1356701917603237888",
    "text": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript"
}

The data we have access to is probably more than we need. However, it really depends on what you’re interested in. For this example, we’ll be storing the following within MongoDB:

{
    "created_at": "Tue Feb 02 20:31:58 +0000 2021",
    "user": {
        "screen_name": "nraboy",
        "location": "Tracy, CA",
        "followers_count": 4599,
        "friends_count": 551
    },
    "text": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript"
}

Without getting too far ahead of ourselves, our analysis will be based off the followers_count and the location of the user. We want to be able to make sense of where our users are and give priority to users that meet a certain followers threshold.

Developing a Webhook Function for Storing Tweet Information with MongoDB Realm and JavaScript

Before we start connecting Zapier and MongoDB, we need to develop the middleware that will be responsible for receiving tweet data from Zapier.

Remember, you’ll need to have a properly configured MongoDB Atlas cluster.

We need to create a Realm application. Within the MongoDB Atlas dashboard, click the Realm tab.

MongoDB Realm Applications

For simplicity, we’re going to want to create a new application. Click the Create a New App button and proceed to fill in the information about your application.

From the Realm Dashboard, click the 3rd Party Services tab.

Realm Dashboard 3rd Party Services

We’re going to want to create an HTTP service. The name doesn’t matter, but it might make sense to name it Twitter based on what we’re planning to do.

Because we plan to work with tweet data, it makes sense to call our webhook function tweet, but the name doesn’t truly matter.

Realm Tweet Webhook

With the exception of the HTTP Method, the defaults are fine for this webhook. We want the method to be POST because we plan to create data with this particular webhook function. Make note of the Webhook URL because it will be used when we connect Zapier.

The next step is to open the Function Editor so we can add some logic behind this function. Add the following JavaScript code:

exports = function (payload, response) {

    const tweet = EJSON.parse(payload.body.text());

    const collection = context.services.get("mongodb-atlas").db("zapier").collection("tweets");

    return collection.insertOne(tweet);

};

In the above code, we are taking the request payload, getting a handle to the tweets collection within the zapier database, and then doing an insert operation to store the data in the payload.

There are a few things to note in the above code:

  1. We are not validating the data being sent in the request payload. In a realistic scenario, you’d probably want some kind of validation logic in place to be sure about what you’re storing.
  2. We are not authenticating the user sending the data. In this example, we’re trusting that only Zapier knows about our URL.
  3. We aren’t doing any error handling.

When we call our function, a new document should be created within MongoDB.

By default, the function will not deploy when saving. After saving, make sure to review and deploy the changes through the notification at the top of the browser window.

Creating a “Zap” in Zapier to Connect Twitter to MongoDB

So, we know the data we’ll be working with and we have a MongoDB Realm webhook function that is ready for receiving data. Now, we need to bring everything together with Zapier.

For clarity, new Twitter matches will be our trigger in Zapier, and the webhook function will be our event.

Within Zapier, choose to create a new “Zap,” which is an automation. The trigger needs to be a Search Mention in Twitter, which means that when a new Tweet is detected using a search query, our events happen.

Zapier Twitter Search Mention

For this example, we’re going to use the following Twitter search query:

url:developer.mongodb.com -filter:retweets filter:safe lang:en -from:mongodb -from:realm

The above query says that we are looking for tweets that include a URL to developer.mongodb.com. The URL doesn’t need to match exactly as long as the domain matches. The query also says that we aren’t interested in retweets. We only want original tweets, they have to be in English, and they have to be detected as safe for work.

In addition to the mentioned search criteria, we are also excluding tweets that originate from one of the MongoDB accounts.

In theory, the above search query could be used to see what people are saying about the MongoDB Developer Hub.

With the trigger in place, we need to identify the next stage of the automation pipeline. The next stage is taking the data from the trigger and sending it to our Realm webhook function.

Zapier to Realm Webhook

As the event, make sure to choose Webhooks by Zapier and specify a POST request. From here, you’ll be prompted to enter your Realm webhook URL and the method, which should be POST. Realm is expecting the payload to be JSON, so it is important to select JSON within Zapier.

We have the option to choose which data from the previous automation stage to pass to our webhook. Select the fields you’re interested in and save your automation.

The data I chose to send looks like this:

{
    "created_at": "Tue Feb 02 20:31:58 +0000 2021",
    "username": "nraboy",
    "location": "Tracy, CA",
    "follower_count": "4599",
    "following_count": "551",
    "message": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript"
}

The fields do not match the original fields brought in by Twitter. It is because I chose to map them to what made sense for me.

When deploying the Zap, anytime a tweet is found that matches our query, it will be saved into our MongoDB cluster.

Analyzing the Twitter Data in MongoDB with an Aggregation Pipeline

With tweet data populating in MongoDB, it’s time to start querying it to make sense of it. In this fictional example, we want to know what people are saying about our Developer Hub and how popular these individuals are.

To do this, we’re going to want to make use of an aggregation pipeline within MongoDB.

Take the following, for example:

[
    {
        "$addFields": {
            "follower_count": {
                "$toInt": "$follower_count"
            },
            "following_count": {
                "$toInt": "$following_count"
            }
        }
    }, {
        "$match": {
            "follower_count": {
                "$gt": 1000
            }
        }
    }, {
        "$group": {
            "_id": {
                "location": "$location"
            },
            "location": {
                "$sum": 1
            }
        }
    }
]

There are three stages in the above aggregation pipeline.

We want to understand the follower data for the individual who made the tweet, but that data comes into MongoDB as a string rather than an integer. The first stage of the pipeline takes the follower_count and following_count fields and converts them from string to integer. In reality, we are using $addFields to create new fields, but because they have the same name as existing fields, the existing fields are replaced.

The next stage is where we want to identify people with more than 1,000 followers as a person of interest. While people with fewer followers might be saying great things, in this example, we don’t care.

After we’ve filtered out people by their follower count, we do a group based on their location. It might be valuable for us to know where in the world people are talking about MongoDB. We might want to know where our target audience exists.

The aggregation pipeline we chose to use can be executed with any of the MongoDB drivers, through the MongoDB Atlas dashboard, or through the CLI.

Conclusion

You just saw how to use Zapier with MongoDB to automate certain tasks and store the results as documents within the NoSQL database. In this example, we chose to store Twitter data that matched certain criteria, later to be analyzed with an aggregation pipeline. The automations and analysis options that you can do are quite limitless.

If you enjoyed this tutorial and want to get engaged with more content and like-minded developers, check out the MongoDB Community.

This content first appeared on MongoDB.

Original article source at: https://www.thepolyglotdeveloper.com/

#mongodb #zapier 

JavaScript Snake Game Tutorial - Develop a Simple Snake Game

Work on real-time JavaScript Snake game project and become a pro

Snake game is an interesting JavaScript project for beginners. Snake game is a single-player game, which we’ve been playing for a very long time. The game mainly consists of two components – snake and fruit. And we just need to take our snake to the food so that it can eat and grow faster and as the number of fruits eaten increases, the length of snake increases which makes the game more interesting. While moving if the snake eats its own body, then the snake dies and the game ends. Now let’s see how we can create this.

JavaScript Project Prerequisites

To implement the snake game in JavaScript you should have basic knowledge of:

1. Basic concepts of JavaScript

2. HTML

3. CSS

Download Project Code

Before proceeding ahead please download source code of Snake Game: Snake Game in JavaScript

Steps to Build the Project – JavaScript Snake game

1. Create Html file

HTML builds the basic structure. This file contains some basic HTML tags like div, h1, title, etc. also we’ve used bootstrap (CDN is already included).

index.html:

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DataFlair Snake game</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <link rel="stylesheet" href="static/style.css">
</head>
<body>
    <div class="container">
        <div class ="Jumbotron">
        <h1>DataFlair Snake game using vanilla JavaScript</h1>

            <h2 class="btn btn-info"> 
                Score:   0 
            </h2>

            <div class="containerCanvas">
                <canvas id="canvas" width="500" height="500" class="canvasmain"> </canvas>
            </div>
        </div>
    </div>
    <script src="static/fruit.js"></script>
    <script src="static/snake.js"></script>
    <script src="static/draw.js"></script>
</body>
</html>

We have used simple HTML tags except tag. This new tag was introduced in HTML5, which is used to draw graphics, via JavaScript. It is nothing without JavaScript so we will use it in our JavaScript file to actually draw the board and the objects required. We’ll see its implementation in JS files.

#javascript tutorial #javascript project #javascript snake game #simple snake game #javascript

lakshya world

lakshya world

1664775764

How to Create a Successful Gaming App?

How to create a game app is a comprehensive guide, explaining the entire process of creating and publishing games for iOS and Android. Covering all the essential information a budding game developer needs to know.

 

Read More - https://www.brsoftech.com/blog/how-to-create-a-game-app/

 

#game-engine  #game-development  #game  #games  #gaming