Notícias, artigos, tutoriais, etc sobre desenvolvimento de jogos eletrônicos



Oportunidade de trabalho no Instituto Nokia de Tecnologia (Recife - programação - Linux)

Essa saiu no Job4Dev (via Br-Linux):

Recife, PE: Desenvolvimento de Jogos em Instituto Nokia de Tecnologia

Para quem se interessar, no link acima tem os emails de contato pros interessados enviarem currículos. As vagas são para desenvolvimento de jogos em Linux! Bom, fiquei um pouco surpreso sobre a vaga, já que a maioria esmagadora de oportunidades aqui no Brasil são para ambiente Windows.

Boa sorte a todos que tentarem.

Novas ferramentas para gamedev by nVidia

Com a premissa de cada vez mais agilizar no processo de criação de jogos, a nVidia acaba de lançar um pacote de ferramentas para gamedev contendo 03 programas:

  • PerfHUD 6 – Ferramenta de análise de desempenho e depuração de gráficos para aplicações DirectX 9 e 10;
  • FX Composer 2.5 – Ambiente de desenvolvimento integrado para criação de efeitos visuais em tempo real;
  • Shader Debugger – Depuração e otimização de sombreadores escritos em HLSL, CgFX, COLLADA FX, CG em DirectX e OpenGL.

Dentre as empresas mais conhecidas que se utilizam das ferramentas criadas pela nVidia estão: Epic, Rockstar, Crytek e Blizzard.

Maiores detalhes podem ser conferidos clicando aqui.

Fonte:

Boletim do Clube do Hardware
Boletim 762 – 22/07/2008

Tutorial: XNA Invasores - Parte 9

Como prometido na última parte do tutorial, hoje vamos começar a implementar as colisões entre os objetos do jogo. Antes de fazer, vamos primeiro organizar melhor a classe JogoInvasores, implantando um sistema de estados para controlar o fluxo do jogo.

Vamos criar um novo arquivo com o nome EstadoJogo.cs e dentro dele adicionar o seguinte código:

namespace Tutorial_XNAInvasores
{
    public enum EstadoJogo
    {
        Menu, TelaDeJogo, GameOver, Encerrar, Invasao, Vitoria
    }
}

Este arquivo irá definir os possíveis estados do jogo: Menu (o jogo está apresentando o menu principal), TelaDeJogo (a tela principal do jogo), GameOver (o jogador perdeu), Encerrar ( o jogador pediu para sair do jogo), Invasao (as naves invadiram a área do jogador) e Vitoria (o jogador venceu).

Para começar a usar estes estados no jogo, primeiro é necessário declarar um atributo em JogoInvasores para armazenar o estado atual:

private EstadoJogo estado;

Feito isso, definimos o estado inicial do jogo no método Initialize.

Estado = EstadoJogo.Menu;

Repare que não estamos definindo o estado diretamente através do atributo, mas sim estamos usando uma propriedade “Estado”. Fazemos isso por que queremos que alguns métodos sejam chamados sempre que houver uma mudança de estado.

A propriedade “Estado” basicamente altera o estado e executa alguma ação extra dependendo do estado anterior.

private EstadoJogo Estado
{
    get { return estado; }
    set
    {
        switch (value)
        {
            case EstadoJogo.Menu:
                estado = value;
                break;
            case EstadoJogo.TelaDeJogo:
                estado = value;
                IniciarJogo();
                break;
            case EstadoJogo.GameOver:
                if (estado == EstadoJogo.TelaDeJogo)
                {
                    FinalizarJogo();
                }
                estado = value;
                break;
            case EstadoJogo.Invasao:
                if (estado == EstadoJogo.TelaDeJogo)
                {
                    FinalizarJogo();
                }
                estado = value;
                break;
            case EstadoJogo.Vitoria:
                if (estado == EstadoJogo.TelaDeJogo)
                {
                    FinalizarJogo();
                }
                estado = value;
                break;
            case EstadoJogo.Encerrar:
                estado = value;
                break;
        }
    }
}

