top of page
Search

Sistema de turnos no Unity (Turn Based System in Unity)

  • Writer: lucas pinto
    lucas pinto
  • Aug 23, 2020
  • 5 min read

Criando um sistema de turnos utilizando Corrotinas no Unity.


Final Fantasy Tactics, xcom, Final Fantasy, Advance Wars são alguns exemplos de excelentes jogos que foram montados em cima da mecânica de turnos.


Os jogos baseados em turno tem uma característica muito peculiar, eles são potencialmente marcantes para o jogador porque eles o convidam para um nível de imersão muito interessante. Em especial, para mim o jogo de turnos que marcou minha vida foi sem sombra de dúvidas o Dofus.


ree

Recentemente na preparação para um Game Jam decidi "esquentar" tentando implementar um sistema de turnos e apesar de ser somente o fundamento básico para esse tipo de mecânica admito que fiquei muito satisfeito com o resultado.


Nesse blog (?) decidi documentar todo o processo de aprendizado que tive nessa atividade e acho extremamente válido compartilhar isso com vocês para que também possam desfrutar dessa satisfação de ver a coisa funcionando com seus próprios olhos.


Estamos falando de um sistema de turnos quando os participantes concorrem por um mesmo recurso chamado tempo. A limitação desse recurso abre diferentes possibilidades para criar estratégias dentro do jogo

https://static.wixstatic.com/media/7f64cd_f05ab07da43b45d49a8fdb76a1bbb899~mv2.gif



1 - As Entidades com turnos (The Turn Entities)


O elemento mais fundamental de um sistema de turnos é a lista contendo todos os participantes do nosso sistema de turnos. Um sistema de turnos nada mais é do que uma forma de gerenciarmos o tempo entre diversos participantes, e por isso devemos definir quem são os participantes.

Um sistema de turnos nada mais é do que uma forma de gerenciarmos o tempo entre os diferentes participantes da nossa cena.

Na nossa abordagem, qualquer participante do jogo que tiver um turno será uma TurnEntity. Essas entidades a princípio precisam de duas propriedades (nome e iniciativa) e um método (TurnCo) conforme o código a seguir:


TurnEntity.cs

public class TurnEntity : MonoBehaviour
{

    public string name;
    public int initiative;
    
    public IEnumerator TurnCo() {

        Debug.Log( name + " turn started" );
        yield return new WaitForSeconds( 5 );
        Debug.Log( name + " turn ended" );

    }

}

A primeira propriedade será apenas para apresentação dos dados na Interface e a segunda e mais importante é a iniciativa que reflete diretamente na ordem que essa entidade vai ter dentro da lista de turnos. Nesse exemplo, se uma entidade tem 1 de iniciativa e outra 10, então esta (de 10) jogará antes no início da partida e a função IEnumerator será a nossa corrotina.


Além disso você notou que temos um método chamado TurnCo que nada mais é do que uma corrotina que vai ser executada quando for o turno daquela Entidade. Lembrando que por ser uma corrotina, ela será chamada por algo externo, que vai ficar esperando com que ela termine para seguir com as outras atividades.


Agora crie os seus GameObjects na cena que representam os jogadores e adicione em cada um deles o componente TurnEntity.cs informando para cada um o nome e a iniciativa.

ree

No meu exemplo eu adicionei também nas minhas entidades uma Material que é pra indicar quem é a Entidade que está no turno atual mas você não precisa disso.


Pronto, isso já basta para que tenhamos as Entidades que terão turnos no jogo. Agora vamos construir quem ficará responsável por gerenciar a lista dessas entidades e controlar o turno de cada uma delas.


2 - O Sistema de Batalha (The Battle System)


Agora que temos as entidades, vamos precisar de um Sistema de Batalha que vai ficar responsável por carregar a Lista de entidades, preparar o sistema de turnos e executar os turnos de cada jogador na ordem e no momento correto.


A primeira coisa mais importante e o coração de um sistema de turnos é a lista de Entidades com turno, sem ela nada disso aqui faria sentido (Lembre-se estamos fazendo tudo isso apenas para controlar uma lista). A segunda coisa mais importante de um sistema de turnos é saber quem é a entidade com o turno atual, para isso vamos criar um inteiro e uma TurnEntity (esta ultima apenas para facilitar a visualização via debug).


Vamos começar criando então a classe BattleSystem e declarar a nossa lista de entidades com turno e também a variável do índice


BattleSystem.cs

public class BattleSystem : MonoBehaviour
{

    [SerializeField] private List<TurnEntity> turnList;
    [SerializeField] private int currentTurnIndex;
    [SerializeField] private TurnEntity currentEntity;

    public List<TurnEntity> TurnList { get => turnList; }
    public int CurrentTurnIndex { get => currentTurnIndex; }
    
}

2.1 - Preenchendo a Turn List (Setup for Turn List)

Agora o nosso primeiro foco deve ser: dar um jeito de preencher a turnList com todas as entidades de turno do jogo na ordem correta. Pra isso vamos criar dois métodos, o primeiro chamado [A] FindTurnEntities() que vai ficar responsável por pegar todas as entidades da cena e adicionar na lista e o segundo, [B] AdjustTurnOrder(), que vai reordenar a nossa turn list de acordo com as iniciativas de cada personagem para que possamos iniciar a partida.


