domingo, 18 de dezembro de 2011

Classe PlayerInput (UnrealScript)

A classe PlayerInput foi criada para gerenciar os dispositivos de entrada do jogo. Esta classe possui uma característica diferente, pois ela é uma classe interna à classe PlayerController. Isto significa que um objeto da classe PlayerInput não pode existir sem um objeto da classe PlayerController.

A declaração da classe PlayerInput foi feita assim:
class PlayerInput extends Input within PlayerController

Uma classe interna possui acesso direto às variáveis e funções da classe externa. Mas se na classe interna houver variáveis/funções com o mesmo nome, é preciso usar a palavra "Outer" para referenciar a classe externa. É sempre recomendado o uso do "Outer" para evitar confusões no código. Como exemplo, na classe PlayerController existe uma variável chamada "bDemoOwner". Para acessá-la na classe PlayerInput, use:
Outer.bDemoOwner
A instância da classe PlayerInput é criada na classe PlayerController na função "InitInputSystem()" da seguinte forma:
PlayerInput = new(Self) InputClass;
O "InputClass" é definido no DefaultProperties do PlayerController e o "Self" é a referência da classe PlayerController que será armazenada no objeto Outer da classe PlayerInput.

-----

Conforme foi visto no artigo sobre Mapeamento de Teclas, o arquivo de configuração DefaultInput.ini é usado para definir os comandos que poderão ser executados pelo jogador.

Na classe PlayerInput são definidas alguns variáveis que irão armazenar as informações relacionadas aos Eixos. Estas variáveis possuem um modificador chamado input tornando elas acessíveis ao sistema de entrada do Unreal.

Abaixo temos alguns exemplos de variáveis da classe PlayerInput:
var input    float    aBaseX;
var input    float    aBaseY;
var input    float    aBaseZ;
var input    float    aMouseX;
var input    float    aMouseY;
var input    float    aForward;
var input    float    aTurn;
var input    float    aStrafe;
var input    float    aUp;
var input    float    aLookUp;

var input    float    aRightAnalogTrigger;
var input    float    aLeftAnalogTrigger;
Exemplo de um mapeamento para uma dessas variáveis:
.Bindings=(Name="GBA_MoveForward",Command="Axis aBaseY Speed=1.0")

A ação GBA_MoveForward (mover em frente) atribuirá a posição do eixo do dispositivo de entrada diretamente à variável aBaseY. O atributo Speed é uma espécie de multiplicador do valor do eixo.

-----

Quando uma função está associada à uma tecla, ela só será chamada uma vez no momento do pressionamento da tecla. Para que ela seja chamada de novo é preciso soltar a tecla e pressionar de novo. Dessa forma não dá para implementar ações que sejam executadas enquanto a tecla está sendo pressionada.

Para resolver este problema podemos usar uma função executável que receba como parâmetro um valor booleano. No mapeamento essa função será chamada inicialmente com o valor "true" e quando a tecla for liberada (Onrelease) a função será chamada com o valor "false".

Como Exemplo, no jogo Unreal Tournament a Tabela de Pontos é exibida enquanto a tecla F1 está sendo pressionada. Ao soltar a tecla a Tabela de Pontos deixa de ser exibida.

O código abaixo mostra o mapeamento e a função que armazena o valor booleano.
//No arquivo DefaultInput.ini

.Bindings=(Name="GBA_ShowScores",Command="SetShowScores true | Onrelease SetShowScores false")


//Na classe HUD (pacote Engine)

exec function SetShowScores(bool bNewValue)
{
    //Armazena o valor booleano que indicará se a Tabela de Pontos deverá ser exibida.
    bShowScores = bNewValue; 
}


segunda-feira, 12 de dezembro de 2011

Programação de Jogos para o Nintendo DS

Existe um manual em inglês de "Introdução à Programação do Nintendo DS" que foi escrita por Jaeden Amero e pode ser acessado em:

http://www.patater.com/manual

O texto mostra a configuração das ferramentas necessárias e aborda a programação em C++ de Sprites, Controles e Sons para o Nintendo DS.


sexta-feira, 2 de dezembro de 2011

Lucas Romero no jornal "O Liberal"

O meu filho Lucas Romero estuda no Colégio Gentil Bittencourt (Belém do Pará). O Colégio selecionou um aluno de cada ano do ensino fundamental para fazer uma visita à redação do jornal local "O Liberal" com o objetivo de divulgar a Cantata Natalina que ocorrerá no dia 03/12.

A nota abaixo foi publicada no jornal "O Liberal" do dia 02/12/2011. O Lucas Romero está no centro da foto. Este garoto vai longe :)

sexta-feira, 25 de novembro de 2011

Explorando o código fonte UnrealScript com UnCodeX

A maior e mais atualizada referência sobre UnrealScript é o código fonte que acompanha o UDK. Só que as vezes é difícil acompanhar o código fonte e entender o relacionamento entre as classes.

Para facilitar esta tarefa vamos utilizar uma ferramenta chamada UnCodeX, que foi desenvolvida por Michiel 'elmuerte' Hendriks, e que pode ser baixada na página do projeto.

A instalação é simples, na 1ª execução do programa aparecerá uma caixa de mensagem perguntando se deseja abrir a janela de propriedades. Escolha "Yes". Caso você queira abrir a janela de propriedades posteriormente, acesse o Menu: "Tree -> Settings...". 

Na propriedade "Source Paths" adicione a pasta "\Development\Src" do UDK, como na imagem abaixo.


O UnCodeX irá analisar todos os pacotes e classes para exibi-los da seguinte forma:


Como exemplo vamos pesquisar e analisar uma classe. No tutorial anterior eu usei o static mesh das "Botas de Pulo" do Unreal Tournament. Vamos tentar encontrar a classe que implementa as "Botas de Pulo". 

Acesse o Menu: "Find -> Find Class...", na pesquisa escreva "boots" e marque a opção "Search from the top" conforme a imagem, para que a pesquisa seja feita a partir da classe base Object.