Conforme você já deve ter reparado, esta propriedade faz chamadas a dois métodos que ainda não foram criados: IniciarJogo e FinalizarJogo. Estes são os métodos responsáveis por iniciar e finalizar a parte central do jogo.

Vamos então ver como fica IniciarJogo. Lembram que a gente adicionou os componentes NaveJogador e ControladorDeNaves dentro do método Update de JogoInvasor? Pois é, isto agora será feito por IniciarJogo, então podemos remover aquele código que marcamos como temporário. Para quem não se lembra, o código a ser removido é este:

    // Temporário: Cria e adiciona uma NaveJogador ao jogo
    if (naveJogador == null)
    {
        naveJogador = new NaveJogador(this, imagemJogador,
            new Vector2(300, 460));
        Components.Add(naveJogador);
    }

    // Temporário: Cria e adiciona um ControladorDeNaves ao jogo
    if (controladorDeNaves == null)
    {
        controladorDeNaves = new ControladorDeNaves(this);
        Components.Add(controladorDeNaves);
    }

IniciarJogo vai fazer praticamente a mesma coisa, com a diferença que não será preciso ficar verificando se o atributo é nulo (já que iremos chamar este método uma vez só, ao contrário de Update que era chamado várias vezes por segundo) e mais pra frente vamos instanciar também os escudos usando este método.

private void IniciarJogo()
{
    naveJogador = new NaveJogador(this, imagemJogador, new Vector2(300, 460));
    Components.Add(naveJogador);
    controladorDeNaves = new ControladorDeNaves(this);
    Components.Add(controladorDeNaves);
}

Para encerrar o jogo usamos o método FinalizarJogo. Nele, armazenamos a pontuação do jogador e depois removemos os componentes do jogo. A remoção dos componentes é uma tarefa muito simples, basta passar uma instância do componente para o método Components.Remove.

Novamente, neste método iremos remover também os escudos futuramente, mas por enquanto fica assim:

private void FinalizarJogo()
{
    ultimaPontuacao = naveJogador.Pontos;
    Components.Remove(naveJogador);
    naveJogador = null;
    Components.Remove(controladorDeNaves);
    controladorDeNaves = null;
}

O atributo ultimaPontuacao serve para armazenar a pontuação do jogador, mesmo depois que o componente for destruído. Ele é declarado assim:

private int ultimaPontuacao;

