De-activating a key-listener

I am making keyboard input-based menus for a game and can’t figure out how to deactivate a keylistener after I’m done using it.

Also, i am trying to make it so that when I press tab while playing, it brings me back to the menu. I tried calling my menuInit method when tab is pressed, but nothing happens.

Here’s my code -

Main class

package main;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class Main extends Canvas implements Runnable
{
	private static final long serialVersionUID = 1L;
	
	public static final int WIDTH = 160;
	public static final int HEIGHT = 120;
	public static final int SCALE = 3;
	
	public int TITLE_FONT_SIZE = 15;
	
	public boolean running = false;
	public boolean mainMenu = false;
	
	public boolean startGame = false;
	public boolean startMenu = true;
	
	public int ticks = 0;
	public int frames = 0;
	public int fps = 0;
	private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
	long lastTimer1 = System.currentTimeMillis();
	long startingTime = lastTimer1;
	long score;

	
	Player p;
	
	public void start()
	{
		menuInit();
		new Thread(this).start();
	}
	
	public void stop()
	{
		running = false;
	}
	
	public void gameInit()
	{
		p = new Player();
		addKeyListener(new ALGame());
		setFocusable(true);
		mainMenu = false;
		running = true;
	}
	
	public void menuInit()
	{
		running = false;
		addKeyListener(new ALMenu());
		mainMenu = true;
	}
	
	public void run()
	{
		long lastTime = System.nanoTime();
		double unprocessed = 0;
		double nsPerTick = 1000000000.0 / 60.0;
		
		while (mainMenu)
		{
			render();
		}
		
		while (running)
		{
			long now = System.nanoTime();
			unprocessed += (now - lastTime) / nsPerTick;
			lastTime = now;
			boolean shouldRender = true;
			
			while (unprocessed >= .2)
			{
				tick();
				unprocessed -= .2;
				shouldRender = true;
			}
			
			Thread.yield();
			
			if (shouldRender)
			{
				frames++;
				render();
			}
			
			if (System.currentTimeMillis() - lastTimer1 > 1000)
			{
				lastTimer1 += 1000;
				System.out.println(ticks + " ticks, " + fps + " fps");
				fps = frames;
				frames = 0;
				ticks = 0;
			}
		}
	}
	
	public void tick()
	{		
		p.move();	
		
		ticks++;
	}
	
	public void render()
	{
		BufferStrategy bs = getBufferStrategy();
		
		if (bs == null)
		{
			this.createBufferStrategy(3);
			return;
		}
		
		Graphics g = image.getGraphics();
		Graphics2D g2d = (Graphics2D) g;
		
		super.paint(g);
		
		if (mainMenu)
		{
			g2d.setColor(Color.WHITE);			
			g2d.setFont(new Font("Times New Roman", 5, TITLE_FONT_SIZE));
			g2d.drawString("Play (space)", WIDTH / 2 - 45, HEIGHT / 2 - 10);
			g2d.drawString("Quit (esc)", WIDTH / 2 - 45, HEIGHT / 2 + 10);
		}
		
		if (running)
		{				
			g.fillRect(10, 10, WIDTH - 20, HEIGHT - 20);
			g2d.drawImage(p.getImage(), p.getX(), p.getY(), null);
		
			g2d.setFont(new Font("Arial", 5, 10));
			g2d.drawString(fps + "fps", 1, 9);
		}
		
		g.dispose();
		
		g = bs.getDrawGraphics();
		g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
		g.dispose();
		bs.show();
	}
	
	public static void main(String[] args)
	{
		Main game = new Main();
		game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		
		JFrame frame = new JFrame("Untitled");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		frame.add(game);
		frame.pack();
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		game.start();
	}
	
	private class ALGame extends KeyAdapter
	{
		public void keyReleased(KeyEvent e)
		{
			p.keyReleased(e);
		}
		
		public void keyPressed(KeyEvent e)
		{
			p.keyPressed(e);
		}
	}
	
	private class ALMenu extends KeyAdapter
	{
		public void keyReleased(KeyEvent e)
		{
			int key = e.getKeyCode();
			if (key == KeyEvent.VK_SPACE) gameInit();
			if (key == KeyEvent.VK_ESCAPE) System.exit(0);
		}
	}
	
	public void getScore()
	{
		long currentTime = System.currentTimeMillis();
		score = (currentTime - startingTime) / 100;
	}
}

