A Voyage AI se une ao MongoDB para impulsionar aplicativos de AI mais precisos e confiáveis no Atlas.

Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Gerencie perfis de usuário do jogo com MongoDB, Changer e JavaScript

Nic Raboy11 min read • Published Jan 18, 2022 • Updated Apr 02, 2024
Facebook Icontwitter iconlinkedin icon
Avaliar este tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Quando se trata de desenvolvimento de jogos, você quase sempre precisará armazene informações sobre seu player. Essas informações podem estar por aí quantos pontos de saúde você tem atualmente no jogo ou eles podem se estender além da experiência de jogo e em detalhes como o faturamento informações para a pessoa que está jogando o jogo. Quando falamos sobre isso tipo de dados, estamos falando de uma loja de perfis de usuário.
O perfil do usuário contém tudo sobre o usuário ou jogador e não termina nos pontos de saúde ou nas informações de faturamento.
Neste tutorial, veremos como criar perfis de usuário em um jogo que utiliza a estrutura de desenvolvimento de jogosPhaser , JavaScript e MongoDB.
Para ter uma ideia melhor do que vamos realizar, dê uma olhada em a seguinte imagem animada:
Phaser User Profile Store with MongoDB
Tudo bem, então não estamos fazendo um jogo muito emocionante. No entanto, muitos modernos Os jogos permitem que você crie seu personagem antes do início do jogo ou em algum ponto à medida que você avança no jogo. Essas personalizações que que você adiciona ao seu personagem são armazenados no perfil do usuário. Em nosso por exemplo, vamos personalizar nosso player e depois enviar o informações ao nosso back-end para armazenamento no MongoDB.

Os requisitos

Existem alguns componentes neste jogo, mas usaremos uma pilha JavaScript por toda parte. Para ter sucesso com este tutorial, você precisará do seguinte:
  • Um cluster MongoDB configurado corretamente com um banco de banco dedados jogos e uma coleção de perfis
Usaremos o Node.js para criar uma API de back-end que se comunicará com nosso cluster MongoDB. O jogo, que será nosso front-end, comunicar-se com nosso back-end Node.js. Não entraremos em detalhes sobre como configurar e implantar uma MongoDB instance, mas você precisará de uma com banco de dados e collection disponíveis para uso.
Se você quiser entrar em operação rapidamente com o MongoDB, considere implantar um clusterdo MongoDB Atlas M0 , que é GRÁTIS para sempre!
Se precisar de ajuda para configurar o MongoDB, confira este tutorial anterior sobre o assunto.
Para criar um jogo Phaser, você não precisa de nada além de HTML básico, CSS, e JavaScript, então não há requisitos prévios para o jogo componente.

Definindo um modelo de dados para um armazenamento de perfil de usuário em um jogo Typer

Antes de entrarmos no código, provavelmente é uma boa ideia explorar o que nosso perfil de usuário deve conter. Essas não são regras gerais para todos os usuários porque muitas vezes, os jogos não são os mesmos.
Com base na imagem criada no início deste tutorial, sabemos que temos o seguinte JSON:
1{
2 "_id": "nraboy",
3 "cosmetics": {
4 "eyes": "player-eyes-1",
5 "mouth": "player-mouth-1"
6 },
7 "hp": 100,
8 "mp": 30
9}
A profundidade do perfil acima é quase nada, mas sabemos disso jogador em particular que eles decidiram dar ao seu personagem um certo aparência e esse personagem tem um conjunto particular de atributos, como pontos de saúde.
Se você quiser ver um perfil de usuário mais agressivo, confira meu tutorial anterior, que é focado em um jogo Unity que estou desenvolvendo com Adriene Tacke.
Você pode considerar adicionar uma senha com hash, a posição do jogador dentro do jogo, itens armazenados ou qualquer outro campo relevante para seu perfil de usuário.
O que torna o MongoDB particularmente poderoso quando se trata de jogos e perfis de usuário é a flexibilidade do modelo de dados pode ser. Se lançarmos um atualização do nosso jogo que inclui personalizações na cor do diamante, poderíamos facilmente adicionar esse campo aos nossos documentos NoSQL. Adicionar e remoção de campos não requer migrações ou codificação especiais, o que se traduz em um desenvolvimento mais rápido e em um código melhor.

Desenvolvendo a API de back-end do armazenamento de perfil de usuário com Express Framework e MongoDB