Agora voltamos ao método Update onde iremos definir o comportamento do jogo de acordo com seu estado atual. São três comportamentos distintos:

  • Se o estado for TelaDeJogo, chamamos o método AtualizarTelaDeJogo que contém a lógica central do jogo.
  • Se o estado for encerrar, finalizamos o jogo com o método Exit, da classe Game.
  • E se o estado for qualquer outro, chamamos o método ChecarOpcao que verifica a opção do usuário.
  • switch (estado)
    {
        case EstadoJogo.Menu:
        case EstadoJogo.GameOver:
        case EstadoJogo.Invasao:
        case EstadoJogo.Vitoria:
            ChecarOpcao();
            break;
        case EstadoJogo.TelaDeJogo:
            AtualizarTelaDeJogo();
            break;
        case EstadoJogo.Encerrar:
            Exit();
            break;
    }
    

    O método ChecarOpcao ficará responsável por verificar se o jogador deseja jogar novamente ou encerrar o jogo, mas nessa parte do tutorial não vamos nos preocupar com isto, portanto vamos apenas fazer com que este método altere o estado para “TelaDeJogo”.

    private void ChecarOpcao()
    {
        Estado = EstadoJogo.TelaDeJogo;
    }
    

    AtualizarTelaDeJogo é um método bem grande que verifica as colisões e checa o fim de jogo. Hoje vamos ver apenas algumas partes deste método. Já que ainda não estamos trabalhando com os escudos, algumas verificações ainda não serão feitas.

    Repare que vamos utilizar uma classe adicional, chamada GerenciadorDeColisao, para testar colisões entre os objetos do jogo. Esta é uma classe que contém apenas métodos estáticos e sua declaração será vista mais abaixo.

    private void AtualizarTelaDeJogo()
    {
    }

    Começamos o método testando se o tiro do jogador não é nulo e depois verificando sua colisão com as naves do ControladorDeNaves.

    if (naveJogador.Tiro != null)
    {
        if (GerenciadorDeColisao.ChecarColisao(controladorDeNaves,
            naveJogador.Tiro))
        {
            naveJogador.Pontos += 100;
            naveJogador.Tiro.Destruir = true;
        }
    }
    

    Depois fazemos o processo inverso, verificando a colisão do tiro das naves com a nave do jogador.

    if (controladorDeNaves.Tiro != null)
    {
        if (GerenciadorDeColisao.ChecarColisao(naveJogador,
            controladorDeNaves.Tiro))
        {
            naveJogador.Vidas--;
            controladorDeNaves.Tiro.Destruir = true;
        }
    }
    

    O resto do método diz respeito ao fim do jogo.

  • Se a quantidade de vidas restantes do jogador for menor que 0, mudamos o estado do jogo para “GameOver”.
  • Se as naves tiverem invadido o espaço do jogador, mudamos o estado para “Invasao”.
  • E se as naves inimigas acabaram, o jogador venceu e mudamos o estado para “Vitoria”.
  • if (naveJogador.Vidas < 0)
    {
        Estado = EstadoJogo.GameOver;
    }
    else if (controladorDeNaves.Invadiu)
    {
        Estado = EstadoJogo.Invasao;
    }
    else if (controladorDeNaves.QuantidadeDeNaves == 0)
    {
        Estado = EstadoJogo.Vitoria;
    }
    

    Por fim, se o jogador apertar ESC a partida atual termina com uma derrota. É claro que esta é uma opção muito simples, um aperfeiçoamento do jogo seria exibir uma tela de pause ou pelo menos de confirmação quando ESC for pressionado.

    if (Keyboard.GetState().IsKeyDown(Keys.Escape))
    {
        Estado = EstadoJogo.GameOver;
    }
    

    Agora já temos quase tudo pronto, falta apenas criar a classe GerenciadorDeColisao. Crie uma nova classe no projeto com este nome:

    namespace Tutorial_XNAInvasores
    {
        class GerenciadorDeColisao
        {
        }
    }
    

    Precisaremos de três versões diferentes do método VerificarColisao. A primeira irá verificar colisão entre as naves do ControladorDeNaves e um Tiro, a segunda irá testar se um Tiro colidiu com a NaveJogador e a última testa colisão entre um retângulo e um Tiro.

    Vamos à implementação do primeiro método:

    public static bool ChecarColisao(ControladorDeNaves controladorDeNaves,
        Tiro tiro)
    {
        NaveInvasor[,] naves = controladorDeNaves.Naves;
        for (int x = 0; x < ControladorDeNaves.NAVES_X; x++)
        {
            for (int y = 0; y < ControladorDeNaves.NAVES_Y; y++)
            {
                if (naves[x, y] != null)
                {
                    if (naves[x, y].ObterRetangulo().Intersects(
                        tiro.ObterRetangulo()))
                    {
                        naves[x, y] = null;
                        controladorDeNaves.QuantidadeDeNaves--;
                        return true;
                    }
                }
            }
        }
         return false;
    }
    

    Aqui estamos percorrendo todas as naves do ControladorDeNaves e verificando se elas não são nulas, ou seja, se já não foram destruídas. Caso não sejam, utilizamos o método Intersects da classe Rectangle para verificar se os retângulos da nave e do tiro se sobrepõem. Se isto acontecer, destruímos a nave em questão, reduzimos a contagem de naves restantes no controlador, e retornamos verdadeiro para que o jogo saiba que o tiro deve ser destruído e pontos devem ser adicionados ao total do jogador.

    A segunda implementação deste método verifica a colisão do Tiro dos invasores com a NaveJogador:

    public static bool ChecarColisao(NaveJogador naveJogador, Tiro tiro)
    {
        if (tiro.ObterRetangulo().Intersects(naveJogador.ObterRetangulo()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    

    Temos um método bem mais simples aqui. Como há apenas uma NaveInvasor e um Tiro pra testar, não há necessidade de iterar por nenhuma lista. Apenas testamos então a interseção entre os retângulos dos dois objetos.

    Por fim, mais simples ainda, o teste que será utilizado futuramente para a colisão com os escudos:

    public static bool ChecarColisao(Rectangle caixa, Tiro tiro)
    {
        if (caixa.Intersects(tiro.ObterRetangulo()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    

    Repare que o funcionamento é basicamente o mesmo da segunda versão, com a diferença que aqui recebemos diretamente uma instância de Rectangle, ao invés de obtê-lo dentro do método. Isto foi feito só para demonstrar algumas formas diferentes de trabalhar com a colisão.

    Para fechar, precisamos apenas adicionar umas propriedades em ControladorDeNaves para que seja possível acessar seus atributos “tiro”, “invadiu”, “quantidadeDeNaves” e “naves”. Feito isso, já podemos compilar e rodar o jogo.

    public Tiro Tiro
    {
        get { return tiro; }
    }
    
    public bool Invadiu
    {
        get { return invadiu; }
    }
    
    public int QuantidadeDeNaves
    {
        get { return quantidadeDeNaves; }
        set { quantidadeDeNaves = value; }
    }
    
    public NaveInvasor[,] Naves
    {
        get { return naves; }
    }
    

    Veja que agora você já pode destruir as naves inimigas e qualquer evento que leve ao fim do jogo (como destruir todos os inimigos ou pressionar ESC) simplesmente faz com que ele reinicie. É claro que vamos mudar isso em breve. Até a próxima.

    Não deixem de conferir o projeto completo aqui.

    Nova versão da Irrlicht

    Segundo uma nota no site da UniDev (que, aliás, agora também tem RSS. Vale a pena acompanhar também), saiu uma nova versão da engine Irrlicht. Para mais informações:

    Irrlicht 1.4.1 Released

    Como a Ogre3D, a Irrlicht é uma das engines mais usadas pelos desenvolvedores indie, por ser opensource e por ter um bom poder de fogo. Com certeza essas duas engines eu poderia usar caso conhecesse bem o C++ (que, aliás, tenho de começar a estudar para não ficar para trás na corrida de conseguir entrar na área de gamedev).

    Engine Unity avaliada para Wii

    Para aqueles que querem aprender a desenvolver para o Nintendo Wii, uma boa alternativa é a Unity , uma engine com custo muito baixo (de 400 a 2 mil dólares) e capaz de criar tanto jogos baseados em web (jogar na internet) quanto jogos para PC e Mac.

    Segundo o Develop , a engine agora está autorizada como ferramenta third-party (ferramenta de terceiros) para o console. Ainda segundo a matéria, ela "tem uma classe simples de scripting para o WiiRemote e uma expansão da ShaderLab, conseguindo vantagens do chip gráfico do console" (tradução livre)

    Conheci a engine durante a entrevista que fiz com o Israel Mendes , da Aquiris, que falou um pouco sobra e engine e a mesma demonstra ter um bom poder gráfico. Caso queira conhecer, jogue o game O Formato é Só um Detalhe .

    Engine Havok disponível para download!

    Esta é para começar bem a semana. Alguns meses atrás, postei no meu blog pessoal sobre a disponibilização da Havok de graça para os desenvolvedores. E só agora que eles colocaram a engine para download:

    Try Havok™ - Registration

    Bom, analisando as páginas, é necessário um registro no site para fazer o download. Vou tentar baixar durante a semana

    Sei que não sei nada da engine, mas pode ser uma boa tentar aprender a usar ela. Muitas produtoras grandes usam ela em seus projetos de games, e ter conhecimento dela pode ajudar a conseguir uma vaga na indústria!

    É claro que se ela exigir o C++, terei de aprender a linguagem antes. Se alguém aqui baixar ela, gostaria que postasse nos comentários (ou no seu blog, se você tiver) alguns comentários a respeito da engine.

    [Via videogaming247 ]

    Tutorial: XNA Invasores - Parte 8

    Hora de implementar as naves inimigas para que a gente possa treinar um pouco a pontaria. Vamos começar adicionando uma nova classe chamada NaveInvasor que herda da classe Nave, feita nas partes anteriores do tutorial.

    Esta classe é bem simples. Precisamos apenas criar um construtor onde definiremos sua direção e velocidade inicial, e sobrescrever o método Update para dizermos como a nave deve ser atualizada.

    Começamos com o construtor. Simplesmente definimos a direção inicial como Direita (o que significa que inicialmente todas as naves estarão se deslocando para a direita) e a velocidade de deslocamento como 20 (ou seja, as naves irão se mover 20 pixels por segundo na direção atual).

    public NaveInvasor(JogoInvasores jogo, Texture2D imagem, Vector2 coord)
        : base(jogo, imagem, coord)
    {
        direcao = Direcao.Direita;
        velocidade = 20.0f;
    }

    O método Update irá mover a nave na direção desejada, da mesma forma que foi feito com NaveJogador (na verdade, esta parte é igual nas duas classes e deveria ter aparecido na classe Nave, mas como isto só foi visto agora, vamos deixar assim mesmo).

    public override void Update(GameTime gameTime)
    {
        coord += new Vector2(velocidade * jogo.DeltaTempo * (int)direcao, 0);
        base.Update(gameTime);
    }

    Para finalizar esta classe, vamos criar um método que deve ser chamado quando uma das naves atinge um dos cantos da tela. Quando isto ocorre, todas as naves se deslocam um pouco para baixo e invertem sua direção.

    public void InverterDirecao()
    {
        coord += new Vector2(0, 10);
        if (direcao == Direcao.Direita)
        {
            direcao = Direcao.Esquerda;
        }
        else if (direcao == Direcao.Esquerda)
        {
            direcao = Direcao.Direita;
        }
    }

    Agora, para adicionar as naves invasoras no jogo, vamos criar um controlador que será responsável por criar, atualizar e destruir estas naves. Adicione uma nova classe no projeto com o nome de ControladorDeNaves e faça com que ela herde de DrawableGameComponent. Faremos isto, pois ControladorDeNaves é que será adicionado como um componente ao jogo, ao invés de adicionarmos cada NaveInvasor individualmente. Isto ajuda em obter um melhor desempenho, uma vez que o XNA não precisará cuidar de dezenas de componentes.

    Criada a classe, adicionamos seus atributos. Como sempre, começamos com uma instância de JogoInvasores:

    private JogoInvasores jogo;

    Em seguida criamos uma matriz que irá conter as naves criadas pelo controlador. Para quem não está muito familiarizado com o C#, é possível fazer a declaração de uma matriz bidimensional sem especificar o tamanho de nenhuma de suas dimensões escrevendo “[,]” após o tipo da matriz. Faremos isto, pois os limites da matriz serão definidos mais à frente, quando criarmos as naves.

    private NaveInvasor[,] naves;

    Duas texturas são necessárias para NaveInvasor: a textura da própria nave e a textura do tiro da nave. Para armazenar estas texturas, criamos mais dois atributos no controlador:

    private Texture2D imagemNave;
    
    private Texture2D imagemTiro;

    O controlador de naves será responsável por verificar se uma nave atingiu os limites da tela e ordenar a todas elas que invertam sua direção. Para fazer este controle, utilizamos um atributo que diz se as naves devem ou não inverter sua direção.

    private bool inverterDirecao;

    Utilizamos uma variável para contar o tempo que falta para que as naves possam atirar, impedindo que elas fiquem atirando direto, sem nenhum intervalo.

    private float tempoParaAtirar;

    Quando for permitido que as naves atirem (ou seja, o tempo para atirar tiver passado) faremos um teste baseado num número aleatório para definir qual nave irá atirar (veremos isto mais abaixo). Para gerar números aleatório, vamos criar uma instância da classe Random provida pelo .Net Framework justamente para lidar com números randômicos.

    private Random random;

    Finalizando a parte dos tiros, quando um for disparado precisaremos armazenar uma nova instância de Tiro, então criaremos um atributo para isto. Assim como foi feito com NaveJogador, apenas um tiro por vez poderá separado, portanto não há necessidade de se criar um vetor ou uma lista, apenas uma variável simples já basta.

    private Tiro tiro;

    Durante sua execução, o Jogo pode acabar de várias formas, mas apenas duas delas estão diretamente relacionadas com as naves invasoras: todas as naves foram destruídas (vitória do jogador) ou as naves ultrapassaram um limite e invadiram a defesa (derrota do jogador). Para controlar isto, teremos um atributo que conta a quantidade de naves restantes no Jogo e um outro que indica se alguma nave conseguiu invadir a área do jogador.

    private int quantidadeDeNaves;
    
    private bool invadiu;

    Para terminar os atributos de ControladorDeNaves, vamos adicionar duas constantes que irá definir a quantidade de naves no eixo X e Y.

    public const int NAVES_X = 10;
    
    public const int NAVES_Y = 5;

    Agora criamos o construtor. Primeiramente armazenamos a instância de JogoInvasores, definimos o atraso inicial do primeiro tiro (tempoParaAtirar), instanciamos o gerador de números aleatórios e definimos tiro como null (isto é importante, pois um dos requisitos para a criação de um novo tiro é que este atributo seja nulo, o que significa que não existe outro tiro na tela disparado por uma nave invasora).

    public ControladorDeNaves(JogoInvasores jogo)
        : base(jogo)
    {
        this.jogo = jogo;
    
        tempoParaAtirar = 2.0f;
        random = new Random();
        tiro = null;

    E depois finalizamos definindo a quantidade inicial de naves como sendo NAVES_X * NAVES_Y, ou seja, cinqüenta naves, e dizendo ao jogo que a invasão ainda não ocorreu (invadiu = false).

    quantidadeDeNaves = NAVES_X * NAVES_Y;
    invadiu = false;

    Continuando, vamos carregar as texturas da nave e do tiro sobrescrevendo o método LoadContent do ControladorDeNaves. Perceba que desta vez vamos carregar as texturas diretamente no componente, ao invés de carregá-lo em JogoInvasores e passar para o ControladorDeNaves como parâmetro. Fizemos isto porque o jogo terá um único controlador de naves, o que significa que uma mesma textura não será carregada várias vezes, portanto podemos deixar que o próprio controlador carregue sua textura.

    Uma vez que a textura da nave já foi carregada, podemos então instanciar as naves do jogo. Para isso, vamos fazer uma chamada ao método CriarNaves dentro de LoadContent, fazendo com que as naves sejam criadas imediatamente após o carregamento das texturas.

    protected override void LoadContent()
    {
        imagemNave = jogo.Content.Load("imagemNaveInvasor");
        imagemTiro = jogo.Content.Load("imagemTiroJogador");
        CriarNaves();
    
        base.LoadContent();
    }

    Ok, estamos carregando as imagems, mas para que elas possam realmente ser carregadas precisamos adicioná-las ao projeto. Vá em Add -> Existing Item… e adicione estes dois arquivos ao seu projeto: imagemTiroInvasor.png e imagemNaveInvasor.

    A implementação do método CriarNaves fica da seguinte forma: alocamos memória para a matriz que tinha sido declarada anteriormente e depois fazemos um laço por todos os elementos desta matriz criando novas instâncias de NaveInvasor, passando suas coordenadas e a imagem.

    O método Update é bem grandinho, então vamos por partes para não confundir. Primeiramente, atualizamos o tempoParaAtirar diminuindo seu valor de acordo com o deltaTempo.

    public override void Update(GameTime gameTime)
    {
        tempoParaAtirar -= jogo.DeltaTempo;

    Depois, fazemos um laço por todas as naves do controlador usando a palavra-chave do C# “foreach”. Dentro deste laço vamos manipular cada uma das naves contidas na matriz naves.

    Antes de mais nada, verificamos se a nave não é nula. Caso seja nula, nada é feito. Caso não seja, chamamos o método Update da nave atual.

    if (nave != null)
    {
        nave.Update(gameTime);

    Agora verificamos se a nave chegou perto demais de um dos lados da tela e ativar o flag inverterDirecao caso isto ocorra. No nosso caso, consideramos que uma nave chegou perto demais de um dos lados se ela estiver a menos de 10 pixels do canto da tela.

    if (nave.Coord.X < 10 && nave.Direcao == Direcao.Esquerda)
    {
        inverterDirecao = true;
    }
    else if (nave.Coord.X + imagemNave.Width > JogoInvasores.LARGURA - 10
             && nave.Direcao == Direcao.Direita)
    {
        inverterDirecao = true;
    }

    Na sequencia verificamos se a nave invadiu o espaço do jogador, o que aqui significa ter sua coordenada Y maior que 340.

    if (nave.Coord.Y > 340)
    {
        invadiu = true;
    }

    Para finalizar este laço, testamos se o tempo para atirar já passou e se não há nenhum outro tiro na tela (tiro == null). Em caso positivo, geramos um número aleatório entre 0 e 1000 e verificamos se ele é menor que 30. Se for, um novo tiro é criado logo abaixo da nave atual. Este teste com o número aleatório existe para permitir que todas as naves atirem. Quando um novo tiro é criado, não podemos esquecer de reinciar o contador tempoParaAtirar.

    if (tempoParaAtirar < 0.0f && tiro == null)
    {
        if (random.Next(1000) < 30)
        {
            tiro = new Tiro(jogo, imagemTiro, new Vector2(
                nave.Coord.X + imagemTiro.Width / 2, nave.Coord.Y),
                200.0f);
            tempoParaAtirar = 2.0f;
         }
    }

    Antes de prosseguir, vamos fechar dois blocos de código que ainda estão abertos.

        }
    }

    Fechamos estes blocos mas ainda há coisas a serem feitas em Update. Primeiramente, caso o flag inverter direção esteja ativo, vamos fazer uma nova iteração por todas as naves chamando seu método inverterDirecao. Estamos fazendo este novo laço ao invés de fazê-lo dentro do laço anterior para garantir que todas as naves tenham sua direção invertida. Caso isso fosse feito diretamente no outro laço, as naves atualizadas antes da ativação do flag inverterDirecao não seriam afetadas pelo flag.

    if (inverterDirecao)
    {
        foreach (NaveInvasor nave in naves)
        {
            if (nave != null)
            {
                nave.InverterDirecao();
            }
        }
            inverterDirecao = false;
    }

    Por fim, caso um tiro tenha sido disparado, ele é atualizado. Se o tiro tiver ativado seu flag destruir (ao sair da tela ou atingir um objeto) atribuímos null a ele e o removemos da memória.

    if (tiro != null)
    {
        tiro.Update(gameTime);
    
        if (tiro.Destruir)
        {
            tiro = null;
        }
    }

    Terminamos o método Update chamando base.Update e fechando as últimas chaves abertas.

    base.Update(gameTime);
    }

    O método Draw é muito simples. Primeiramente iteramos por todas as naves e verificamos se a nave corrente é nula, e caso não seja, a desenhamos. Depois terminamos o método desenhando o tiro caso ele não seja nulo.

    public override void Draw(GameTime gameTime)
    {
        foreach (NaveInvasor nave in naves)
        {
            if (nave != null)
            {
                nave.Draw(gameTime);
            }
        }
    
        if (tiro != null)
        {
            tiro.Draw(gameTime);
        }
    
        base.Update(gameTime);
    }

    Para adicionar o controlador de naves ao jogo e permitir que as naves apareçam na tela, vamos usar o mesmo processo que fizemos antes com NaveJogador. Iremos criar uma variável controladorDeNaves e instanciá-lo temporariamente no método Update de JogoInvasores. Mais à frente quando implementarmos o sistema de estados do Jogo, a criação de controladorDeNaves irá aparecer em um lugar mais conviniente.

    Declare a variável dentro da classe JogoInvasores da seguinte forma:

    private ControladorDeNaves controladorDeNaves;

    Depois, adicione o seguinte trecho de código no método Update.

    // Temporário: Cria e adiciona um ControladorDeNaves ao jogo
    if (controladorDeNaves == null)
    {
        controladorDeNaves = new ControladorDeNaves(this);
        Components.Add(controladorDeNaves);
    }

    Agora já temos as naves inimigas voando pelo cenário e atirando contra o jogador. Mas por hora nenhuma colisão está sendo feita e os tiros irão simplesmente passar direto pelos personagens. Pior que isso, nenhuma verificação está sendo feita para determinar se o jogo acabou, portanto se você deixar o jogo rodando por um tempo as naves eventualmente irão sair pela parte de baixo da tela.

    Nas próximas partes tanto os cálculos de colisão quanto as verificação de fim de jogo serão adicionadas. Enquanto isso, vocês podem testar o jogo baixando o projeto aqui.

    Até mais.

    Jogo com as Naves Invasoras!

    Um pouco sobre engines

    Bom, uma das ferramentas essenciais na criação de games são as engines. Elas que são responsáveis pela geração dos gráficos do jogo além de ligar os modelos e cenários a rotinas de programação (caso a equipe tenha usado uma linguagem externa) como lógicas de inteligência artificial e scripts do roteiro. Então o Develop montou um artigo sobre as principais engines do mercado:

    Horsepower for courses

    Vale a pena conferir apenas como curiosidade, já que estas engines estão inacessíveis para nós, desenvolvedores indie, por causa do custo de cada uma (que, aliás, nem estão precificadas no artigo. Tem que consultar as empresas). Dizem que a Unreal Engine 3 custa pelo menos 1 milhão de dólares só para licenciá-la.

    Outro artigo, que deve ser levado em consideração (também do Develop) é este:

    Gentlemen, Start Your Engines

    Mostra as diferenças entre adquirir/usar ume engine pronta e desenvolver internamente (in-house).

    Vou procurar depois sobre engines mais baratas (e freeware/opensource) e postar aqui no GamedevBR.

    [Via Develop 1 e 2]

    Tutorial: XNA Invasores - Parte 7

    Logo do XNADe volta com o tutorial XNA Invasores e é hora de adicionar um pouco de ação ao jogo. Hoje vamos dar poder de fogo à nossa nave.

    Iniciamos com a criação da classe Tiro que irá representar os tiros disparados pelo Jogador (e futuramente pelas naves inimigas). Como sempre, criamos uma nova classe (Add -> Class…) e damos a ela o nome de Tiro. Como esta classe também é um Drawable Game Component, fazemos com que ela herde de Entidade (assim com foi feito com a classe Nave).

    Read more

    Preview da versão 3.0 do XNA

    Foi lançado ontem o Community Technical Preview do XNA Game Studio 3.0.

    As novidades desta nova versão incluem suporte ao Visual Studio 2008 e a possibilidade de se rodar os jogos no Zune. Além disso, há melhorias na API do Framework no suporte a multimídia e efeitos sonoros.

    O suporte ao XBOX 360 foi removido desta versão de testes e estará de volta na versão final. Talvez por isso mesmo, o tamanho do download caiu de 98.6 MB da versão 2.0 para 41.7 MB no 3.0 (veremos se o motivo da redução é esse mesmo quando a versão final sair).

    Para fazer o download e saber mais detalhes, clique aqui.

    Fonte: Meiobit Games.

    Agradecimentos ao Rodrigo pela dica.

    « Previous PageNext Page »