Player class -

package main;

import java.awt.Image;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;

public class Player 
{
	double negChange = -.2;
	double posChange = .2;
	
	double x, dx, y, dy, xSpeed, ySpeed;
	Image player;
	
	Main m;
	
	int key2;
	boolean move = false;
	
	ImageIcon up = new ImageIcon(getClass().getResource("/textures/Up.png"));
	ImageIcon down = new ImageIcon(getClass().getResource("/textures/Down.png"));
	ImageIcon leftRunning = new ImageIcon(getClass().getResource("/textures/LeftRunning.png"));
	ImageIcon rightRunning = new ImageIcon(getClass().getResource("/textures/RightRunning.png"));
	
	public Player()
	{
		m = new Main();
		player = down.getImage();
		x = 12;
		y = 12;
	}
	
	public void move()
	{
		if (x <= 1)
		{
			if (dx > 0)
			{
				move = true;
			}
			
			else
			{
				dx = 0;
				move = false;
			}
		}
		
		else if (x >= 145)
		{
			if (dx < 0)
			{
				move = true;
			}
			
			else
			{
				dx = 0;
				move = false;
			}
		}
		
		else if (y <= 1)
		{
			if (dy > 0)
			{
				move = true;
			}
			
			else
			{
				dx = 0;
				move = false;
			}
		}
		
		else if (y >= 105)
		{
			if (dy < 0)
			{
				move = true;
			}
			
			else
			{
				dx = 0;
				move = false;
			}
		}
		
		else move = true;
		
		if (move)
		{
			x = x + dx;
			y = y + dy;
		}
	}
	
	public int getX()
	{
		return (int) x;
	}
	
	public int getY()
	{
		return (int) y;
	}
	
	public Image getImage()
	{
		return player;
	}
	
	public void keyPressed(KeyEvent e)
	{
		int key = e.getKeyCode();
		
		if (key == KeyEvent.VK_A)
		{
			if ((key2 != 2) && (key2 != 3) && (key2 != 4))
			{
				key2 = 1;
				dx = negChange;
				player = leftRunning.getImage();
			}
		}
		if (key == KeyEvent.VK_D) 
		{
			if ((key2 != 1) && (key2 != 3) && (key2 != 4))
			{
				key2 = 2;
				dx = posChange;
				player = rightRunning.getImage();
			}
		}	
		if (key == KeyEvent.VK_W)
		{
			if ((key2 != 1) && (key2 != 2) && (key2 != 4))
			{
				key2 = 3;
				dy = negChange;
				player = up.getImage();
			}
		}
		if (key == KeyEvent.VK_S) 
		{
			if ((key2 != 1) && (key2 != 2) && (key2 != 3))
			{
				key2 = 4;
				dy = posChange;
				player = down.getImage();
			}
		}
		if (key == KeyEvent.VK_TAB) m.menuInit();
	}
	
	public void keyReleased(KeyEvent e)
	{
		int key = e.getKeyCode();
		
		if (key == KeyEvent.VK_A) 
		{
			dx = 0;
			key2 = 0;
		}
		if (key == KeyEvent.VK_D)
		{
			dx = 0;
			key2 = 0;
		}
		if (key == KeyEvent.VK_W)
		{
			dy = 0;
			key2 = 0;
		}
		if (key == KeyEvent.VK_S)
		{
			dy = 0;
			key2 = 0;
		}
	}
}

Thanks.

removeKeyListener(myListener);