Temos uma ideia aproximada de como deve ser nosso perfil de usuário para isso jogo particular. Agora precisamos ser capazes de adicioná-lo ao MongoDB e consultá-lo. Para fazer isso, criaremos um back-end com duas API simples endpoints: um endpoint para criar um documento com o perfil e um ponto final para recuperá-lo.
Para este tutorial específico, não vamos nos preocupar com autenticação. Em outras palavras, vamos criar apenas um usuário perfis e não se importa a quem eles realmente pertencem.
Em um novo diretório em seu computador, execute os seguintes comandos:
1npm init -y
2npm install cors express body-parser mongodb --save
Os comandos acima criarão um novo pacote.json do pacote e baixarão as dependências que planejamos usar. Estamos usando o pacote cors porque planejamos executar nosso jogo localmente em um navegador da web. Se não gerenciarmos o compartilhamento de recursos entre origens (CORS), receberemos erros. Os pacotes Express e body-parser serão para nossa estrutura de desenvolvimento, e o pacote mongodb será para interação com o MongoDB.
Dentro do mesmo projeto, crie um arquivoprincipal.js com o seguinte código:
1const { MongoClient, ObjectID } = require("mongodb");
2const Express = require("express");
3const BodyParser = require("body-parser");
4const Cors = require("cors");
5
6const server = Express();
7
8server.use(BodyParser.json());
9server.use(BodyParser.urlencoded({ extended: true }));
10server.use(Cors());
11
12const client = new MongoClient(process.env["ATLAS_URI"]);
13
14var collection;
15
16server.post("/profile", async (request, response, next) => {});
17server.get("/profile/:username", async (request, response, next) => {});
18
19server.listen("3000", async () => {
20 try {
21 await client.connect();
22 collection = client.db("gamedev").collection("profiles");
23 console.log("Listening at :3000...");
24 } catch (e) {
25 console.error(e);
26 }
27});
O núcleo do que está acontecer no código acima é que estamos lançando a base para nosso aplicativo Express Framework importando as dependências e inicializando a estrutura. Quando começam a escutar conexões, nós nos conectamos ao MongoDB e definimos qual banco de dados e collection queremos usar. Neste exemplo, estamos utilizando um banco de dadosgamedev e uma collectionprofiles. Você pode usar o que fizer sentido para você.
As informações reais de conexão do MongoDB são definidas com uma variável de ambienteATLAS_URI. Você poderia facilmente usar um arquivo de configuração ou codificar esse valor, mas as variáveis de ambiente tendem a funcionar melhor por motivos de segurança e implantação.
A string de conexão a ser usada seria mais ou menos assim:
1mongodb+srv://<username>:<password>@plummeting-us-east-1.hrrxc.mongodb.net/<dbname>
Você pode coletar as informações do painel do MongoDB Atlas depois de criar as credenciais de usuário apropriadas para seu banco de dados e coleção.
Quando adiciono uma variável de ambiente, geralmente faço algo como o seguinte seguinte:
1export ATLAS_URI="mongodb+srv://<username>:<password>@plummeting-us-east-1.hrrxc.mongodb.net/<dbname>"
Existem várias maneiras de fazer isso e ela pode variar dependendo do seu sistema operacional.
Então, vamos nos concentrar nesses endpoints.
Quando se trata de criar um perfil de usuário no MongoDB, podemos ter um endpoint parecido com o seguinte:
1server.post("/profile", async (request, response, next) => {
2 try {
3 let result = await collection.insertOne(request.body);
4 response.send(result);
5 } catch (e) {
6 response.status(500).send({ message: e.message });
7 }
8});
O endpoint acima é provavelmente o endpoint mais básico que você pode criar. Estamos aceitando uma carga útil do front-end e inserindo-a no banco de dados. Em seguida, estamos retornando a resposta que o banco de dados nos dá.
Em um jogo de produção, você provavelmente vai querer validar seus dados para evitar cola ou outras atividades maliciosas. Você pode pesquisar a validação de esquema com o MongoDB ou usar um pacote voltado para o cliente como o joi.
Com os dados entrarem no MongoDB, agora podemos criar nosso endpoint para recuperá-los:
1server.get("/profile/:username", async (request, response, next) => {
2 try {
3 let result = await collection.findOne({ "_id": request.params.username });
4 response.send(result);
5 } catch (e) {
6 response.status(500).send({ message: e.message });
7 }
8});
Depois de fornecer a esse endpoint um nome de usuário, que em nosso exemplo é o campo_id, o usamos para encontrar um documento específico. Esse documento é devolvido ao jogo ou ao que quer que tenha tentado recuperá-lo.
Novamente, não estamos usando nenhuma validação ou autenticação de dados para este tutorial, pois isso está fora do escopo do que queremos realizar.
Agora temos uma API para trabalhar.

Construindo um jogo de criação de personagens com Phaser e JavaScript

Com a API em vigor, podemos nos concentrar no jogo em si. Lembre-se de que a A profundidade do nosso jogo é bem pequena, mas ele oferece um recurso que muitos muitos jogos oferecem. Esse recurso é personalizar o personagem.
Não vou compartilhar meus recursos gráficos porque quero que você use um pouco de imaginação para criar os seus próprios. Você vai criar algo melhor? Talvez, mas é por sua conta!
Crie um arquivoindex.html no seu computador com a seguinte marcação HTML:
1<!DOCTYPE html>
2<html>
3 <body>
4 <div id="game"></div>
5 <script src="//cdn.jsdelivr.net/npm/phaser@3.51.0/dist/phaser.min.js"></script>
6 <script>
7
8 const phaserConfig = {
9 type: Phaser.AUTO,
10 parent: "game",
11 width: 1280,
12 height: 720,
13 scene: {
14 init: initScene,
15 preload: preloadScene,
16 create: createScene
17 }
18 };
19
20 const game = new Phaser.Game(phaserConfig);
21
22 function initScene() {}
23 function preloadScene() {}
24 function createScene() {}
25
26 </script>
27 </body>
28</html>
A marcação acima é um boilerplate padrão do Phaser. Definimos o container onde o jogo deve ser carregado, importamos a dependência JavaScript e inicializamos o jogo enquanto dissemos para ele usar o container.
A verdadeira mágica vai acontecer nas funções do ciclo de vidascene. A funçãoinitScene é onde inicializaremos nossas variáveis, a funçãopreloadSceneé onde carregaremos nossos ativos de mídia e a funçãocreateSceneé onde faremos nossa primeira renderização.
Começando com a initScene função, adicione o seguinte:
1function initScene() {
2
3 this.player = {
4 "_id": "nraboy",
5 "cosmetics": {
6 "eyes": "",
7 "mouth": ""
8 },
9 "hp": 100,
10 "mp": 30
11 }
12 this.eyes = [];
13 this.mouth = [];
14
15}
Para nosso exemplo, o objetoplayer representará nosso objeto de perfil de usuário armazenado localmente. Ele será exatamente igual ao que você encontraria armazenado no banco de dados. Esse objeto pode ser tão simples ou complexo quanto você quiser, dependendo das necessidades do jogo. O campo_id deve ser exclusivo, portanto, se você preferir deixar o MongoDB gerá-lo, remova o campo e crie um username ou um campo semelhante. Só depende de você.
As arrayseyes e mouthrepresentarão todas as opções possíveis para customização de caracteres. A personalização escolhida será armazenada no perfil do usuário.
O próximo passo é examinar a função preloadScene:
1function preloadScene() {
2
3 this.load.image("background", "game-background.png");
4 this.load.image("player-base", "player-base.png");
5 this.load.image("player-eyes-1", "player-eyes-1.png");
6 this.load.image("player-eyes-2", "player-eyes-2.png");
7 this.load.image("player-mouth-1", "player-mouth-1.png");
8 this.load.image("player-mouth-2", "player-mouth-2.png");
9
10}
Na função acima, estamos carregando nossos arquivos de imagem e dando a eles um nome de referência a ser usado durante todo o jogo. No exemplo acima, o nome de referência é um nome muito próximo ao próprio nome do arquivo, mas não precisa ser.
Não forneci os ativos da imagem, então fique à vontade para ser ousado.
A mágica acontece na funçãocreateScene:
1function createScene() {
2
3 this.add.image(640, 360, "background");
4 this.add.image(800, 250, "player-base");
5 this.eyesActive = this.add.image(800, 250, "player-eyes-1");
6 this.mouthActive = this.add.image(800, 250, "player-mouth-1");
7
8 for (let i = 1; i <= 2; i++) {
9 this.eyes.push(
10 this.add.image(75, 90 * i, "player-eyes-" + i)
11 .setCrop(150, 115, 100, 50)
12 .setScale(0.5)
13 .setInteractive()
14 .on("pointerdown", () => {
15 this.eyesActive.setTexture("player-eyes-" + i);
16 })
17 );
18 this.eyes[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 115, 100, 50);
19 }
20
21 for (let i = 1; i <= 2; i++) {
22 this.mouth.push(
23 this.add.image(215, 90 * i - 40, "player-mouth-" + i)
24 .setCrop(150, 195, 100, 50)
25 .setScale(0.5)
26 .setInteractive()
27 .on("pointerdown", () => {
28 this.mouthActive.setTexture("player-mouth-" + i);
29 })
30 );
31 this.mouth[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 195, 100, 50);
32 }
33
34 this.add.text(289, 525, "PROFILE:", { fontSize: 36, color: "#FFFFFF", fontStyle: "bold" })
35 this.profileData = this.add.text(289, 575, "", { fontSize: 16, color: "#FFFFFF" })
36
37 this.startButton = this.add.text(1125, 640, "START", { fontSize: 40, color: '#000000', fontStyle: "bold", backgroundColor: "#FFFFFF", padding: 10 });
38 this.startButton.setInteractive();
39 this.startButton.on("pointerdown", () => {
40 this.player.cosmetics.mouth = this.mouthActive.texture.key;
41 this.player.cosmetics.eyes = this.eyesActive.texture.key;
42 fetch("http://localhost:3000/profile", {
43 "method": "POST",
44 "headers": {
45 "content-type": "application/json"
46 },
47 "body": JSON.stringify(this.player)
48 })
49 .then(response => response.json())
50 .then(response => {
51 this.profileData.setText(JSON.stringify(this.player));
52 }, error => {
53 console.error(error);
54 });
55 }, this);
56
57}
Permitam-me começar por dizer algumas coisas primeiro.
  • O jogo no meu exemplo é 1280x720 e os recursos da imagem refletem isso.
  • A imagem base do personagem e qualquer imagem de anexo cosmético têm a mesma resolução.
Em vez de criar imagens de anexos cosméticos de tamanho perfeito, na minha exemplo, acabei de remover a imagem base, mas deixei as imagens de anexo do mesmo tamanho, mas com transparência. Tentando descobrir como posicionar imagens pequenas podem se transformar em mais trabalho do que eu queria. Camadas as imagens funcionaram melhor.
Com isso dito, temos o seguinte:
1this.add.image(640, 360, "background");
2this.add.image(800, 250, "player-base");
3this.eyesActive = this.add.image(800, 250, "player-eyes-1");
4this.mouthActive = this.add.image(800, 250, "player-mouth-1");
As imagens de inicialização são colocadas na tela em posições variáveis. O eyesActive e mouthActive representam os anexos iniciais do caractere. Estamos definindo isso apenas como uma variável porque planejamos alterá-las mais tarde.
Com base na convenção de nomenclatura das imagens e nos tamanhos que escolhemos usar, podemos exibir as opções de anexo para eyes na tela:
1for (let i = 1; i <= 2; i++) {
2 this.eyes.push(
3 this.add.image(75, 90 * i, "player-eyes-" + i)
4 .setCrop(150, 115, 100, 50)
5 .setScale(0.5)
6 .setInteractive()
7 .on("pointerdown", () => {
8 this.eyesActive.setTexture("player-eyes-" + i);
9 })
10 );
11 this.eyes[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 115, 100, 50);
12}
Neste exemplo, temos duas possibilidades diferentes para os olhos. Quando empurrando-os para nosso array, estamos especificando a posição, mas estamos também mudando algumas coisas em relação à imagem. Por exemplo, sabemos que as imagens dos anexos são em sua maioria transparentes. Podemos cortá-las e escalá-las para mostrar apenas o que queremos. Isso é para fins de seleção somente, não aparecendo no personagem real.
Também queremos responder a eventos de clique. Quando um evento de clique acontece, queremos que a imagem apropriada seja usada como a imagem ativa. No entanto, como dimensionamos e recortamos nossa imagem, precisamos redefinir o hitArea, que é a área que deve ser clicável na imagem.
A execução da maioria dessas etapas só é necessária se você estiver usando imagens do mesmo tamanho e estiver planejando colocá-las em camadas, como eu.
Podemos reproduzir nossas etapas para as opções de boca:
1for (let i = 1; i <= 2; i++) {
2 this.mouth.push(
3 this.add.image(215, 90 * i - 40, "player-mouth-" + i)
4 .setCrop(150, 195, 100, 50)
5 .setScale(0.5)
6 .setInteractive()
7 .on("pointerdown", () => {
8 this.mouthActive.setTexture("player-mouth-" + i);
9 })
10 );
11 this.mouth[i - 1].input.hitArea = new Phaser.Geom.Rectangle(150, 195, 100, 50);
12}
As etapas são as mesmas, mas estamos usando posições diferentes.
Lembre-se de que essas duas etapas são para definir as miniaturas de nossas imagens e especificando uma região de clique para eles. Enquanto a imagem ativa é o que mostramos no personagem, a liderança está relacionada às miniaturas.
Com as miniaturas fora do caminho, podemos adicionar algum texto de espaço reservado de Qual perfil queremos enviar:
1this.add.text(289, 525, "PROFILE:", { fontSize: 36, color: "#FFFFFF", fontStyle: "bold" })
2this.profileData = this.add.text(289, 575, "", { fontSize: 16, color: "#FFFFFF" })
O objetivo com este texto é apenas mostrar como é o nosso objetoplayer na tela. Isso nos dará uma boa ideia do que foi enviado para nosso MongoDB database.
Por fim, temos o botão que enviará os dados para nossa API:
1this.startButton = this.add.text(1125, 640, "START", { fontSize: 40, color: '#000000', fontStyle: "bold", backgroundColor: "#FFFFFF", padding: 10 });
2this.startButton.setInteractive();
3this.startButton.on("pointerdown", () => {
4 this.player.cosmetics.mouth = this.mouthActive.texture.key;
5 this.player.cosmetics.eyes = this.eyesActive.texture.key;
6 fetch("http://localhost:3000/profile", {
7 "method": "POST",
8 "headers": {
9 "content-type": "application/json"
10 },
11 "body": JSON.stringify(this.player)
12 })
13 .then(response => response.json())
14 .then(response => {
15 this.profileData.setText(JSON.stringify(this.player));
16 }, error => {
17 console.error(error);
18 });
19}, this);
Quando o botão é clicado, a referência da imagem é adicionada ao objeto player . Usando uma operaçãofetch, podemos enviar o objetoplayer para nosso backend, que o armazenará no MongoDB. Como não estamos fazendo nenhuma validação de dados, isso será bem-sucedido desde que o campo_id seja exclusivo.
Phaser User Profiles in MongoDB
Se você estiver usando o MongoDB Atlas e examinar o gerenciador de dados, os documentos devem ser semelhantes à imagem acima.

Conclusão

Você acabou de ver como armazenar perfis de usuário para um jogo Phaser como NoSQL documentos no MongoDB. Dependendo do jogo que você está criando, o usuário documentos de perfil podem ser significativamente mais complexos em comparação com o que foi visto neste exemplo. No final do dia, você está armazenando informações sobre sua experiência de jogo e essas informações podem incluir informações sobre o usuário, como senha, cobrança informações, ou similares, e você também está armazenando informações sobre o jogo, que pode incluir personalizações de roupas, inventário de itens, jogadores localização, ou qualquer outra coisa relacionada ao jogo.
Se você estiver interessado em ver mais exemplos do Stager com o MongoDB, confira alguns dos meus tutoriais anteriores sobre o assunto. Se você é um desenvolvedor Unity, também tenho algum conteúdo sobre esse assunto.
Tem perguntas sobre este tutorial ou MongoDB? Visite os Fóruns daMongoDB Community e conecte-se conosco!

Facebook Icontwitter iconlinkedin icon
Avaliar este tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Como migrar seu aplicativo Node.js do SQL para o MongoDB


Jul 09, 2024 | 16 min read
Tutorial

Consulta flexível com Atlas Search


Jul 12, 2024 | 3 min read
Tutorial

Use o MongoDB como o armazenamento de dados para seu CMS Strapi Headless


Sep 23, 2022 | 8 min read
Tutorial

Pesquisando sua localização com Atlas Search e operadores geoespaciais


Mar 21, 2025 | 9 min read