Desenvolvimento & Produção

Como criar um leaderboard em C# e estimular a competitividade entre os jogadores do seu game

Como criar um leaderboard em C# e estimular a competitividade entre os jogadores do seu game

Coloque seu nome e email para receber meus melhores conteúdos:

Uma forma de tornar seu game mais divertido é criar um ranking. E neste artigo eu vou te ensinar como desenvolver um leaderboard para seu game um usando C#.

Existem diversas ferramentas e provedores que podem ser utilizados para isso, pois você precisará manter a pontuação de seus usuários armazenadas em um servidor na nuvem.

Eu gostava muito do Parse, mas talvez você já saiba que ele será desativado em janeiro de 2017 e obviamente é perigoso permanecer em um serviço que está com data de morte anunciada.

Fiz uma pesquisa e encontrei diversos possíveis substitutos. Uma opção que me agradou é o Playfab, um provedor BAAS (Mobile backend as a service) focado em games, o que é ótimo, pois muitas funcionalidades que são usadas nos games já estão prontas.

Em nosso último jogo publicado, o Sweet Lab, estávamos usando o Parse, e agora convertemos os dados para o Playfab.

sweet-lab-playfab

A seguir vou mostrar para você o processo de logon simplificado no PlayFab e como criar um Leaderboard como o nosso:

sweet-lab-leaderboard

(Uma pequena curiosidade: após jogar a primeira partida do Sweet Lab, você vai notar um tal de Vincent, que está sempre rodeando os primeiros lugares e que, curiosamente, é da Bielorrussia. Quando publicamos o game na Play Store ele foi o primeiro a fazer o download. ☺)

Conheça a Playfab

Antes de mais nada, entrem no site da Playfab e façam o cadastro. É gratuito para projetos pequenos.

playfab-site

Depois, faça o download e importem o package PlayFabClientSDK.unitypackage no projeto. É possível baixá-lo clicando aqui.

Criando logon anônimo: exemplo completo em C#

Existem diversos tipos de login: Google, Facebook, conta própria, iOS, etc. Como o Sweet Lab é um game simples, optamos por ocultar esse processo do jogador, ou seja, ele não será obrigado a criar usuário e senha pois faremos isso internamente no game. Futuramente, iremos acrescentar outras opções para logon, mas uma não inviabiliza a outra.

Adicione as referências abaixo em sua classe:

using System;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using PlayFab;
using PlayFab.ClientModels;
using System.Collections.Generic;

Para registrar um novo Player, você pode adaptar a função abaixo:

public static void Register()
    {
        var request = new RegisterPlayFabUserRequest()
        {
            TitleId = PlayFabSettings.TitleId,
            Username = GameController.ChaveUnica,
            Password = Encrypt(GameController.ChaveUnica),
            Email = GameController.ChaveUnica + "@spoxgamestudio.com"
        };
        PlayFabClientAPI.RegisterPlayFabUser(request, (result) =>
        {
            LoginRegisterSuccess(result.PlayFabId);
        }, (error) =>
        {
            Debug.Log("Register" + error.ErrorMessage);
        });
    }

TitleId: é um código que a Playfab irá lhe fornecer.

Username: usamos uma chave única, criada internamente em nosso game.

Password: Encriptamos a chave única com um algoritmo interno. O usuário não terá acesso a senha.

Email: Como e-mail é um campo obrigatório criamos um e-mail baseado no username.

A função RegisterPlayFabUser passa os parâmetros para PlayFab, que faz um callback indicando sucesso ou falha no registro do novo usuário.

O processo de login é feito de forma semelhante:

public static void Login()
    {
        var loginRequest = new LoginWithPlayFabRequest()
        {
            TitleId = GameController.PlayFabID,
            Username = GameController.ChaveUnica,
            Password = Encrypt( GameController.ChaveUnica)
        };

        PlayFabClientAPI.LoginWithPlayFab(loginRequest, (result) =>
        {
            LoginRegisterSuccess(result.PlayFabId);

        }, (error) =>
        {
             
        });
    }

A função LoginRegisterSuccess é chamada no callback. Em nossa classe GameController criamos uma variável booleana chamada IsLogged, pois para criar o Leaderboard é necessário que o jogador esteja logado.

Como o processo é assíncrono fazemos a chamada do leaderboard numa função Update()  que será descrita posteriormente.

O código em C# fica:

private static void LoginRegisterSuccess(string playFabId)
    {
        GameController.IsLogged = true;
    }

Os usuários podem ser consultados no site do Playfab, juntamente com diversas outras estatísticas:

playfab-players

Atualizando pontuação

Quando o jogador conclui cada uma das fases, ele marca pontos. Além de mantermos os dados locais no device, também enviamos os dados de pontuação para o Playfab:

public static void UpdateScore(int score)
    {
        Dictionary<string, int> stats = new Dictionary<string, int>();
        stats.Add("score", score);
        UpdateUserStatistics(stats);
    }

    
    public static void UpdateUserStatistics(Dictionary<string, int> stats)
    {
        PlayFab.ClientModels.UpdateUserStatisticsRequest request = new PlayFab.ClientModels.UpdateUserStatisticsRequest();
        request.UserStatistics = stats;
        PlayFabClientAPI.UpdateUserStatistics(request, StatsUpdated, OnPlayFabError);
    }

    private static void StatsUpdated(PlayFab.ClientModels.UpdateUserStatisticsResult result)
    {
        GameController.AllDataSet = true;
    }

