terça-feira, 24 de fevereiro de 2026

UEFN Verse: Construtor e Persistência

Neste artigo vou mostrar como usar um construtor para facilitar a atualização de dados que são persistidos.

Se você ainda não conhece nada sobre persistência usando Verse, eu recomendo a leitura do meu artigo anterior:

UEFN Verse: Introdução à Persistência

A melhor opção para persistir os dados do jogador é usando uma classe. Na classe você pode reunir vários campos e associar a instância desta classe ao jogador no weak_map. Outra vantagem do uso de classes na persistência é que elas permitem a adição de campos depois da publicação da ilha.

As classes usadas na persistência precisam ter os especificadores <persistable> e <final>, que indica que a classe não pode ter subclasses. 

Outro ponto importante é que a classe não pode ter variáveis, somente constantes. O código abaixo é um exemplo simples de classe usada para persistência. 

player_data := class<final><persistable>:
    Version<public>:int = 1
    PlayerXP<public>:int = 0
    ItemsCollected<public>:int = 0

É uma boa prática ter um campo que indique a versão da classe. Desta forma, você pode detectar se os dados de um jogador ainda estão em uma versão anterior e fazer algum tipo de conversão se for necessário.

A linha a seguir mostra a variável do tipo weak_map que associa o tipo player à nova classe player_data. Os valores desta variável são persistidos.

var PlayerDataMap:weak_map(player, player_data) = map{}

Como uma classe usada para persistência não pode ter variáveis, será preciso criar uma nova instância da classe toda vez que houver uma atualização dos dados. Podemos usar um construtor para facilitar esta tarefa.

Um construtor é um tipo de função que cria uma instância de uma classe. Em nosso exemplo, ele será usado para fazer uma cópia de uma instância e depois poderemos atualizar apenas os campos que foram modificados.

O código abaixo é de um construtor que recebe como parâmetro uma instância de player_data e retorna uma nova instância com os valores copiados.

NewPlayerData<constructor>(OldData:player_data)<transacts> := player_data:
    Version := OldData.Version
    PlayerXP := OldData.PlayerXP
    ItemsCollected := OldData.ItemsCollected

Observe que a função NewPlayerData não tem um tipo de retorno usando o operador ":". Uma função do tipo construtor recebe uma instância de uma classe usando o operador de atribuição ":=".

Uma função do tipo construtor pode ser chamada na instanciação de arquétipo de uma classe. No exemplo abaixo, o objetivo é criar uma nova instância de player_data e atribuir um novo valor ao campo ItemsCollected

set PlayerDataMap[Player] = player_data:
                ItemsCollected := NewItemsCollected
                NewPlayerData<constructor>(PlayerSavedData)

Esta é a forma usada para atualizar campos específicos de uma classe persistida. Na instanciação de arquétipo, o construtor é executado primeiro fazendo as cópias de todos os dados. Depois são feitas as atribuições nos campos como o do ItemsCollected.

Para ver estes códigos de persistência em uso, vamos criar um exemplo usando um Item Spawner Device. O jogador poderá coletar itens. Cada vez que um item for coletado, será atualizado o valor de ItemsCollectedQuando o jogador entrar de novo na experiência, a contagem dos itens coletados continuará a partir do valor que parou na última execução.

Abra o Verse Explorer, clique com o botão-direito no nome do projeto e escolha a opção Add new Verse file to project.

Em Device Name coloque constructor_device clique no botão Create Empty.

Copie o código Verse abaixo para o dispositivo constructor_device:

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

player_data := class<final><persistable>:
    Version<public>:int = 1
    PlayerXP<public>:int = 0
    ItemsCollected<public>:int = 0

NewPlayerData<constructor>(OldData:player_data)<transacts> := player_data:
    Version := OldData.Version
    PlayerXP := OldData.PlayerXP
    ItemsCollected := OldData.ItemsCollected

var PlayerDataMap:weak_map(player, player_data) = map{}

constructor_device := class(creative_device):

    @editable  
    ItemSpawnerDevice: item_spawner_device = item_spawner_device{}

    OnBegin<override>()<suspends>:void =
        ItemSpawnerDevice.ItemPickedUpEvent.Subscribe(HandleItemPickedUp)
        for (Player : GetPlayspace().GetPlayers()):
            if:
                not PlayerDataMap[Player]
                set PlayerDataMap[Player] = player_data{}

    HandleItemPickedUp(Agent:agent):void=	
        if: 
            Player := player[Agent]
            PlayerSavedData := PlayerDataMap[Player]
            NewItemsCollected := PlayerSavedData.ItemsCollected + 1
            set PlayerDataMap[Player] = player_data:
                ItemsCollected := NewItemsCollected
                NewPlayerData<constructor>(PlayerSavedData)                
        then:
            Print("Itens coletados: {NewItemsCollected}")

A função OnBegin registra a função HandleItemPickedUp no evento ItemPickedUpEvent do ItemSpawnerDevice. Esta função será executada quando o jogador coletar um item. O laço for é usado para verificar se todos os jogadores que estão na experiência possuem dados persistidos. Se um jogador não possuir os dados, será criado um player_data com valores padrões e armazenado no PlayerDataMap weak_map. 

A função HandleItemPickedUp recupera os dados persistido do jogador e armazena em PlayerSavedData. O novo valor para o campo ItemsCollected é calculado e armazenado temporiamente em NewItemsCollected. O PlayerDataMap weak_map recebe uma nova instância de player_data que foi criada usando o construtor junto com a atualização do campo ItemsCollected.  

Salve o arquivo e compile o código Verse usando a opção Verse > Compile Verse Code do menu do UEFN. 

Acesse o Content Drawer e adicione o dispositivo constructor_device ao nível. Adicione um Item Spawner Device no nível e na aba Details configure conforme a imagem para que uma moeda seja gerada a cada 3 segundos.


Selecione o constructor_device no nível. Na aba Details adicione a referência ao Item Spawner Device.


Salve o nível e clique no botão Launch Session para carregar o nível no Fortnite. Colete algumas moedas. Cada vez que uma moeda for coletada, o contador de moedas que está persistido será atualizado e uma mensagem será exibida com o valor atual do contador.

Para simular o uso da persistência localmente não encerre a sessão do UEFN. No Fortnite aperte ESC para sair da experiência atual sem sair da sessão do UEFN. 


Quando entrar novamente na experiência com o mesmo jogador e coletar uma moeda, a contagem continuará a partir do último valor que foi salvo antes de sair da experiência.

Sumário Verse