The background of the Jpanel is grey - java

Im trying to create a game in java.
At the first I've made a Jpanel and made a FPS counter and the background was white.
after that I've tried to made that when you pressing ESCAPE, the Jpanel shutting down and then I've tried to make that when you pressing Space, the Background moving to green but after that, when Im trying to start, the Jpanel moving to grey background and the FPS counter has been disappear.
this is the frame class
public class Frame extends JFrame{
public static void main(String[] args) {
new Frame();
}
public Frame(){
new JFrame();
this.setTitle("Tower Defence - By Sahar Haine");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setExtendedState(MAXIMIZED_BOTH);
this.setUndecorated(true);
this.setResizable(false);
this.setVisible(true);
Screen screen = new Screen(this);
this.add(screen);
}
this is the screen class:
public class Screen extends JPanel implements Runnable{
Thread thread = new Thread(this);
Frame frame;
private int fps = 0;
public int scene;
public Boolean running = false;
public Screen(Frame frame){
this.frame = frame;
this.frame.addKeyListener(new KeyHandller(this));
thread.start();
}
#Override
public void paintComponent(Graphics g){
g.clearRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
g.drawString(fps + "", 10, 10);
if(scene == 0){
g.setColor(Color.BLUE);
}else{
}
g.setColor(Color.GREEN);
g.fillRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
}
public void run() {
System.out.println("Success");
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = 0;
while(running){
try {
repaint();
frames++;
if(System.currentTimeMillis() - 1000>= lastFrame){
fps = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
Thread.sleep(1);
} catch (InterruptedException ex) {
Logger.getLogger(Screen.class.getName()).log(Level.SEVERE, null, ex);
}
}
System.exit(0);
}
public class KeyTyped{
public void keyESC(){
running = false;
}
public void keySPACE() {
scene = 1;
}
and this is the key handler class:
public class KeyHandler implements KeyListener{
private Screen screen;
private Screen.KeyTyped keyTyped;
public KeyHandler(Screen screen){
this.screen = screen;
this.keyTyped = this.screen.new KeyTyped();
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
System.out.println(e.getKeyCode());
if(keyCode == 27){
this.keyTyped.keyESC();
}
if(keyCode == 27){
this.keyTyped.keySPACE();
}
}
public void keyReleased(KeyEvent e) {
}

One issue that jumps out immediately is that you have hard coded "keyCode = 27" for both Escape and Space which is not correct. "27 - Escape" and "32 - Space". Rather than hard code the values, please use KeyEvent.VK_ESCAPE and KeyEvent.VK_SPACE as documented here, KeyEvents
EDIT 1
Also make certain that you draw the string AFTER the "Fill" or else the "Fill" will over paint the FPS string and you will not see it.

I think you have to call
super.paintComponent(g)
as the first line in your paintComponent method override.

Related

Repaint() not being called if i use ImageIO.read()

When i try to set value to BufferedImage called dinoImage in Dino.java in a constructor i just get a blank screen every time (second picture) because repaint() is not being called, but if i set it to null it is working just fine but without this image (first picture).
No exceptions, everything seems fine in this code, this problem appears when i try to set value to this field using static method getImage of Resource.java which uses this line of code ImageIO.read(new File(path)) and it causes that repaint() is not being called, i guess this line causes such weird behavior but i dont know how to solve it.
Main.java
public class Main {
public static void main(String[] args) {
GameWindow gameWindow = new GameWindow();
gameWindow.startGame();
}
}
GameWindow.java
public class GameWindow extends JFrame {
private GameScreen gameScreen;
public GameWindow() {
super("Runner");
setSize(1000, 500);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameScreen = new GameScreen();
add(gameScreen);
}
public void startGame() {
gameScreen.startThread();
}
}
GameScreen.java
public class GameScreen extends JPanel implements Runnable, KeyListener {
private Thread thread;
public static final double GRAVITY = 0.1;
public static final int GROUND_Y = 300;
private Dino dino;
public GameScreen() {
thread = new Thread(this);
dino = new Dino();
}
public void startThread() {
thread.start();
}
#Override
public void run() {
while(true) {
try {
Thread.sleep(20);
dino.updatePosition();
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawLine(0, GROUND_Y, getWidth(), GROUND_Y);
dino.draw(g);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("Key Pressed");
dino.jump();
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("Key Released");
}
}
Dino.java
public class Dino {
private double x = 100;
private double y = 100;
private double speedY = 0;
private BufferedImage dinoImage;
public Dino() {
dinoImage = getImage("data/dino.png");
}
public void updatePosition() {
if(y + speedY >= GROUND_Y - 100) {
speedY = 0;
y = GROUND_Y - 100;
} else {
speedY += GRAVITY;
y += speedY;
}
}
public void jump() {
if(y == GROUND_Y - 100) {
speedY = -5;
y += speedY;
}
}
public void draw(Graphics g) {
g.setColor(Color.BLACK);
g.drawRect((int)x, (int)y, 100, 100);
g.drawImage(dinoImage, (int)x, (int)y, null);
}
}
Resource.java
public class Resource {
public static BufferedImage getImage(String path) {
BufferedImage image = null;
try {
image = ImageIO.read(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
}
setSize(1000, 500);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameScreen = new GameScreen();
add(gameScreen);
Swing components need to be added to the frame BEFORE the frame is made visible. Otherwise the panel has a size of (0, 0) and there is nothing to paint.
The code should be something like:
gameScreen = new GameScreen();
add(gameScreen);
setSize(1000, 500);
setVisible(true);

Adding animations to JPanel

I've written some code that essentially animates a sequence of images and adds them to a frame when I run the file.
I want to implement a functionality where I can add this animation to two different areas of a JPanel that has a BorderLayout (North and West).
I want to do this using a button but I don't know how to do that. I am new to event handling and layout managers.
How do I go about this?
My code for the animation:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ImageSequence extends JPanel implements ActionListener {
private ImageSQPanel imageSQPanel;
private static int frameNumber = -1;
private Timer timer;
private void buildUI(Container container, Image[] arrows) {
int fps = 10;
int delay = 1000 / fps;
timer = new Timer(delay, this);
timer.setInitialDelay(0);
timer.setCoalesce(true);
imageSQPanel = new ImageSQPanel(arrows);
container.add(imageSQPanel, BorderLayout.CENTER);
}
private synchronized void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
public void actionPerformed(ActionEvent e) {
frameNumber++;
imageSQPanel.repaint();
}
class ImageSQPanel extends JPanel {
Image arrowAnimation[];
ImageSQPanel(Image[] arrowAnimation) {
this.arrowAnimation = arrowAnimation;
}
//Draw the current frame of animation.
public void paintComponent(Graphics g) {
super.paintComponent(g); //paint background
//Paint the frame into the image.
try {
g.drawImage(arrowAnimation[ImageSequence.frameNumber % 10], 0, 0, this);
} catch (ArrayIndexOutOfBoundsException e) {
//On rare occasions, this method can be called
//when frameNumber is still -1. Do nothing.
}
}
}
//Invoked only when this is run as an application.
public static void main(String[] args) {
Image[] waving = new Image[7];
for (int i = 1; i <= 7; i++) {
waving[i - 1] = Toolkit.getDefaultToolkit().getImage(
"/Users/sarthaksachdeva/Documents/IntelliJ Projects/Animation/src/images/Arrow" + i + ".png");
}
JFrame f = new JFrame("ImageSequenceTimer");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
ImageSequence controller = new ImageSequence();
controller.buildUI(f.getContentPane(), waving);
controller.startAnimation();
f.setSize(new Dimension(200, 200));
f.setVisible(true);
}
}

Screen Keeps Flickering when Drawing Graphics on Canvas (Java)

I am a complete noob to Java Graphics.
I wrote a simple "game" in which you control a box with the arrow keys
Here is the source code:
package com.thundercrust.graphics;
public class Drawings extends Canvas implements KeyListener, Runnable {
public static Thread thread;
public static Drawings draw;
private static final long serialVersionUID = 1L;
public static boolean running = false;
public static int x = 640;
public static int y = 320;
public static int bulletX = 0;
public static int bulletY = 0;
public static int direction = 2;
public static boolean fired = false;
public static boolean show = false;
public static String colorCode;
public static final int WIDTH = 1366;
public static final int HEIGHT = WIDTH / 16 * 9;
public static final String title = "A Moving Box!";
JFrame frame = new JFrame();
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.setColor(Color.black);
g2D.fillRect(0, 0, 1366, 768);
g2D.setColor(Color.pink);
g2D.fillRect(50, 50, 1266, 668);
if (colorCode.equals("red")) g2D.setColor(Color.red);
if (colorCode.equals("orange")) g2D.setColor(Color.orange);
if (colorCode.equals("yellow")) g2D.setColor(Color.yellow);
if (colorCode.equals("green")) g2D.setColor(Color.green);
if (colorCode.equals("blue")) g2D.setColor(Color.blue);
if (colorCode.equals("cyan")) g2D.setColor(Color.cyan);
if (colorCode.equals("gray")) g2D.setColor(Color.gray);
g2D.fillRect(x, y, 50, 50);
}
public Drawings() {
frame.addKeyListener(this);
frame.setTitle(title);
frame.setSize(WIDTH, HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
frame.add(this);
}
public void display() {
while (running = true) {
repaint();
try {
Thread.sleep(30);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) {
colorCode = JOptionPane.showInputDialog("Enter the color of the box: ");
running = true;
draw = new Drawings();
draw.start();
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) { y-= 5; direction = 0; }
if (keyCode == KeyEvent.VK_DOWN) { y+= 5; direction = 2; }
if (keyCode == KeyEvent.VK_LEFT) {x-= 5; direction = 3;}
if (keyCode == KeyEvent.VK_RIGHT) {x+= 5; direction = 1;}
if (keyCode == KeyEvent.VK_Z) System.out.println("You pressed z");
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (running = true) {
System.out.println("The Game is Running!");
repaint();
try {
Thread.sleep(60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
The reason why I am asking for help is because the application always flickers and it gets really annoying.
Is there any way to fix this?
Any help would be appreciated.
Canvas is not double buffered and I'd recommend not using it this way. Instead, consider using a JPanel and overriding it's paintComponent method, this will give you double buffering for free.
See Painting in AWT and Swing and Performing Custom Painting for some deatils
Alternatively, you could use BufferStrategy, which would allow you to define you own double buffering strategy as well as take complete control over the painting process, letting you control when the canvas was painted (AKA active painting)

Problems adding JButton to Window

I'm now having problems with JButton.
My JButton will not get added to the JFrame or the JPanel and it seems to be disabling paintComponent() from running.
Here's my JPanel class:
public class BLANKScreen extends JPanel implements Runnable {
Thread thread = new Thread(this);
public boolean running = false;
public static ImageIcon greenButton = new ImageIcon("resources/menubuttons/GreenButton.png");
public static JButton mainMenuPlayButton = new JButton("Play!", greenButton);
private int fps;
static BLANKWindow w;
public int scene = 0;
public void run() {
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = 0;
while (running) {
if (scene == 0) {
addMainMenuButtonsOptions();
} else if (scene == 1) {
}
repaint();
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
fps = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.exit(0);
}
public BLANKScreen (BLANKWindow w) {
this.w = w;
this.w.addKeyListener(new KeyHandler(this));
this.w.addMouseListener(new MouseHandler(this));
thread.start();
}
public class KeyTyped {
public void keyESC() {
running = false;
}
public void keySpace() {
if (scene == 0) {
scene = 1;
} else if (scene == 1) {
scene = 0;
}
}
}
public class MouseClicked { }
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0, 0, this.w.getWidth(), this.w.getHeight());
if (scene == 0) {
int nameLength = g.getFontMetrics().stringWidth("BLANK!");
g.drawString("BLANK!", w.getWidth() / 2 - nameLength / 2, w.getHeight() / 4);
}
}
public void addMainMenuButtonsOptions() {
mainMenuPlayButton.setActionCommand("/mainMenuPlayButton");
mainMenuPlayButton.addActionListener(new ActionHandler());
this.add(mainMenuPlayButton);
System.out.println("ThingHappend");
}
}
And the this.add(mainMenuPlayButton); also doesn't work when I say w.add(mainMenuPlayButton);.
Here's my JFrame class:
public class BLANKWindow extends JFrame {
public static void main(String[] args) {
new BLANKWindow();
}
public BLANKWindow() {
new JFrame();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
this.setSize(screenWidth, screenHeight);
this.setTitle("BLANK");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setResizable(true);
this.setExtendedState(MAXIMIZED_BOTH);
this.setVisible(true);
BLANKScreen dbs = new BLANKScreen(this);
this.add(dbs);
}
}
For some reason neither JButton nor paintCompenent() has decided to load?
Basically, at the core, you have a thread violation of the Swing API and seem to have a lack of knowledge about the basic workings of Swing...Welcome to the danger zone...
Swing is single threaded and is not thread safe. This means that you should block the EDT but you should also never interact with or modify any UI components from outside the EDT
You need to change your approach.
Swing already has an Event Queue, a thread for processing those events, the Event Dispatching Thread and uses a passive painting algorithm to update the UI (and layout managers).
You need to working within those constraints in order to make it all work.
Instead of trying to switch states within the BLANKScreen class, you should start with something like a "menu panel" and a "game panel".
The "menu panel" will display the menu options to the user, the "game panel" will display the game, it will have the game loop and painting logic for the actual game.
Now, you "could" get them to work together, but I think you need to break them apart to start with...it will only complicate the issues...
You could then use CardLayout to switch between them. This will make it easier to change state the visible states.
Also take a look at:
Painting in AWT and Swing
Concurrency in Swing
Updated...
The first rule Object Oriented Programming - separation of responsibilities. An object is responsible for it's job and only it's job. If you find that your object is trying to do more (like update the frames of game AND display a menu), then you're think along the wrong lines (especially in this case).
You need to separate the menu from the game. The game has a update mechanism, the menu doesn't care, it doesn't need it, it's updated by the core framework.
The game has user interaction of it's own, so does the menu, but they have different meanings to each other (a mouse click on the menu does something, but in the game it does something else), so they should separated...
The menu and the game probably don't even care about each other, so you could use a controller of some kind, which controls the state between two and facilitates the communication
This is a crude example, I would normally spend more time building better controllers and models, but this is designed to demonstrate the point of the concept, not provide a fully, out of the box, running solution...
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGameMenu {
public static void main(String[] args) {
new TestGameMenu();
}
public TestGameMenu() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MainView());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainView extends JPanel {
private MenuPane menuPane;
private GamePane gamePane;
public MainView() {
setLayout(new CardLayout());
menuPane = new MenuPane();
gamePane = new GamePane();
add(menuPane, "menu");
add(gamePane, "game");
menuPane.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ("play".equalsIgnoreCase(e.getActionCommand())) {
((CardLayout) getLayout()).show(MainView.this, "game");
gamePane.resume();
}
}
});
}
}
public class MenuPane extends JPanel {
private JButton playGame;
public MenuPane() {
setLayout(new GridBagLayout());
playGame = new JButton("Play My Awesome Game!");
playGame.setActionCommand("play");
add(playGame);
}
public void addActionListener(ActionListener listener) {
playGame.addActionListener(listener);
}
public void removeActionListener(ActionListener listener) {
playGame.removeActionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class GamePane extends JPanel {
private Point p;
private int xDelta;
private int yDelta;
private volatile boolean running = true;
private volatile boolean paused = true;
private ReentrantLock lckPause;
private Condition conPause;
public GamePane() {
p = new Point(100, 100);
do {
xDelta = (int)((Math.random() * 10) - 5);
} while (xDelta == 0);
do {
yDelta = (int)((Math.random() * 10) - 5);
} while (yDelta == 0);
lckPause = new ReentrantLock();
conPause = lckPause.newCondition();
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while (running) {
while (paused) {
lckPause.lock();
try {
conPause.await();
} catch (InterruptedException ex) {
} finally {
lckPause.unlock();
}
}
p.x += xDelta;
p.y += yDelta;
if (p.x < 0) {
p.x = 0;
xDelta *= -1;
} else if (p.x > getWidth()) {
p.x = getWidth();
xDelta *= -1;
}
if (p.y < 0) {
p.y = 0;
yDelta *= -1;
} else if (p.y > getHeight()) {
p.y = getHeight();
yDelta *= -1;
}
repaint();
if (running && !paused) {
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
}
}
}
}
});
t.start();
}
public void pause() {
if (!paused && running) {
lckPause.lock();
try {
paused = true;
} finally {
lckPause.unlock();
}
}
}
public void resume() {
if (paused && running) {
lckPause.lock();
try {
paused = false;
conPause.signalAll();
} finally {
lckPause.unlock();
}
}
}
public void stop() {
if (running) {
lckPause.lock();
try {
paused = false;
running = false;
conPause.signalAll();
} finally {
lckPause.unlock();
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(p.x - 5, p.y - 5, 10, 10);
g2d.dispose();
}
}
}
#MadProgrammer 's comment has the answer. But aside from the severe threading problem, you appear to be adding the button many times to the container. As far as I can tell, addMainMenuButtonsOptions is called every 2 ms until a key is pressed. (Although you don't show the code that uses the class KeyTyped, I assume you have bound it to a key listener?).
A smaller point: Have you considered using setBackground and setOpaque in the constructor, instead of calling clearRect manually?
Also as #Tom pointed out, it looks like you are using a default layout so the (multiple?) buttons will probably float to the top - is that what you want?

Start timer on keypress, java

I want to, when i click the "d" button, start a timer. This is to animate a player walking. The timer doesn't start when i press the key, how do i do that?
The code I have is this:
public class Game extends JPanel implements KeyListener {
//Player variables
private BufferedImage playerStanding;
private BufferedImage playerWalking;
private BufferedImage playerFrame;
private boolean walking = false;
private final int PLAYER_HEIGHT = 100;
private final int PLAYER_WIDTH = 100;
private final int INITIAL_X = 0;
private final int INITIAL_Y = 500;
private int x = INITIAL_X;
private int y = INITIAL_Y;
//The timer I want to start on keypress-> "d"
private Timer playerAnimationTimer;
public Game() {
setPreferredSize(new Dimension(800, 800));
setBackground(Color.CYAN);
//Player
try {
playerStanding = ImageIO.read(getClass().getResource("player1.gif"));
playerWalking = ImageIO.read(getClass().getResource("player2.gif"));
playerFrame = playerStanding;
}
catch (IOException ex) {
ex.printStackTrace();
}
playerAnimationTimer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
walking = !walking;
playerFrame = walking ? playerWalking : playerStanding;
x += 10;
if (x > getWidth() - PLAYER_WIDTH) {
x = INITIAL_X;
}
repaint();
}
});
playerAnimationTimer.setRepeats(true);
}
public Dimension setPreferredSize() {
return new Dimension(800, 800);
}
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D graphics2D = (Graphics2D) graphics;
if (playerFrame != null) {
graphics2D.drawImage(playerFrame, x, y, PLAYER_WIDTH, PLAYER_HEIGHT, this);
}
graphics2D.dispose();
}
#Override
public void keyTyped(KeyEvent e) {
//This doesn't work
playerAnimationTimer.start();
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}
//The class to hold the gamepanel
public class StartGame extends JFrame implements ActionListener {
private static JButton startGame = new JButton();
StartGame() {
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
setSize(200, 100);
setVisible(true);
setBackground(Color.BLUE);
setLocationRelativeTo(null);
startGame.setText("Play!");
startGame.setSize(100, 25);
startGame.addActionListener(this);
add(startGame);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == startGame) {
this.setVisible(false);
new GameWindow();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new StartGame();
}
});
}
}
How could I make the timer start when I click the "d" button?
Your KeyListener doesn't work because you never add the KeyListener to anything much less to a component that has focus, which is needed for a KeyListener to work.
I suggest that you instead use Key Bindings as a cleaner safer way to capture the desired key press.
As an aside, never dispose of a Graphics object that is given to you from the JVM.
For a better answer, please edit your code to make it comply with the mcve standard. You should use no images files, and it should compile and run for us unaltered.
You could set the private Timer like you did and start it like this...
public void startTimer(){
timer.start();
timer.setRepeats(true);
}

Categories