O Playfab possui uma classe chamada UserStatistics, que deve ser utilizada para armazenar os achievements do jogador: pontos, fases completas, vitória sobre boss, ou qualquer outro item que você deseja para criar rankings ou usar para premiar o jogador. Você é livre para definir o nome dos campos. Em nosso Leaderboard os dados são armazenados na variável score.

O Playfab irá atualizar os dados por meio de requests e irá fazer callbacks dependendo do resultado: sucesso ou falha.

PlayFabClientAPI.UpdateUserStatistics(request, StatsUpdated, OnPlayFabError);

StatsUpdated é o callback de sucesso.

OnPlayFabError é callback de erro.

sweet-lab-playfab-1

Construção do Leaderboard

A classe que irá construir o leaderboard checará as variáveis AllDataSet (configurada junto com a IsLogged) e leaderboardLoaded. Dessa forma, ela não fará nada até que os dados estejam todos configurados, e também não irá criar caso o LB já tenha sido criado previamente.

void Update()
    {
        if (GameController.AllDataSet == true && leaderboardLoaded == false)
        {
            BuildLB();
        }
    } 

void BuildLB()
    {
        PlayFab.ClientModels.GetLeaderboardRequest request = new PlayFab.ClientModels.GetLeaderboardRequest();
        request.MaxResultsCount = 5;
        request.StatisticName = "score";

        PlayFabClientAPI.GetLeaderboard(request, ConstructLeaderboard, OnPlayFabError);
    }

A função BuildLB irá buscar os dados do LB no servidor do Playfab. Como a maioria das funções do provedor, ela irá enviar um request e receber um callback indicando sucesso ou fracasso (ConstructLeaderboard ou OnPlayFabError).

No Sweet Lab, o LB tem somente 5 posições: request.MaxResultsCount = 5

Como eu havia dito anteriormente, qualquer campo pode ser utilizado para construir o LB, mas é necessário indicar qual: request.StatisticName = “score”;

Assim que os dados são recuperados, o Playfab fará um callback.

Configuramos o callback de sucesso para  ConstructLeaderboard. Então, com os dados já locais iremos preencher os campos com seus devidos valores:

private void ConstructLeaderboard(PlayFab.ClientModels.GetLeaderboardResult result)
    {
        leaderboardLoaded = true;
        int i = 0;
        foreach (PlayFab.ClientModels.PlayerLeaderboardEntry entry in result.Leaderboard)
        {
            UpdateField("Jogador (" + i + ")", entry.DisplayName);
            UpdateField("PontosJogador (" + i + ")",  entry.StatValue.ToString() );
            i++;
        }
        GetPlayerPosition();
    } 

Result é uma lista com os valores do LB. A função ConstructLeaderboard irá fazer um loop pelos valores de result e preencher os campos utilizando a função UpdateField. Esta função não é um bom exemplo em termos de performance, mas ainda não decidimos o formato final do LB e, por hora, ela nos dá flexibilidade.

public void UpdateField(string nome, string valor)
    {
        try
        {
            var generico = GameObject.FindGameObjectsWithTag("SCORE");
            foreach (GameObject item in generico)
            {
                if (item.name == nome)
                {
                    Text text = item.GetComponent<Text>();
                    if (text != null)
                    {
                        text.text = valor;
                    }
                }
            }
        }
        catch (Exception e)
        {
            
        }
    }

A última etapa da construção do LB é buscar a posição do jogador atual. A função GetPlayerPosition é chamada por ConstructLeaderboard.

void GetPlayerPosition()
    {
        PlayFab.ClientModels.GetLeaderboardAroundCurrentUserRequest request = new PlayFab.ClientModels.GetLeaderboardAroundCurrentUserRequest();
        request.MaxResultsCount = 1;
        request.StatisticName = "score";

        PlayFabClientAPI.GetLeaderboardAroundCurrentUser(request, SetCurrentPlayerPosition, OnPlayFabError);
    }

    void SetCurrentPlayerPosition(PlayFab.ClientModels.GetLeaderboardAroundCurrentUserResult result) {
        UpdateField("Jogador", GameController.NomeDoJogador);
        if (GameController.PontuacaoTotal>0)
            UpdateField("PontosJogador",  GameController.PontuacaoTotal.ToString());

        foreach (PlayFab.ClientModels.PlayerLeaderboardEntry entry in result.Leaderboard)
        {
            UpdateField("PosicaoJogador", entry.Position.ToString());

        }
        GOLoading.SetActive(false);
    }

Perceba que para este caso utilizamos MaxResultsCount = 1. Quando o LB estiver finalizado, desativamos o objeto loading: GOLoading.SetActive(false);

Considerações Finais e Próximos Passos

E então, pronto para implementar o LeaderBoard do seu jogo em C#?

Você quer saber como criar jogos com potencial de vender milhares de cópias e que podem ser desenvolvidos por uma pessoa sozinha ou uma pequena equipe?

Eu preparei um vídeo simples explicando como eu faço isso no meu estúdio de games. Acesse essa página para conferir: https://pdj.blog.br/aula-criar-jogos.

Opa,

qual foi a maior sacada que você teve? Conte nos comentários.