Pressione "OK" para iniciar a pesquisa. O UnCodex encontrou a classe "UTJumpBoots" que é exatamente a que estávamos querendo. Se quisermos continuar a pesquisa basta pressionar F3 para "Encontrar o Próximo".


Clicando com o botão direito na classe, podemos observar que ela se encontra no pacote "UTGameContent". Vamos escolher a opção "Show Properties..." para termos uma visão geral das variáveis e funções desta classe.


Olhando no código fonte desta classe podemos perceber a existência de funções feitas para lidar com o aspecto multiplayer do Unreal Tournament.

Vamos começar analisando o bloco defaultproperties. É definido o StaticMesh que irá representar visualmente esta classe no jogo e são atribuidos valores para algumas variáveis, como a variável "Charges" que indica quantas vezes as botas podem ser usadas.


A função AdjustPawn() adiciona ou remove o bônus das botas no jogador. Nesta função podemos ver um erro de comentário resultante de uma operação Copiar/Colar.


Outra função interessante é a BotDesireability() que calcula o interesse de um Bot por este item. Basicamente a função verifica se o Bot já possui e quantas vezes ainda pode ser usada.


O principal objetivo deste artigo é mostrar que você pode aprender muitas coisas sobre UnrealScript visualizando o código fonte que vem com o UDK.

domingo, 20 de novembro de 2011

Tocando Sons em UnrealScript

SoundCue é um conjunto de instruções que informa a Unreal engine como tocar determinado som. SoundCues são bem flexíveis e podem ser criados no SoundCue Editor do UDK. Tocar um SoundCue em UnrealScript é muito simples, como pode ser visto no código abaixo:
class TesteSom1 extends Actor
      placeable;

var SoundCue exemploSom; 

event PostBeginPlay()
{
   SetTimer( 3 );    
}
function Timer()
{
   PlaySound( exemploSom );
}
defaultproperties
{        
    exemploSom = SoundCue'A_Music_GoDown.MusicMix.A_Music_GoDownMixCue'    
}

No exemplo acima usei um SoundCue que representa uma música. Então em algum momento posso querer parar essa música. Já que usamos a função PlaySound() então deve haver uma função StopSound(), certo? Errado, pois não existe a função StopSound() :(

Isto limita o uso da função PlaySound() para efeitos sonoros pequenos e sem loop. Para podermos usar música e sons em loop, teremos de usar a classe AudioComponent. O exemplo abaixo cria um AudioComponent que representa um som ambiente de cachoeira. Para testá-lo basta colocar em algum lugar do cenário.
class TesteSom2 extends Actor
      placeable;

defaultproperties
{
    Begin Object Class=AudioComponent Name=AudioComp
        SoundCue=A_Ambient_Loops.Water.Waterfall_Medium_02_Cue
        bAutoPlay=true        
    End Object    
    Components.Add( AudioComp )
}

A classe AudioComponent possui algumas funções úteis:
  • function Play();
  • function Stop();
  • function bool IsPlaying();
  • function FadeIn( float FadeInDuration, float FadeVolumeLevel );
  • function FadeOut( float FadeOutDuration, float FadeVolumeLevel );
Como exemplo, criei um mini game onde o jogador precisa coletar as Botas (classe TesteSomItem) que estão no cenário. Quando o jogador pega uma Bota um som é tocado. Este exemplo usa duas músicas. Uma música durante o jogo e uma música de vitória que é iniciada quando o jogador coletar todas as Botas. Não esqueça de definir o GameType como TesteSomGameInfo.
class TesteSomGameInfo extends UTGame;

var SoundCue itemSom; 
var AudioComponent musicaJogo, musicaVitoria;
var int itemCont;

function StartMatch()
{
   local TesteSomItem itemLocal;
   
   Super.StartMatch();
   
   itemCont = 0;
   
   //Verificar quantos Itens tem no cenário
   foreach AllActors( class'TesteSomItem', itemLocal )
   {
      itemCont++;
   }
      
   musicaJogo.Play();      
}

function RemoveItem( TesteSomItem item )
{      
   PlaySound( itemSom );
   item.Destroy();
   itemCont--;
   
   if ( itemCont <= 0 ) 
   {
      Broadcast( self, " Voce Coletou todos os Itens.") ;      
      musicaJogo.FadeOut( 2, 0 );
      musicaVitoria.FadeIn( 2, 1 );
   }
}
defaultproperties
{        
    itemSom = SoundCue'A_Pickups_Powerups.PowerUps.A_Powerup_JumpBoots_PickupCue'
    
    Begin Object Class=AudioComponent Name=Musica01Comp
        SoundCue=A_Music_GoDown.MusicMix.A_Music_GoDownMixCue                
    End Object
    
    musicaJogo = Musica01Comp        
    
    Begin Object Class=AudioComponent Name=Musica02Comp
        SoundCue=A_Music_RomNecris01.MusicSegments.A_Music_RomNecris01_Victory01Cue            
    End Object
        
    musicaVitoria = Musica02Comp
}

Classe que representa as Botas:
class TesteSomItem extends Actor
      placeable;

event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
{
   local TesteSomGameInfo testeSomGameInfo;    
   
   testeSomGameInfo = TesteSomGameInfo( worldinfo.game );
   
   if( testeSomGameInfo != None)
   {    
    if( Pawn(Other) != None && Pawn(Other).controller.bIsPlayer )
    {               
        testeSomGameInfo.RemoveItem( self );      
    }
   }
}     
defaultproperties
{        
    bCollideActors=true    
    bBlockActors=false
    
    Begin Object Class=CylinderComponent Name=CylinderComp
        CollisionRadius=20
        CollisionHeight=28
        CollideActors=true        
        BlockActors=false
    End Object    
    Components.Add( CylinderComp )
    CollisionComponent=CylinderComp    
        
    Begin Object Class=DynamicLightEnvironmentComponent Name=LightEnvironmentComp
        bEnabled = true
    End Object    
    Components.add( LightEnvironmentComp )
    
    Begin Object Class=StaticMeshComponent Name=StaticMeshComp
        StaticMesh = Pickups.JumpBoots.Mesh.S_UN_Pickups_Jumpboots002    
        LightEnvironment = LightEnvironmentComp
        Scale = 2
    End Object    
    Components.add( StaticMeshComp )    
}     


Para mais informações sobre Sons:
Audio System

terça-feira, 15 de novembro de 2011

Colisão em UnrealScript

Colisão em UnrealScript nem sempre se trata de física complexa como resposta à uma colisão. As vezes queremos apenas saber se dois Actors estão se sobrepondo.

Apesar de os gráficos 3D dos personagens nos jogos atuais serem bem complexos e com muitos polígonos, as formas geométricas utilizadas para detectar uma colisão continuam sendo simples para poder manter um bom desempenho. O cilindro é uma das formas geométricas mais utilizadas para detectar colisão. Para definirmos um cilindro precisamos apenas informar o Raio da base e sua Altura.

A classe Actor possui algumas propriedades booleanas relacionadas a colisão, como:
  • bCollideActors : Se for falso, o Actor não responderá a nenhum tipo de colisão.
  • bBlockActors: Se for verdadeiro, a Unreal engine não permitirá que os dois Actors ocupem o mesmo lugar.
E alguns eventos relacionados à colisão:
  • event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal): Este evento é chamado quando dois Actors estão se sobrepondo, mas só ocorrerá se em um deles a propriedade bBlockActors for falso.
  • event Bump(Actor Other, PrimitiveComponent OtherComp, Vector HitNormal): Este evento é chamado quando dois Actors estão se encostando, mas só ocorrerá se nos dois Actors a propriedade bBlockActors for verdadeiro.
