sábado, 17 de março de 2012

Usando Arquivos de Configuração em UnrealScript

Arquivos de Configuração são arquivos em formato de texto simples com a extensão ".ini" que contém informações que serão utilizadas pelo jogo.

Na pasta "..\UDK-20??-??\UDKGame\Config" você encontrará diversos arquivos de configuração usados pelo UDK. Como exemplo temos o arquivo "DefaultInput.ini" que é usado para o mapeamento de teclas.

A principal vantagem no uso de arquivos de configuração é permitir o ajuste de um jogo sem a necessidade de recompilar o código fonte. Dessa forma um Game Designer pode fazer os ajustes dos dados de um jogo sem depender do programador.

Para utilizar um arquivo de configuração em uma classe, adicione o modificador config da seguinte forma:
class NomeClasse extends SuperClasse config(NomeConfig); 

Para identificar quais são as variáveis que irão obter seus valores a partir de arquivos de configuração, utilize também o modificador config ao declarar variáveis:
var config int exemploVar;

O próximo passo é criar na pasta "..\UDK-20??-??\UDKGame\Config" um arquivo de configuração com o nome "DefaultNomeConfig.ini" contendo os valores que devem ser armazenados nas variáveis. Quando o jogo for executado será procurado um arquivo chamado "UDKNomeConfig.ini", caso  este arquivo não seja encontrado, será procurado o arquivo "DefaultNomeConfig.ini" para poder criar o "UDKNomeConfig.ini" com valores padrões. Você não deve editar diretamente o arquivo "UDKNomeConfig.ini", porque em algum momento ele poderá ser sobrescrito e você irá perder suas modificações.

Usando a função "SaveConfig()" é possível salvar no arquivo "UDKNomeConfig.ini" novos valores das variáveis de configuração que foram modificadas durante a execução do jogo. Existe outra função chamada "StaticSaveConfig()" que permite que os valores padrões (default) sejam restaurados. Esta é uma opção comum na tela de configuração dos jogos, como pode ser visto na imagem abaixo.


A função "StaticSaveConfig()" é estática, isso significa que ela não precisa de um objeto para poder ser executada. As funções estáticas são como se fossem funções de classe, para executá-la basta usar o nome da classe mais a palavra static como neste exemplo:
class'NomeClasse'.static.StaticSaveConfig();

Como exemplo, fiz uma classe que desenha na tela algumas informações que foram obtidas em um arquivo de configuração, como pode ser visto na imagem abaixo.


Criei um arquivo de configuração chamado "DefaultWarrior.ini" que contém as seguintes informações:
[RomeroScripts.CyberWarrior]
codename=DeadLocker
age=33

implants=Bionic Eye
implants=Night Vision
implants=Thermal Resistence

Attributes=(strength=12, agility=10, baseExperience=5000, currentExperience=6000)

A primeira linha indica qual é a classe que irá utilizar este arquivo. "RomeroScripts" é o nome do pacote (package) que eu uso e "CyberWarrior" é o nome da classe.

A classe "CyberWarrior" define as variáveis de configuração. Além disso, ela utiliza um Timer de 1 segundo para aumentar o nível de experiência atual e utiliza outro Timer de 10 segundos para salvar os dados do jogador. A função "DrawHUD()" desenha as informações na tela.
class CyberWarrior extends SimplePawn
      config(Warrior);

var config string codename;      
var config int age;

var config Array<string>    implants;

struct stAttributes
{
    var int strength, agility, baseExperience, currentExperience;
};

var config stAttributes Attributes;

event simulated PostBeginPlay()
{
    SetTimer(1, true);
    SetTimer(10, true, 'SaveWarrior');
}
function Timer()
{
    Attributes.currentExperience++;    
}
function SaveWarrior()
{
    SaveConfig();
}

simulated function DrawHUD( HUD H )
{
    local int i;
    
    super.DrawHUD(H);
    
    H.Canvas.SetDrawColor(255, 255, 255); // Branco   
    
    H.Canvas.SetPos(50, 10);   
    H.Canvas.DrawText("Codename:" @ codename @ "- Age:" @ age) ;
    
    H.Canvas.SetPos(50, 40);
    H.Canvas.DrawText( "*** Attributes ***" );
    H.Canvas.SetPos(50, 55);
    H.Canvas.DrawText( "Strength :" @ Attributes.strength);
    H.Canvas.SetPos(50, 70);
    H.Canvas.DrawText( "Agility :" @ Attributes.agility);
    H.Canvas.SetPos(50, 85);
    H.Canvas.DrawText( "Base Experience :" @ Attributes.baseExperience);
    H.Canvas.SetPos(50, 100);
    H.Canvas.DrawText( "Current Experience :" @ Attributes.currentExperience);
    
    H.Canvas.SetPos(300, 40);
    H.Canvas.DrawText( "*** Implants ***");
    for( i =0; i < implants.Length; i++)
    {
       H.Canvas.SetPos(300, 55 + i * 15);       
       H.Canvas.DrawText( implants[i] );
    }    
} 

Para utilizar esta classe precisamos criar o novo GameInfo que irá referenciar a classe CyberWarrior:
class GameWarrior extends SimpleGame;

defaultproperties
{        
    DefaultPawnClass=class'RomeroScripts.CyberWarrior'    
}

Estou usando como classe base o "SimpleGame" porque é mais fácil de escrever na Tela no "SimpleGame" do que usando o "UTGame".

Ao executar o jogo você verá que a informação "Current Experience" é alterado a cada segundo. Espere pelo menos 10 segundos antes de sair do jogo para que seja salvo o estado atual da classe. Ao sair do jogo, procure pelo arquivo "UDKWarrior.ini" na pasta dos arquivos de configuração. Você verá que o "currentExperience", que se encontra dentro de "Attributes", foi modificado. Neste arquivo aparecerão também outras variáveis de configuração que pertencem à superclasse "SimplePawn".

Para mais informações:
Configuration files