Estou desenvolvendo um jogo 2D de batalha espacial

public class Container extends JFrame{

public Container() {
    add(new Fase());
    setTitle("Meu Jogo");
    setSize(1920, 1080);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    this.setResizable(true);
    setVisible(true);
    
}

public static void main(String[] args) {
    new Container();
}

}

package meuJogo.modelo;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Fase extends JPanel implements ActionListener{
private Image fundo;
private Player player;
private Timer timer;

public Fase(){
    setFocusable(true);
    setDoubleBuffered(true);
    
    ImageIcon referencia = new ImageIcon("res\\background.jpg");
    fundo = referencia.getImage();
    
    Player player = new Player();
    player.load();
    
    addKeyListener(new TecladoAdapter());
    
    timer = new Timer(5, this);
    timer.start();
}

@Override
public void paint(Graphics g){
    Graphics2D graficos = (Graphics2D) g;
    graficos.drawImage(fundo, 0, 0, null);
    graficos.drawImage(player.getImagem(), player.getX(), player.getY(), this); (O java lança uma NullPointException nessa linha)
    g.dispose();
}

@Override
public void actionPerformed(ActionEvent e) {
     player.update(); (NullPointException aqui)
     repaint();
}

public class TecladoAdapter extends KeyAdapter{
    
    public void KeyPressed(KeyEvent e){
        player.keyPressed(e);
    }
    
    public void KeyReleased(KeyEvent e){
        player.keyRelease(e);
    }

}
}

package meuJogo.modelo;

import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;

public class Player {
private int x, y;
private int dx, dy;
private int altura, largura;
private Image imagem;

public Player(){
    this.x = 100;
    this.y = 100;
    
}

public void load(){
    ImageIcon referencia = new ImageIcon("res\\spaceship.png");
    imagem = referencia.getImage();
    altura = imagem.getHeight(null);
    largura = imagem.getWidth(null); 
}

public void update(){
    y += dy;
    x += dx;
}

public void keyPressed(KeyEvent tecla){
   int codigo = tecla.getKeyCode();
   
   if(codigo == KeyEvent.VK_W){
       dy = -3;
   }
   
   if(codigo == KeyEvent.VK_S){
       dy = 3;
   }
   
   if(codigo == KeyEvent.VK_A){
       dy = -3;
   }
   
   if(codigo == KeyEvent.VK_D){
       dy = 3;
   }
}

public void keyRelease(KeyEvent tecla){
   int codigo = tecla.getKeyCode();
   
   if(codigo == KeyEvent.VK_W){
       dy = 0;
   }
   
   if(codigo == KeyEvent.VK_S){
       dy = 0;
   }
   
   if(codigo == KeyEvent.VK_A){
       dy = 0;
   }
   
   if(codigo == KeyEvent.VK_D){
       dy = 0;
 
    }

}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public Image getImagem() {
    return imagem;
}

}

Quando executo, aparece só a janela branca; como se não tivesse nada programado.
Alguém pode me ajudar?

Em componentes Swing você deve sobrescrever o método paintComponent e não o paint.

obrigado pela ajuda, mas eu fiz isso e continua executando a tela limpa…

Quando você cria o Player no construtor da classe Fase, ele é uma variável local dentro do construtor, não o atributo player declarado no topo da classe Fase. Isso faz com que o player que você está tentando acessar dentro do paint e do actionPerformed esteja nulo.


public Fase(){
    setFocusable(true);
    setDoubleBuffered(true);
    
    ImageIcon referencia = new ImageIcon("spaceship.jpg");
    fundo = referencia.getImage();

    System.out.println("Criar Player");

    // Player player = new Player(); // remova essa linha
    player = new Player(); // deixe assim
    player.load();
    
    addKeyListener(new TecladoAdapter());
    
    timer = new Timer(5, this);
    timer.start();
}

Ademais, como o @staroski disse, é mais usual sobrescrever o paintComponent do JPanel nesse caso:

@Override
protected void paintComponent(Graphics g){
    super.paintComponent(g); // recomendado chamar o paintComponent da classe pai
    Graphics2D graficos = (Graphics2D) g;
    graficos.drawImage(fundo, 0, 0, null);
    graficos.drawImage(player.getImagem(), player.getX(), player.getY(), this);
    graficos.dispose(); // note que o dispose é no Graphics2D criado localmente
}

Por fim, em se tratando de Java Swing, é recomendado sempre criar sua tela usando a thread do Swing, para que os eventos do Swing sejam corretamente processados:

public static void main(String[] args){
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new Container();
        }
    });
}

Abraço.

DEU CERTO!!!
muito obrigado, amigo. Me ajudou bastante, viu?