Como exemplo criei um UltraHealthPack. Quando o jogador colide com este UltraHealthPack a sua energia máxima é aumentada em 50 e a sua energia atual recebe o novo valor de sua energia máxima.
class UltraHealthPack extends Actor
      placeable;

event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
{
   local Pawn pawnLocal;
   
   pawnLocal = Pawn( Other ); 
   
   if(pawnLocal != None)
   {
        //verifica se é o Jogador
      if( pawnLocal.controller.bIsPlayer ) 
      {
         pawnLocal.HealthMax += 50;
         pawnLocal.Health = pawnLocal.HealthMax;
         
         //'self' é uma referencia ao objeto atual (UltraHealthPack)
         self.Destroy();
      }
   }
}    
defaultproperties
{
    bCollideActors=true    
    bBlockActors=false
    
    Begin Object Class=CylinderComponent Name=CylinderComp
        CollisionRadius=32
        CollisionHeight=48
        CollideActors=true        
        BlockActors=false
    End Object
    
    Components.Add( CylinderComp )
    CollisionComponent=CylinderComp    
        
    Begin Object Class=DynamicLightEnvironmentComponent Name=LightEnvironmentComp
        bEnabled = true
    End Object
    
    Components.add( LightEnvironmentComp )
    
    Begin Object Class=StaticMeshComponent Name=StaticMeshComp
        StaticMesh = Pickups.Health_Large.Mesh.S_Pickups_Health_Large_Keg        
        LightEnvironment = LightEnvironmentComp
    End Object
    
    Components.add( StaticMeshComp )                
}


Para mais informações sobre Colisão:
Collision Reference

sexta-feira, 11 de novembro de 2011

Desenhando o HUD (UnrealScript)

HUD (Head Up Display) é uma espécie de painel utilizado para mostrar informações durante o jogo usando imagens e textos. Existem duas formas de desenhar o HUD, a mais comum é utilizando uma classe Canvas que possui funções para desenhar os gráficos e textos. A outra opção é utilizando o Scaleform GFx que permite que cenas feitas no editor Flash possam ser importadas para o UDK.

A imagem abaixo mostra as subclasses de HUD encontradas no UDK:


Fiz um exemplo de HUD que desenha uma imagem, um texto e um retangulo. Neste exemplo é desenhado uma barra de energia, a medida que o tempo passa a barra de energia diminui e muda de cor, como pode ser visto nas imagens:




Código do exemplo:
class TesteHUD extends UTHUD;

var CanvasIcon relogioIcon;
var int relogio; 

event PostBeginPlay()
{
   SetTimer( 1, true );
   relogio = 30;
}

function Timer()
{
  relogio--;

  if(relogio <= 0)
  {     
     relogio = 30;
  }
}

function DrawHUD()
{
   super.DrawHUD();    

   Canvas.DrawIcon(relogioIcon, 0, 0);     

   Canvas.Font = class'Engine'.static.GetLargeFont();      
   Canvas.SetDrawColor(255, 255, 255); // Branco
   Canvas.SetPos(70, 15);
   
   Canvas.DrawText(relogio);

   if(relogio < 10)
   {
     Canvas.SetDrawColor(255, 0, 0); // Vermelho
   }
   else if (relogio < 20)
   {
     Canvas.SetDrawColor(255, 255, 0); // Amarelo
   } 
   else 
   {
     Canvas.SetDrawColor(0, 255, 0); // Verde
   }
 
   Canvas.SetPos(200, 15);   
   Canvas.DrawRect(20 * relogio, 30);      

}
defaultproperties
{
 relogioIcon=(Texture=Texture2D'UDKHUD.Time')  
}

Agora, precisamos criar uma subclasse de GameInfo para referenciar a nova classe HUD. Só uma observação, se você estiver utilizando as classes UTGame/UTHUD é preciso definir como true a propriedade bUseClassicHUD na subclasse de UTGame, senão o seu HUD não irá funcionar.
class MeuUTGame extends UTGame;

defaultproperties
{    
   HUDType=class'RomeroScripts.TesteHUD'
   bUseClassicHUD=true
}

Para mais informações sobre HUD:
HUD Technical Guide

sábado, 5 de novembro de 2011

Classe GameInfo (UnrealScript)

