enemy.CanShot := (not TEnemy(Self.Items[i+20]).Alive) and (not TEnemy(Self.Items[i+40]).Alive) and (not TEnemy(Self.Items[i+60]).Alive) and (not TEnemy(Self.Items[i+80]).Alive); 2: enemy.CanShot := (not TEnemy(Self.Items[i+20]).Alive) and (not TEnemy(Self.Items[i+40]).Alive) and (not TEnemy(Self.Items[i+60]).Alive); 3: enemy.CanShot := (not TEnemy(Self.Items[i+20]).Alive) and (not TEnemy(Self.Items[i+40]).Alive); 4: enemy.CanShot := (not TEnemy(Self.Items[i+20]).Alive) ; end; end;end;Ja que temos 20 inimigos por linha, dispostos em um padrao retangular, se somarmos 20 ao indice de um inimigo qualquer, podemos referenciar o inimigo exatamente abaixo dele. Dependendo da linha em que estamos no grid, devemos fazer mais ou menos comparacoes. As naves posicionadas na sexta linha sempre podem atirar, ja que sao a primeira linha de ataque dos alienigenas.Para criar o projetil basta vamos o metodo TGame.doOnshot para criar uma instancia de TShot e configura-la de acordo com o emissor do evento, representado pelo parametro Sender.procedure TGame.doOnShot(Sender: TGameObject); procedure CreateShot(Position: TPoint; Direction: TShotDirection); var shot : TShot; begin shot := TShot.Create( fRenderer ); shot.Sprite.Texture.Assign( fTextures[ Ord(TSpriteKind.ShotA) ] ); shot.Sprite.InitFrames( 1,1 ); shot.Position := Position; shot.Position.X -= (shot.Sprite.CurrentFrame.Rect.w / 2); shot.OnCollided := @doOnShotCollided; shot.DrawMode := GetDrawMode; shot.Direction:= Direction; fShots.Add( shot ); end;begin if (Sender is TPlayer) then CreateShot(TPlayer(Sender).ShotSpawnPoint, TShotDirection.Up) else if (Sender is TEnemy) then CreateShot(TEnemy(Sender).ShotSpawnPoint, TShotDirection.Down);end;O mesmo vale para as checagens de colisao. Vamos estender TGame.CheckCollision para que ele tambem possa detectar colisoes entre o jogador e os tiros disparados pelas naves.procedure TGame.CheckCollision;var i : integer; shotList : TShotList; suspectList : TEnemyList;begin //check all shots going upwards with all alive enemies if (fShots.Count > 0) and ( fEnemies.Count > 0 ) then begin shotList := fShots.FilterByDirection( TShotDirection.Up ); suspectList := fEnemies.FilterByLife( true ); for i:=0 to Pred(shotList.Count) do TShot(shotList[i]).CheckCollisions( suspectList ); shotList.Free; suspectList.Free; end; //check all shots going downwards against the player if (fShots.Count > 0) then begin shotList := fShots.FilterByDirection( TShotDirection.Down ); for i:=0 to shotList.Count-1 do TShot(shotList[i]).CheckCollisions( fPlayer ); shotList.Free; end;end;Animacao de morteEvolucao da opacidade da explosaoao longo do tempoAo morrer, um inimigo simplesmente some da tela, gerando um efeito visual muito pobre. Vamos melhorar isto criando uma uma pequena animacao utilizando um sprite que ja carregamos para memoria mas ainda nao utilizamos. O sprite de explosao.A ideia aqui tambem nao e nova vamos criar um objeto TExplosion, descendente de TGameObject e uma classe para gerenciar todas as suas instancias. Exatamente como fizemos com os inimigos e com os tiros. Veja a implementacao desas duas classes no repositorio.O que queremos, e fazer com que um sprite de explosao seja exibido no lugar do inimigo abatido. Este sprite ficara visivel por um tempo e depois comecara a sumir gradualmente ate desaparecer por completo.Duas constantes governam este comportamento. LIFE_TIME define o tempo total em que a explosao sera visivel e START_FADE define em que ponto dentro de LIFE_TIME a explosao comecara a esmaecer. A opacidade e a visibilidade do sprite sao calculadas em TExplosion.Update. O grafico acima mostra em detalhes a evolucao de fOpacity em funcao do tempo.procedure TExplosion.Update(const deltaTime: real);var elapsed: UInt32; opacity : extended; fadeTime: integer;begin if fVisible then begin elapsed := SDL_GetTicks - fCreatedTicks; if elapsed > START_FADE then begin elapsed -= START_FADE; fadeTime:= LIFE_TIME-START_FADE; opacity := 255 - ((elapsed / fadeTime) * 255 ); opacity:= Round(opacity); opacity:=