Essas duas atividades de setup da Turn List vão ser chamadas na chamada da função [C] Start do Monobehaviour (No meu caso eu gosto de driar uma função Setup() e chamar dentro do Start pra ficar mais organizado).


[A] BattleSystem.cs - FindTurnEntities ()

private void FindTurnEntities () {

    TurnEntity [ ] turnEntities = GameObject.FindObjectsOfType<TurnEntity>();

    if (turnList == null)
        turnList = new List<TurnEntity>();

    foreach (TurnEntity turnEntity in turnEntities)
        TurnList.Add( turnEntity );

}

[B] BattleSystem.cs - AdjustTurnOrder ()

private void AdjustTurnOrder () {

    if (TurnList == null) return;
    if (TurnList.Count <= 0) return;

    TurnList.Sort( (a, b) => a.initiative.CompareTo( b.initiative ) );
    TurnList.Reverse();

} 

[C] BattleSystem.cs - Start() e Setup() []

private void Setup () {

    FindTurnEntities();
    AdjustTurnOrder();

}

private void Start () {

    Setup();

}

Ótimo, agora crie na sua cena GameObject chamado BattleSystem, adicione o componente BattleSystem.cs, dê o Play no Unity e veja a nossa Turn List sendo preenchida. Você pode criar um GameObject vazio como pai do BattleSystem chamado Managers:


ree
ree
ree

2.2 - Preparando a Batalha (Preparing the Battle)


Com o "Coração" (Lista de Turnos) preenchida em ordem, antes de chamar o loop dos turnos, vamos fazer um preparativo simples. Vamos criar uma variável que vai controlar se estamos em batalha ou não e definir o primeiro índice da nossa lista de turnos como 0.


BattleSystem.cs - bool inBattle + Setup()


[SerializeField] private bool inBattle;
...

private void Setup () {

    ...
    inBattle = true;
    currentTurnIndex = 0;

}

2.3 - Loop dos Turnos (Turn Loop)


Agora entramos nas etapas finais: Controlar os turnos. Lembra lá no começo quando disse que tinhamos uma corrotina Turn lá na entidade que seria chamada enquanto alguém iria esperar que ela terminasse de ser executada? Então, esse alguém é o nosso TurnLoop.


O TurnLoop vai ser uma corrotina também responsável por [A] encontrar qual é a entidade de turno atual, [B] chamar o turno da entidade atual e aguardar que ele acabe e [C] atualizar o índice de turno para a próxima entidade.


BattleSystem.cs - Start()

private void Start () {

    ...
    StartBattle();

}


BattleSystem.cs - StartBattle (), TurnLoopCo () e GetNextTurnIndex (int)

private void StartBattle () {

    StartCoroutine( TurnLoopCo() );

}

private IEnumerator TurnLoopCo () {

    while (inBattle) {

        currentEntity = turnList [ CurrentTurnIndex ];
        yield return currentEntity.Turn();
        currentTurnIndex = GetNextTurnIndex( CurrentTurnIndex );
        currentEntity = turnList [ CurrentTurnIndex ];

    }
    
}

private int GetNextTurnIndex (int currentIndex) {

    currentIndex++;
    currentIndex %= turnList.Count;
    return currentIndex;

}

Prontinho! Agora é só colocar pra Executar! Deixei um screenshot do meu funcionando.


Obs.: Adicionei também um no projeto um sisteminha de UI que representa a Turn List.

Obs2.: Assim que eu conseguir mais tempo atualizo esse post com esse sistema de UI.

ree

ree


Gostou do Conteúdo?


Se você achou útil o que leu aqui, me ajuda também. Existem várias formas de você contribuir, seja baixando meu jogo, seja fazendo uma doação ou seja até mesmo comentando aqui o que achou.

 
 
 

4 Comments


Gabriel Duarte
Gabriel Duarte
Sep 24, 2024

Parabéns pelo post,eu estava a procura de videos para aprender o mesmo conceito mas nenhum explicou bem o suficiente quanto este post, reproduzi o passo a passo e foi um sucesso.

ree

A coroutine que contém nos scripts das entidades são iniciados de acordo com a ordem de turno, quando ela finaliza ação, é a vez de outra entidade agir.

ree


Para quem pretende realizar uma cena de batalha no estilo RPG turn based, é somente implementar a UI de ações e acrescentar um enum de ações no script BattleSystem para dividir os turnos dos jogador ,inimigos e aliados.

Like
LP
LP
Sep 24, 2024
Replying to

Sensacional! Fico muito feliz que tenha te ajudado.

Like

Thiago Mendes
Thiago Mendes
Apr 20, 2022

Lucas esse post ficou muito bom, parabéns! Estou tentando criar esse sistema inspirado no Wakfu, mas estou utilizando o Construct 3 e como sou bem iniciante estou tendo dificuldades. Infelizmente não entendo muito da linguagem que vc utilizou, o que deixa a minha compreensão bem mais difícil, adoraria ver esse guia para o Construct tbm

Like
LP
LP
Sep 24, 2024
Replying to

Poxa, infelizmente só fui ver seu comentário muito depois. Infelizmente eu não trabalho com a Construct se tiver dúvidas em Unity pode me falar que eu te ajudo.

Like
Turn Based.png

A Blog by Opus Studio

WEEKLY NEWSLETTER 

Thanks for submitting!

CREATED WITH WIX.COM

bottom of page