When I call this line -

addKeyListener(new ALMenu());

this line here -

removeKeyListener(ALMenu);

Doesn’t even recognize ALMenu. Should I be putting something else as the parameter?

No you should be storing the reference to ALMenu like so:


public class Main ... {
    ...
    ALMenu alMenu = new AlMenu();
    
    public void menuInit() {
        addKeyListener(alMenu);
        ...
    }
    
    public void gameInit() {
        removeKeyListener(alMenu);
        ...
    }
    
    ...
}

Thanks!

Also, I am trying to make it so that when I press tab while playing, it brings me back to the menu. I tried calling my menuInit method when tab is pressed, but nothing happens.

^^See code above

O_o You are creating a completely separate instance of Main inside your player class. Shouldn’t you be passing the Main instance to the player?


public class Main {
    private Player player;
    public Main() {
        player = new Player(this):
    }
}

public class Player {
    private Main main;
    
    public Player(Main main) {
        this.main = main;
    }
}

I have the same thing here

public Main()
	{
		p = new Player(this);
	}

public Player(Main main)
	{
		this.main = new Main();
		player = down.getImage();
		x = 12;
		y = 12;
	}

but it now gives a stackOverFlowError when I run. :’(

No! You are creating a new Main object! You are supposed to assign main to this.main.

You need to read up more on objects, methods, and variables before you do graphics.

Oops. Sorry.

This (I think) brought me one step closed to figuring out why I can’t re-open the menu. When escape is pressed while in game now, it closes the window. I put a println where it’s calls Main.menuInit(); and it print just fine, so it’s getting to that point, it just isn’t doing what I want to, which is:

A: Program opened, menuInit started
B: In menuInit, mainMenu is set to true, running is set to false, keylistener alGame is removed, and keylistener alMenu is added
C: In game loop now,

while (mainMenu)
		{
			render();
		}

D:When in the menu, if space is pressed, gameInit is called, new Player is created (p = new Player(this);), alMenu is removed, alGame is added, mainMenu is set to false, and running is set to true

This all works just fine.

E: When escape is pressed in game, main.menuInit is called, which prints “WORKING” to the console, sets mainMenu to true and running to false which SHOULD initiate the while (running) in the game loop, but instead, decides to close.

:o

If it sets running to false, why would the while(running) loop run? :stuck_out_tongue:

My bad, I meant “sets mainMenu to true and running to false which SHOULD initiate the while (mainMenu) in the game loop, but instead, decides to close.”

That’s because the run() method returns…

*tries to understand what that means…fails

What does that mean?

p.s. Thanks so much for the help, I’m obviously still learning :x

You see the run() method that contains both while loops?
Well when you set running to false, the while(running) loop ends, then execution reaches the end of the run() method and the thread ends.

OH!

Thanks! :smiley:

And of course, something doesn’t work D:

I simply changed the “running” boolean to “playing” and added a new “running” boolean that is always true until you exit.

However, when I press escape, it flashes to the menu then closes again.

while (running)
		{
			while (mainMenu)
			{
				render();
			}
		
			while (playing)
			{
				long now = System.nanoTime();
				unprocessed += (now - lastTime) / nsPerTick;
				lastTime = now;
				boolean shouldRender = true;
			
				while (unprocessed >= .2)
				{
					tick();
					unprocessed -= .2;
					shouldRender = true;
				}
			
				Thread.yield();
			
				if (shouldRender)
				{
					frames++;
					render();
				}
			
				if (System.currentTimeMillis() - lastTimer1 > 1000)
				{
					lastTimer1 += 1000;
					System.out.println(ticks + " ticks, " + fps + " fps");
					fps = frames;
					frames = 0;
					ticks = 0;
				}
			}
		}

Make sure you don’t set running to false accidentally anywhere. Otherwise, try debugging it.

Finally fixed it.

Thanks so much! +1 to you good sir

Glad to help :smiley: