fSprite.Texture.Data <> nil ) then begin source := Sprite.CurrentFrame.Rect; destination.x := Round(self.Position.X); destination.y := Round(self.Position.Y); destination.w := 16; destination.h := 16; SDL_RenderCopy( fRenderer, fSprite.Texture.Data, @source, @destination) ; end;Bingo!Neste ponto o game deve estar executando sem erros e a animacao dos inimigos sendo exibida. Caso tenha dificuldades para fazer o game compilar, faca o download do codigo-fonte e use-o como referencia. E muito importante entender o que fizemos ate aqui antes de seguir para a proxima secao.Input de Teclado ( movendo e atirando )Ja implementamos a leitura do teclado para usarmos tecla ESC para fechar a janela do game. Vamos agora refinar o metodo TGame.HandleEvents para movimentar o jogardor pela tela usando as setas do teclado ou as teclas A e D, numa versao simplificada do esquema de movimentacao WASD. Mas antes de seguir, precisamos de um objeto para representar o jogador, capaz de mover-se pela tela para a esquerda ou direita, em resposta aos inputs do jogador.Observe a listagem abaixo e, depois de estuda-la um pouco, implemente-a em seu projeto.TGameObjectNotifyEvent = procedure(Sender: TGameObject) of object;TPlayerInput = ( Left, Right, Shot);TPlayer = class( TGameObject )privateconst DEFAULT_SPEED = 200.0; DEFAULT_COOLDOWN = 500;var fSpeed : real; fSprite : TSprite; fCooldown : integer; fCooldownCounter : integer; fInput : array[0..2] of boolean; fOnShotTriggered : TGameObjectNotifyEvent; function GetInput(index: integer): boolean; procedure SetInput(index: integer; AValue: boolean);public constructor Create(const aRenderer: PSDL_Renderer); override; destructor Destroy; override; procedure Draw; override; procedure Update(const deltaTime : real); override; property Input[index: integer] : boolean read GetInput write SetInput; property Sprite: TSprite read fSprite; property Speed : real read fSpeed write fSpeed; //em pixels por segund property Cooldown: integer read fCooldown write fCooldown; property CooldownCounter: integer read fCooldownCounter; property OnShotTriggered : TGameObjectNotifyEvent read fOnShotTriggered write fOnShotTriggered;end;(...){ TPlayer Implementation }{ TPlayer }function TPlayer.GetInput(index: integer): boolean;begin if index > Length(fInput)-1 then raise IndexOutOfBoundsException.CreateFmt('TPlayer.GetInput(%d)', [index]); result := fInput[index];end;procedure TPlayer.SetInput(index: integer; AValue: boolean);begin if index > Length(fInput)-1 then raise IndexOutOfBoundsException.CreateFmt('TPlayer.SetInput(%d)', [index]); fInput[index] := AValue;end;constructor TPlayer.Create(const aRenderer: PSDL_Renderer);var i : integer;begin inherited Create(aRenderer); fSprite := TSprite.Create; fSpeed := DEFAULT_SPEED; fCooldown := DEFAULT_COOLDOWN; fCooldownCounter:= 0; for i :=0 to High(fInput) do fInput[i] := false;end;destructor TPlayer.Destroy;begin fSprite.Free; inherited Destroy;end;procedure TPlayer.Draw;var source, destination : TSDL_Rect;begin if ( fSprite.Texture.Data <> nil ) then begin source := Sprite.CurrentFrame.Rect; destination.x := Round(self.Position.X); destination.y := Round(self.Position.Y); destination.w := 26; destination.h := 16; SDL_RenderCopy( fRenderer, fSprite.Texture.Data, @source, @destination) ; end;end;procedure TPlayer.Update(const deltaTime: real);begin if fInput[Ord(TPlayerInput.Left)] then Position.X:= Position.X - ( fSpeed * deltaTime ); if fInput[Ord(TPlayerInput.Right)] then Position.X:= Position.X + ( fSpeed * deltaTime ); if fCooldownCounter > 0 then dec(fCooldownCounter, round(deltaTime * MSecsPerSec)); if ( (fInput[Ord(TPlayerInput.Shot)]) and ( fCooldownCounter <= 0) ) then begin if Assigned(fOnShotTriggered) then fOnShotTriggered(self); fCooldownCounter := fCooldown; fInput[Ord(TPlayerInput.Shot)] := false; end;end;Note que, por especializar TGameObject, TPlayer se parece muito com TEnemy, o que e otimo pois nos ajuda a compreender seu codigo mais rapidamente. Veja tambem como TPlayer sabe como mover-se dentro do metodo Update: ele mantem, em um array, um valor booleano para cada entrada possivel ( Left, Right e Shot ) e, caso estes indiquem que ha um movimento a ser atualizado naquele instante, a posicao e atualizada multiplicando a velocidade pela