A classe GameInfo especifica as características e regras do jogo. É uma espécie de classe gerenciadora que mantém as referências de outras classes responsáveis por diferentes aspectos do jogo. Por exemplo, o código abaixo foi extraído da classe UTGame que é uma subclasse de GameInfo.
//definições de variáveis, funções, estados...

defaultproperties
{
    HUDType=class'UTGame.UTHUD'
    PlayerControllerClass=class'UTGame.UTPlayerController'
    ConsolePlayerControllerClass=class'UTGame.UTConsolePlayerController'
    DefaultPawnClass=class'UTPawn'
    PlayerReplicationInfoClass=class'UTGame.UTPlayerReplicationInfo'
    GameReplicationInfoClass=class'UTGame.UTGameReplicationInfo'
    DeathMessageClass=class'UTDeathMessage'
    PopulationManagerClass=class'UTPopulationManager'
    BotClass=class'UTBot'

    //...
}

Cada tipo de jogo no Unreal Tournament (DeathMatch, Capture the Flag, etc.) é especificado por uma subclasse de GameInfo, como pode ser visto na hierarquia de classes abaixo.


Quando criamos e compilamos uma nova subclasse de GameInfo ela passa a ser automaticamente listada no Game Type que pode ser acessado através da opção de Menu do UDK: View -> WorldProperties -> Game Type.


Todo objeto da classe Actor tem acesso ao objeto GameInfo que representa o jogo atual. Esse acesso ocorre através de uma outra classe chamada WorldInfo. A classe WorldInfo contém as informações relacionadas ao mapa/fase atual, sendo que uma dessas informações é uma variável do tipo GameInfo chamada game. Nos tutoriais anteriores nós já estavamos utilizando essa variável GameInfo para poder escrever mensagens na tela usando a sua função Broadcast() como no exemplo abaixo:
class MeuActor extends Actor;

event PostBeginPlay()
{
 worldinfo.game.broadcast(self, "Usando o GameInfo");
}

Há apenas um problema, essa variável game faz referencia apenas para a classe base GameInfo. Se criarmos uma nova subclasse de GameInfo com novas variáveis e novas funções, a variável game não conseguirá acessar as novas variáveis/funções e irá gerar um erro de compilação. Para resolver este problema, presisamos converter a variável game para o tipo da subclasse GameInfo que estamos utilizando, como no exemplo abaixo:
//Em MeuGameInfo.uc:
class MeuGameInfo extends GameInfo;

var vector vPosicaoBonus;

//Em MeuActor.uc:
class MeuActor extends Actor;

var vector vObjetivo;

event PostBeginPlay()
{
   local MeuGameInfo meuGameInfo;

   //Converter o objeto GameInfo para um objeto do tipo MeuGameInfo   
   meuGameInfo = MeuGameInfo(worldinfo.game);
   
   //verificar se a conversão deu certo
   if(meuGameInfo != None )
   {
     vObjetivo = meuGameInfo.vPosicaoBonus;
   }
}

Para mais informações sobre a classe GameInfo:

sexta-feira, 28 de outubro de 2011

Mapeamento de Teclas em UnrealScript

UnrealScript permite a definição de funções executáveis. Essas funções podem ser chamadas durante o jogo a partir do game console e também podem ser associadas a teclas/botões.

Exemplo de uma função executável:
exec function TesteExec()
{    
 worldinfo.game.broadcast(self, "TESTANDO EXEC FUNCTION");
}

As funções executáveis só podem ser definidas em algumas classes (e subclasses) específicas, como: Controller, Pawn, Input, InventoryManager, Weapon, HUD, GameInfo, CheatManager.

Já existem diversas funções executáveis definidas no UnrealScript que são usadas principalmente para testes. Por exemplo, inicie um jogo como UTGame (Menu UDK: View -> WorldProperties -> Game Type). Abra o game console pressionando a tecla ' ou ~. Digite ThrowWeapon e pressione ENTER.


O mapeamento de teclas em UnrealScript é feito em um arquivo de configuração chamado DefaultInput.ini que se encontra na pasta "..\UDK-20??-??\UDKGame\Config\".

Por exemplo, para associarmos a tecla H com a função TesteExec() que foi criada acima, usamos o seguinte:

.Bindings=(Name="H",Command="TesteExec")

Analisando o arquivo DefaultInput.ini podemos observar que as funções executáveis foram organizadas em Game Bindable Action (GBA), para facilitar o mapeamento de uma função a várias teclas/botões.

O nosso exemplo então ficaria assim:

.Bindings=(Name="GBA_Teste",Command="TesteExec")

.Bindings=(Name="H",Command="GBA_Teste")

E se quisermos adicionar o botão Y do controle do XBox 360 para executar a mesma função:

.Bindings=(Name="XboxTypeS_Y",Command="GBA_Teste")


