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 .

Learn why MongoDB was selected as a leader in the 2024 Gartner® Magic Quadrant™
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
JavaScriptchevron-right

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

Nic Raboy11 min read • Published Jan 18, 2022 • Updated Apr 02, 2024
AtlasJavaScript
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Quando se trata de desenvolvimento de jogos, quase sempre você precisará armazenar informações sobre seu jogador. Essas informações podem ser sobre quantos pontos de saúde você tem atualmente no jogo ou podem ir além da experiência de jogo e abranger detalhes como as informações de cobrança da pessoa que está jogando. Quando falamos desse tipo de dados, estamos falando de um armazenamento de perfis de usuário.
O perfil do usuário contém tudo sobre o usuário ou jogador e não termina em pontos de saúde ou informações de cobrança.
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 na seguinte imagem animada:
Armazenamento de perfil de usuário faser com MongoDB
Tudo bem, então não estamos fazendo um jogo muito emocionante. No entanto, muitos jogos modernos permitem que você crie seu personagem antes do início do jogo ou em algum momento à medida que avança no jogo. Essas personalizações que você adiciona ao seu personagem são armazenadas no perfil do usuário. Em nosso exemplo, personalizaremos nosso player e enviaremos as informações para nosso back-end para armazenamento no MongoDB.

Os requisitos

Existem alguns componentes neste jogo, mas usaremos uma pilha JavaScript por toda parte. Para que este tutorial cumpra sua finalidade, você precisará do seguinte:
  • Um cluster MongoDB configurado corretamente com um banco de dados jogos e uma coleçãode perfis
Usaremos o Node.js para criar uma API de back-end que se comunicará com nosso cluster MongoDB. O jogo, que será nosso frontend, se comunicará com nosso backend do Node.js. Não entraremos em detalhes sobre como configurar e implantar uma instância MongoDB, mas você precisará de uma com banco de dados e coleção 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 em Phaser, você não precisa de nada além de HTML, CSS e JavaScript básicos, portanto, não há nenhum requisito prévio para o componente do jogo.

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 o nosso perfil de usuário deve conter. Essas não são regras gerais para todos os perfis de usuário porque, muitas vezes, os jogos não são iguais.
Com base na imagem animada do 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 que esse jogador em particular decidiu dar a seu personagem uma certa aparência e que esse personagem tem um conjunto específico 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, posição do jogador no jogo, itens armazenados ou qualquer outro campo relevante ao 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 uma atualização em nosso jogo que inclua personalizações na cor do diamante, poderemos facilmente adicionar esse campo aos nossos documentos NoSQL. Adicionar e remover campos não requer migrações ou codificação especiais, o que se traduz em desenvolvimento mais rápido e 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 este jogo em particular. Agora precisamos ser capazes de adicioná-lo ao MongoDB e consultá-lo. Para fazer isso, criaremos um back-end com dois endpoints de API simples: um endpoint para criar um documento com o perfil e um endpoint para recuperá-lo.
Para este tutorial em particular, não vamos nos preocupar com autenticação. Em outras palavras, estaremos apenas criando perfis de usuário e não nos importando 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 arquivopackage.json e baixarão as dependências que planejamos usar. Estamos usando o pacotecors 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 pacotesExpress e body-parser serão para nossa estrutura de desenvolvimento, e o pacoteMongoDB será para interação com o MongoDB.
Dentro do mesmo projeto, crie um arquivo main.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 usando 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:
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 a inserindo 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 implementada, podemos nos concentrar no jogo em si. Lembre-se de que a profundidade do nosso jogo é bem rasa, mas ele oferece um recurso que 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 é 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 para ser usado ao longo do jogo. No exemplo acima, o nome de referência é um nome bem próximo ao nome do arquivo em si, 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 ativos de 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, no meu exemplo, apenas removi a imagem base, mas deixei as imagens de anexos do mesmo tamanho, mas com transparência. Tentar descobrir como posicionar imagens pequenas pode se tornar mais trabalhoso do que eu queria. A disposição das imagens funcionou 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. Ao inseri-los em nossa matriz, estamos especificando a posição, mas também estamos 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 é apenas para fins de seleção, não sendo exibido 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 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 especificar uma região de clique para elas. Enquanto a imagem ativa é o que mostramos no personagem, o lead up está relacionado às miniaturas.
Com as miniaturas fora do caminho, podemos adicionar um texto de espaço reservado sobre o perfil que 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.
Perfis de usuário do Faser no 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 Typer como documentos NoSQL no MongoDB. Dependendo do jogo que você está criando, os documentos de perfil do usuário podem ser significativamente mais complexos em comparação com o que foi visto neste exemplo. No final das contas, você está armazenando informações sobre a sua experiência de jogo e essas informações podem incluir informações sobre o usuário, como senha, informações de cobrança ou similares, e você também está armazenando informações sobre o jogo, que podem incluir personalizações de roupas, inventário de itens, localização do jogador ou qualquer outra coisa relacionada ao jogo.
Se você estiver interessado em ver mais exemplos de Phaser com MongoDB, confira alguns de meus tutoriais anteriores sobre o assunto. Se você é um desenvolvedor Unity, também tenho algum conteúdo sobre esse assunto.
Tem dúvidas sobre este tutorial ou sobre o MongoDB? Visite os Fóruns da MongoDB Community e conecte-se conosco!

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Início rápido

Aggregation Framework com Node.js 3.3.2 Tutorial


Oct 01, 2024 | 9 min read
Tutorial

Criando gráficos de AI com Rivet e MongoDB Atlas Vector Search para potencializar aplicativos de AI


Sep 18, 2024 | 10 min read
Tutorial

Usar a OpenAI e o MongoDB Atlas para melhorar a funcionalidade de pesquisa


Sep 18, 2024 | 5 min read
Tutorial

Trabalhando com MongoDB Charts e o novo SDK para JavaScript


Apr 02, 2024 | 10 min read
Sumário