fVisible := ( (Position.Y >= -(fSprite.Texture.H+1)) and (Position.Y < 600+1) ) and ( (Position.X >= -(fSprite.Texture.W+1)) and (Position.X < 800+1) ); end;end;Mais uma vez, ela segue o padrao dos outros descendentes de TGameObject, encapsulando o conhecimento necessario para desenhar e atualizar a si mesmo. No final do metodo update, verificamos se depois de mover-se o projetil continua dentro da area visivel do jogo e ajusta sua visibilidade de acordo.A implementacao de TShotList, logo abaixo, tambem e simples. Mantemos um array de TShot inicializado e vamos reutilizar as instancias invisiveis para representar os tiros que forem necessarios. Caso o seja solicitado um novo indice e nao houver nenhum disponivel, a funcao NextIndexAvailable retorna -1, indicando neste instante, nao ha mais tiros disponiveis, efetivamente limitando a quantidade de projeteis que podem estar visiveis na tela em um dado instante do gameplay, baseando-se no valor da constante MAX_SHOTS.{ TShotList }function TShotList.GetItems(index: integer): TShot;begin if index > High(fShots) then raise IndexOutOfBoundsException.CreateFmt('TShotList.GetItems(%d)', [index]); result := fShots[index];end;constructor TShotList.Create(const aRenderer: PSDL_Renderer; aTexture: TTexture);var i : integer;begin for i:= 0 to MAX_SHOTS-1 do begin fShots[i] := TShot.Create( aRenderer ); fShots[i].Visible:= false; fShots[i].Position.X := - 100; fShots[i].Position.Y := - 100; fShots[i].Speed := 250; //pixels por segundo fShots[i].Sprite.Texture.Assign(aTexture); fShots[i].Sprite.InitFrames(1,1); end;end;destructor TShotList.Destroy;var i: integer;begin for i:= 0 to MAX_SHOTS-1 do; fShots[i].Free;end;procedure TShotList.Update(const deltaTime: real);var i: integer;begin for i:= 0 to MAX_SHOTS-1 do fShots[i].Update(deltaTime); inherited;end;procedure TShotList.Draw;var i: integer;begin for i:= 0 to MAX_SHOTS-1 do fShots[i].Draw;end;function TShotList.NextIndexAvailable: integer;var i: integer;begin result := -1; for i:= 0 to MAX_SHOTS-1 do if not fShots[i].Visible then begin result := i; break; end;end;Com isto, finalizamos, por hora, os controles do jogador.Ainda ha o que melhorar. Veja, por exemplo, que e possivel move-lo para fora da area visivel e que manter pressionada a barra de espaco sem mover a nave fara com que varios tiros consecutivos sejam criados. Esta e uma otima oportunidade de exercita seu conhecimento ate aqui e implementar estas correcoes por conta propria. Nao se preocupe se tiver dificuldades, voltaremos a estas rotinas no futuro.Suporte a JoysticksGame rodando com suporte a joysticksNeste ponto, seu game deve estar executando sem problemas, com as animacoes dos inimigos rodando corretamente (muito embora eles ainda nao se movam) e o jogador se movendo e atirando. E importando garantir que tudo esta funcionando como o esperado porque vamos inicializar mais um subsistema da SDL e nao queremos adicionar um novo ponto de falha em um codigo ja quebrado.Aviso dado, vamos em frente.Para utilizar um joystick com a SDL e preciso inicializar o subsistema de controles atraves de uma chamada a SDL_Init, passado o flag SDL_INIT_JOYSTICK como parametro e depois adquirir um ponteiro para o dispositivo controlador atraves da funcao SDL_JoystickOpen.Comecaremos, entao, alterando o codigo de inicializacao do game para incluir SDL_INIT_JOYSTICK nos flags.procedure TGame.Initialize;var flags, result: integer;begin if ( SDL_Init( SDL_INIT_VIDEO or SDL_INIT_TIMER or SDL_INIT_JOYSTICK ) <> 0 )then raise SDLException.Create(SDL_GetError); fWindow := SDL_CreateWindow( PAnsiChar( WINDOW_TITLE ), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_SHOWN); if fWindow = nil then raise SDLException.Create(SDL_GetError); fWindowSurface := SDL_GetWindowSurface( fWindow ); if fWindowSurface = nil then raise SDLException.Create(SDL_GetError); fRenderer := SDL_CreateRenderer(fWindow, -1, SDL_RENDERER_ACCELERATED or SDL_RENDERER_PRESENTVSYNC); if fRenderer = nil