Para fazer um teste do mapeamento de teclas, criei uma subclasse de UTPawn com o nome de TestePawn, defini como placeable e adicionei um SkeletalMesh. Coloquei uma Exec Function para ser executada quando pressionasse a tecla H. Inseri esse TestePawn no editor e iniciei o jogo. Pressionei a tecla H e então... nada aconteceu??? :(

Veriquei o Log (..\UDK-20??-??\UDKGame\Logs\Launch.log) e encontrei o nome da minha função "TesteExec". Isto significa que o mapeamento feito no arquivo DefaultInput.ini funcionou, o problema é que a Unreal Engine não encontrou a definição da função que fiz no TestePawn.

Depois eu inseri a minha Exec Function direto na classe UTPlayerController. Testei e funcionou perfeitamente.

Criei uma subclasse de UTGame apenas para definir que o jogo deveria usar minha classe TestePawn como a classe Pawn padrão. Iniciei o jogo como MeuUTGame. O mapeamento da tecla também funcionou corretamente.
class MeuUTGame extends UTGame;
//UTGame é uma subclasse de GameInfo
defaultproperties
{
    DefaultPawnClass=class'RomeroScripts.TestePawn'
}

A conclusão que eu cheguei é que provavelmente a Unreal Engine procura as funções executáveis a partir da classe GameInfo. Sendo assim a classe que você quiser usar com Exec Function precisa estar sendo referenciada direta ou indiretamente a partir de uma classe GameInfo. A classe GameInfo será o assunto do próximo artigo.

Para mais informações sobre mapeamento de teclas e funções executáveis acesse:
http://udn.epicgames.com/Three/ExecFunctions.html

domingo, 23 de outubro de 2011

Usando Vetores em UnrealScript

Um vetor em UnrealScript é apenas uma estrutura que contém os valores X, Y e Z. O uso de vetores simplifica muita coisa na programação de jogos 3D. Os vetores podem ser usados para representar posição, velocidade, aceleração, distância, direção, etc.

UnrealScript possui algumas funções utilitárias para vetores, segue abaixo as duas mais importantes:
  • function float VSize( vector A ): Retorna o comprimento do vetor.
  • function vector Normal( vector A ): Retorna um vetor Normalizado, ou seja, um vetor cujo comprimento é 1.

A classe Actor possui um vetor chamado Location que indica sua posição atual no mundo 3D.

O exemplo abaixo ilustra os diversos usos de vetores. Foi criado um Actor que se movimenta em direção a uma posição aleatória. Quando ele chega no destino uma nova posição é escolhida.
class TesteVetores extends Actor
      placeable;
     
var vector vDestino;

const VEL = 100; 
//Velocidade = 100 UU/Seg (Unidade Unreal / Segundo)
     
function DefinirNovoDestino()
{
    //Escolhe posição aleatória
    vDestino.X = -500 + Rand(1001); //-500 a 500
    vDestino.Y = -500 + Rand(1001); //-500 a 500
    vDestino.Z = 150 + Rand(101); //150 a 250
    
    worldinfo.game.broadcast(self, "NOVO DESTINO: " $ vDestino);
}     

event PostBeginPlay()
{
    DefinirNovoDestino();
}

//Esta função é chamada a cada Quadro (Frame) pela Unreal Engine
event Tick(float DeltaTime)
{   
   local vector vDistancia;
   local vector vDirecao;
   local vector vVelocidade;
   local vector vPasso;
   
   vDistancia = vDestino - Location;   
   
   //Verificar se o Actor chegou no destino
   if( VSize( vDistancia ) < VEL )
   {
        DefinirNovoDestino();
        vDistancia = vDestino - Location;
   }
   
   vDirecao = Normal( vDistancia );      
   vVelocidade = vDirecao * VEL;
   vPasso = vVelocidade * DeltaTime;
   Move( vPasso );    
}
     
defaultproperties
{        
    begin object Class=StaticMeshComponent Name=Modelo3D
        StaticMesh = Pickups.UDamage.Mesh.S_Pickups_UDamage        
    end object
    
    Components.add(Modelo3D);
}    

quinta-feira, 20 de outubro de 2011

Usando Timers em UnrealScript

Timers são contadores existentes na classe Actor. Para cada Timer deve haver uma função associada que será executada quando a contagem chegar ao fim.

Vamos iniciar com um exemplo bem simples de Timer que conta 3 segundos e depois explode :)
class SimplesTimer extends Actor
      placeable;

event PostBeginPlay()
{
    SetTimer(3);
}
function Timer()
{    
    //o comando abaixo escreve um texto na tela
    worldinfo.game.broadcast(self, "KABOOM");
}

A função SetTimer() recebe como parâmetro a quantidade de segundos que deve contar. Quando esse tempo acabar será executada uma função com o nome de Timer() e depois disso este Timer será encerrado.

A função SetTimer() possui alguns parâmetros opcionais como pode ser visto abaixo:

function SetTimer(float InRate, optional bool inbLoop, optional Name inTimerFunc='Timer', optional Object inObj)

Descrição dos parâmetros:
  • InRate: Tempo de execução do Timer em segundos;
  • inbLoop: Variável booleana (true ou false) que indica se o Timer deve ser repetido;
  • inTimerFunc: Nome da função que o Timer deve chamar quando acabar a contagem;
  • inObj: Nome do objeto que contém a função que será chamada;
    No próximo exemplo vamos fazer um Timer que criará um inimigo no cenário a cada 10 segundos. Quando já tiverem sido criados 4 inimigos esse Timer será desativado.
    class TesteTimer extends Actor
          placeable;
    
    var int numInimigos;    
        
    event PostBeginPlay()
    {
        SetTimer(10, true, 'criarInimigo');    
        numInimigos = 0;    
    }
    function criarInimigo()
    {
        local Pawn inimigo;
        inimigo = spawn(class 'UTGame.UTPawn'); 
        inimigo.SpawnDefaultController();
        
        numInimigos++;    
        if( numInimigos >= 4 )
        {
            ClearTimer('criarInimigo');        
        }    
    }
    

    Para testar este código basta colocar no cenário um objeto dessa classe TesteTimer. Os inimigos serão criados na posição em que estiver o TesteTimer.

    Existem outras funções relacionadas a Timers, segue abaixo a descrição de algumas delas:
    • function ClearTimer(optional Name inTimerFunc='Timer', optional Object inObj): Desativa o Timer informado.
    • function ClearAllTimers(optional Object inObj): Desativa todos os Timers.
    • function PauseTimer( bool bPause, optional Name inTimerFunc='Timer', optional Object inObj ): Pausa/Despausa o Timer informado.
    • function bool IsTimerActive(optional Name inTimerFunc='Timer', optional Object inObj): Verifica se o Timer informado está ativo.
    • function float GetTimerCount(optional Name inTimerFunc='Timer', optional Object inObj): Retorna o tempo que já passou no Timer.
    • function float GetTimerRate(optional name TimerFuncName = 'Timer', optional Object inObj): Retorna o tempo de execução do Timer.
    • function float GetRemainingTimeForTimer(optional name TimerFuncName = 'Timer', optional Object inObj): Retorna o tempo restante do Timer.

    terça-feira, 18 de outubro de 2011

    Conclusão Genesis

    A minha história de desenvolvimento de jogos pode ser dividida nas seguintes fases:
    • 1ª Fase (1993 a 1998) : Pré RH Games
    • 2ª Fase (1999 a 2003) : RH Games e UFPA
    • 3ª Fase (2004 a 2009) : Beljogos
    • 4ª Fase (2010 em diante) : Romero Games
    Eu me diverti muito relembrando todas essas minhas aventuras desenvolvendo jogos. Foi muito bom organizar e disponibilizar para o público todo este material que eu produzi. Tenho certeza que ele será útil para as pessoas interessadas em aprender desenvolvimento de jogos.

    segunda-feira, 17 de outubro de 2011

    Eventos

    Em setembro de 2008 organizamos o 1º Encontro dos Desenvolvedores de Jogos do Pará. A programação do evento pode ser visto no anúncio abaixo.

    Durante o ano de 2010 o Beljogos organizou os eventos:

    domingo, 16 de outubro de 2011

    UDK

    Em novembro/2009, a empresa Epic Games lançou o Unreal Development Kit (UDK) que pode ser usado livremente para fins não comerciais.

    Eu gosto muito da Unreal Engine, e o lançamento do UDK foi quase como um sonho realizado. Essa ferramenta representa minha principal área de interesse para estudo, principalmente porque jogos como Gears of War, Mass Effect e Bioshock foram feitos com a Unreal Engine.

    O UDK pode ser obtido na página: http://www.udk.com/

    sábado, 15 de outubro de 2011

    Game Maker

    Eu gosto muito da ferramenta Game Maker, pois ela me permite ensinar a criação de jogos para pessoas que não são programadoras.

    Essa ferramenta foi muito bem projetada pois ela não te prende a nenhum tipo específico de jogo. É possível implementar qualquer jogo 2D usando o Game Maker.

    O Game Maker pode ser baixado na página: www.yoyogames.com/make

    Desenvolvi alguns exemplos de jogos em Game Maker que estão disponíveis com o código fonte neste link: Jogos Game Maker.

    sexta-feira, 14 de outubro de 2011

    Revista GameZone Especial

    Diversos trabalhos meus, como o MRDX, TCC, apresentações, jogos, etc, foram publicados no CD da revista Gamezone Especial nº 1 que foi lançada em junho de 2007.

    A revista pode ser adquirida na página da Editora Digerati.

    quinta-feira, 13 de outubro de 2011

    CryENGINE

    A CryEngine foi desenvolvida por uma empresa alemã chamada CryTek. O 1º jogo feito com a engine foi o "Far Cry". O ponto mais forte da engine é a parte gráfica. A CryEngine consegue representar ambientes externos com uma beleza impressionante.

    A 2ª versão da CryEngine foi utilizada para o desenvolvimento do jogo "Crysis". O foco da 3a versão da CryEngine foi o desenvolvimento multiplataforma. Ela se encontra agora disponível para as principais plataformas de games, como PC, XBox 360 e PlayStation 3, e foi usada no desenvolvimento do jogo "Crysis 2".

    Escrevi um texto apresentando os recursos básicos do editor de níveis do jogo Far Cry, que se encontra neste arquivo : cryengine.pdf

    quarta-feira, 12 de outubro de 2011

    Neverwinter Nights

    “Neverwinter Nights” é um jogo de RPG em 3D baseado nas regras de “Dungeons & Dragons” (D&D) e ambientado no mundo de “Forgotten Realms”. Foi desenvolvido pela Bioware e lançado no ano de 2002.

    O editor do Neverwinter foi o que mais me interessou no jogo. Ele possui muitos recursos e consegue manter uma interface bem simplificada. Mesmo sem se aprofundar nos recursos avançados dá para fazer algumas aventuras simples mas interessantes.

    Como tenho interesse no aprendizado e no ensino de desenvolvimento de jogos, encontrei neste editor um ótimo recurso para as duas coisas. Além disso, é possível se concentrar na parte de "Game Design", pois não é preciso se preocupar com a programação de aspectos de baixo nível do jogo e nem em modelagem 3D de itens básicos.

    Escrevi um texto que apresenta alguns aspectos básicos do editor. O texto está disponível neste arquivo: neverwinter.pdf

    terça-feira, 11 de outubro de 2011

    Romerian Strike

    Com o lançamento do MIDP 2.0 a SUN introduziu uma Game API (biblioteca) que facilita muito a criação de jogos 2D. A Game API possui classes para lidar com sprites, cenários e gerenciadores. 

    Para experimentar os recursos da Game API eu programei um jogo baseado no clássico 1945. Ele possui vários elementos como o jogador, diversos aviões inimigos, tiros, explosões, ilhas, pontos e barra de energia do jogador.

    Entretanto, nessa época o meu amigo foi chamado para trabalhar em um projeto grande da Microsoft em São Paulo. Depois de sua partida a empresa dele praticamente se desfez e cada sócio seguiu outros rumos.

    O jogo Romerian Strike com código fonte está disponível neste arquivo : RomerianStrike.zip

    segunda-feira, 10 de outubro de 2011

    Micro RPG

    Desenvolvi um protótipo de RPG para celulares de baixa capacidade mas que suportem o MIDP 1.0. O jogo permite scroll (rolagem da tela) e é possível conversar com os outros personagens.

    Para a edição dos cenários eu usei o utilitário Tile Studio. As imagens abaixo mostram diferentes cenários do jogo.

    O jogo Micro RPG com código fonte está disponível neste arquivo : MicroRPG.zip

    domingo, 9 de outubro de 2011

    Mina 3 (Java ME)

    Criei o jogo Mina 3 em java para celular com o objetivo de mostrar as possibilidades deste mercado para um amigo meu que tinha uma empresa de desenvolvimento de software junto com outros sócios.

    O Mina 3 foi feito para a especificação MIDP 1.0, que é a mais limitada mas que era suportada pela maioria dos celulares no mercado nessa época.

    O jogo Mina 3 com código fonte está disponível neste arquivo: Mina3.zip

    sábado, 8 de outubro de 2011

    Classe Controller (UnrealScript)

    A classe Controller é como se fosse o cérebro por trás das entidades na Unreal Engine. Ela é responsável por receber eventos externos, processar esses eventos e decidir qual será a próxima ação que deve ser executado pelo objeto do tipo Pawn que esteja associado ao Controller.

    As duas subclasses principais de Controller são AIController e PlayerController. Se o objetivo é criar um personagem que será controlado pelo jogador então devemos criar uma subclasse de PlayerController. Se o personagem for controlado por Inteligência Artificial então criaremos uma subclasse de AIController. A imagem abaixo mostra as subclasses de Controller existentes no UDK:


    O momento em que um objeto Pawn é associado a um objeto Controller é chamado de Possessão. Existe uma função na classe Controller com o nome de Possess() que é chamada quando a Possessão ocorre, sendo um ótimo local para fazer algum tipo de inicialização.

    O código abaixo cria uma simples subclasse de AIController. Este novo Controller define dois estados: Parado e Perseguindo. O personagem começa o jogo no estado Parado, quando ele visualizar o jogador, passa para o estado Perseguindo e começa a se mover em direção ao jogador.
    class TesteController extends AIController;
    
    var Pawn jogador;
    
    event Possess(Pawn inPawn, bool bVehicleTransition)
    {
      super.Possess(inPawn, bVehicleTransition);
      //A função abaixo inicializa a Física do objeto Pawn
      inPawn.SetMovementPhysics();
    }
    
    auto state Parado
    {
       event SeePlayer(Pawn Seen)
       {      
         super.SeePlayer(Seen);
         jogador = Seen;
         GotoState('Perseguindo');
       }
    }
    
    state Perseguindo
    {
       Begin:
          MoveToward(jogador); 
          goto 'Begin';
    }
    

    Para testar este código crie uma classe Pawn e coloque o nome do pacote e o nome da classe Controller no atributo ControllerClass da classe Pawn, como no exemplo abaixo:

    ControllerClass=RomeroScripts.TesteController


    Para mais informações sobre Controller e Pawn:
    Characters Technical Guide

    Robocode


    O Robocode é um ambiente muito divertido para praticar a linguagem Java e ganhar uma noção básica de programação de jogos.

    Fiz um robô que utiliza duas estratégias diferentes, uma para quando tem muitos robôs na arena e a outra para quando tem poucos robôs. O código fonte está bem documentado explicando a lógica utilizada na criação deste robô.

    O meu robô pode ser importado no ambiente do Robocode e está disponível neste arquivo: rhgames.RomeroX_1.0.jar

    sexta-feira, 7 de outubro de 2011

    Beljogos

    Como já estava saindo da UFPA, resolvi criar o grupo Beljogos para reunir todos os interessados em desenvolvimento de jogos para trocarmos experiências e disseminarmos essa cultura em Belém do Pará.

    Participei de vários eventos locais de informática representando o Beljogos, como na Semana Paraense de Informática (SEPAI) e no Fórum Paraense de Software Livre. Uma apresentação que fiz sobre o Beljogos na SEPAI 2005 está disponível neste arquivo: beljogos_2005.pdf

    Devido a grande movimentação na lista do Beljogos no Yahoo!Grupos, ela é listada na 1ª posição na categoria "Programação de Jogos", como pode ser visto na imagem abaixo:

    quinta-feira, 6 de outubro de 2011

    Trabalho de Conclusão de Curso

    O meu principal objetivo na elaboração do meu Trabalho de Conclusão de Curso (TCC), que se chama "Programação de Jogos 2D usando o MRDX e Fundamentos de Jogos 3D", era criar um texto bem prático em português sobre programação de jogos em C/C++, pois sentia falta deste tipo de material aqui no Brasil.

    A minha visão estava certa e o meu TCC se tornou muito popular, como pode ser visto nesta postagem: A Popularidade do meu TCC.

    Com esse trabalho eu conclui o curso de Bacharelado em Ciência da Computação na UFPA. Ele está disponível neste arquivo: TCCjogos.pdf

    quarta-feira, 5 de outubro de 2011

    Labirinto 3D


    Este programa foi feito para ser usado no meu Trabalho de Conclusão de Curso como uma introdução aos jogos 3D. 

    Ele desenha um simples labirinto 3D usando OpenGL. O usuário pode se mover pelo labirinto através das setas do teclado. Há uma esfera que se movimenta pelo labirinto desviando das paredes. Para visualizar o labirinto em modo wireframe pressione a tecla 'W'. 

    O programa com código fonte está disponível neste arquivo: lab3d.zip

    terça-feira, 4 de outubro de 2011

    Classe Pawn (UnrealScript)

    A classe Pawn é utilizada para representar fisicamente os jogadores, personagens e criaturas na Unreal Engine. Ela contém funcionalidades para lidar com modelos 3D (Mesh), animação, colisão, danos, física, uso de armas, geração de som, etc.

    Um objeto do tipo Pawn pode ser controlado por um jogador ou por Inteligência Artificial. Este controle fica encapsulado em uma classe do tipo Controller e é indiferente para a classe Pawn. No próximo artigo abordaremos o uso da classe Controller.

    Existem diversas subclasses de Pawn existentes na Unreal Engine como pode ser visto na imagem abaixo. Podemos ver que a classe Pawn também é usada para a criação de veículos. As classes que começam com UT contém as implementações específicas para o jogo Unreal Tournament.


    Fiz um exemplo bem simples para ilustrar o uso da classe Pawn. Estendi a classe UTPawn para permitir que a minha nova classe Pawn possa ser inserida direto pelo editor através do uso do atributo placeable, adicionei um SkeletalMeshComponent (modelo 3D com animação) e defini os atributos mínimos necessários para o funcionamento. Por fim defini que essa minha classe será controlada por uma classe Controller do tipo UTBot.

    class TestePawn extends UTPawn
          placeable;
    
    defaultproperties
    {
       begin object class=SkeletalMeshComponent Name=Modelo3D                
          SkeletalMesh=CH_LIAM_Cathode.Mesh.SK_CH_LIAM_Cathode
          PhysicsAsset=CH_AnimCorrupt.Mesh.SK_CH_Corrupt_Male_Physics
          AnimSets(0)=CH_AnimHuman.Anims.K_AnimHuman_BaseMale
          AnimtreeTemplate=CH_AnimHuman_Tree.AT_CH_Human
       end object
    
       Components.Add(Modelo3D);
        
       ControllerClass=UTGame.UTBot
    }

    Abaixo temos uma imagem do meu teste usando essa classe e rodando no próprio Editor do UDK:

    Editor de Mapas 2D


    Este programa foi feito com o MRDX para ser usado no meu Trabalho de Conclusão de Curso. Consiste de um simples editor de mapas que demonstra os principais conceitos para a construção de cenários 2D. 

    Neste programa eu usei uma versão simplificada de uma Classe C++ que havia feito para representar cenários no ROGUE. O programa com código fonte está disponível neste arquivo: editor_mapa.zip

    segunda-feira, 3 de outubro de 2011

    Animação de Sprites


    Este programa foi feito com o MRDX para ser usado no meu Trabalho de Conclusão de Curso. Ele ilustra o uso de animação de sprites em jogos 2D. Você controla um lutador que pode executar diversos movimentos como soco, chute e pulo.

    Eu aproveitei uma Classe C++ que havia feito para controlar animação no ROGUE. O programa com código fonte está disponível neste arquivo: animacao.zip

    domingo, 2 de outubro de 2011

    Minicursos

    Em junho de 2001, eu ministrei o 1º curso sobre Programação de Jogos em Belém do Pará, como pode ser visto no certificado abaixo.

    Enquanto estive na Universidade Federal do Pará (UFPA) ministrei vários cursos sobre jogos, principalmente durante a Semana de Informática (SEMINF), evento que ocorria anualmente na UFPA.

    sábado, 1 de outubro de 2011

    Outras Apresentações

    Preparei uma apresentação sobre "Jogos e Software Livre" que usei no III Fórum Paraense de Software Livre. A apresentação está disponível neste arquivo : jogos_livre.pdf

    Para complementar o meu material didático sobre desenvolvimento de jogos, preparei uma breve apresentação sobre a história dos videogames que está disponível neste arquivo: historia_videogames.pdf

    sexta-feira, 30 de setembro de 2011

    Projeto JEDI

    Eu criei o projeto JEDI (Jogos Eletrônicos Distribuídos e Inteligentes) na UFPA para servir como um laboratório para as disciplinas de Inteligência Artificial e Sistemas Distribuídos com o objetivo de despertar o interesse da comunidade acadêmica de computação para o potencial da área de desenvolvimento de jogos.

    Produzi diversos materiais didáticos que estão disponíveis e descritos abaixo:
    • IA_jogos.pdf : Apresentação com 49 slides. Mostra algumas técnicas fundamentais de Inteligência Artificial como Máquina de Estados Finitos e Pathfinding. Depois comenta sobre características da IA em diferentes estilos de jogos. Encerra com dois estudos de caso : Um sobre a simulação dos sentidos e o outro sobre um jogo de corrida pela cidade.
    • jog_rede.pdf : Apresentação com 18 slides. Comenta sobre a implementação de Multiplayer de dois jogos bem conhecidos : Age of Empires e Unreal Tournament.
    • Fuzzy_Jogos.pdf : Artigo sobre a integração de lógica fuzzy ao desenvolvimento de jogos. Apresenta fundamentos essenciais e ferramentas para auxiliar o programador.

    quinta-feira, 29 de setembro de 2011

    ActorComponent (UnrealScript)

    ActorComponent é uma classe base usada para componentes com utilidades bem definidas que podem ser anexados a um Actor. Por exemplo, podemos ter um componente que define o modelo 3D (Mesh) de um Actor, também tem componentes relacionados a imagem, iluminação, audio, etc.

    Existe uma forma especial para criar um objeto de um componente dentro de um bloco DefaultProperties de uma classe Actor, como veremos no exemplo a seguir.

    Como 1º exemplo vou adicionar um StaticMesh a uma classe UnrealScript. Vá no ContentBrowser do UDK e escolha um StaticMesh, anote o Nome e o Path como pode ser visto na imagem abaixo.


    Depois crie uma classe simples que estende Actor conforme código abaixo:
    class TesteMesh extends Actor
          placeable;  //indica que esta classe pode ser inserida no cenário através do editor de níveis
         
    defaultproperties
    {
       begin object Class=StaticMeshComponent Name=Modelo3D
          StaticMesh = Pickups.UDamage.Mesh.S_Pickups_UDamage        
       end object
        
       Components.add(Modelo3D);
    }

    O par de comandos "Begin Object / End Object" permite a criação de um objeto informando a classe e o nome que o objeto terá no código. Dentro do bloco é possível definir os atributos do objeto. Neste exemplo apenas definimos o caminho e nome do StaticMesh que será usado pelo Actor. Após a criação do objeto adicionamos ele à lista de componentes do Actor.

    A imagem abaixo mostra como o nosso novo Actor é exibido no editor do UDK:


    Como 2º exemplo vamos criar um SpriteComponent (imagem 2d) para o Actor. Neste exemplo queremos que a imagem seja exibida no editor mas não apareça no jogo. Isto é muito útil para objetos invisíveis que contém apenas lógica de Inteligência Artificial, pois com a imagem fica fácil de localizá-lo no editor.

    Segue abaixo código e imagem do 2º exemplo.
    class TesteSprite extends Actor
          placeable;
         
    defaultproperties
    {
       begin object Class=SpriteComponent Name=Imagem2D
          Sprite = EditorResources.AIScript
          HiddenGame = true   //não exibir a imagem no jogo
       end object
        
       Components.add(Imagem2D);
    }
    


    Para mais informações sobre Actor Components:
    Actor Components