KeyListener Lags Inconsistantly - java

I am writing a little game in Java and, like most games, I need to listen for user input. I decided to use java.awt.event.KeyListener to handle inputs.
Here is my main method:
public static void main(String[] args){
LevelViewer l = new LevelViewer(LevelFileIO.load());
List<Entity> entities = l.getEntities();
List<GameObject> gameObjects = l.getGameObjects();
Pacman player = null;
for (Entity e : entities){
if (e instanceof Pacman){
player = (Pacman)e;
break;
}
}
JFrame frame = new JFrame();
frame.add(l);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(true);
frame.requestFocus();
class Listener implements KeyListener{
Pacman player;
Listener(Pacman player){
this.player = player;
}
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_W:
player.up();
break;
case KeyEvent.VK_D:
player.right();
break;
case KeyEvent.VK_S:
player.down();
break;
case KeyEvent.VK_A:
player.left();
break;
}
}
#Override
public void keyReleased(KeyEvent e) {}
#Override
public void keyTyped(KeyEvent e) {}
}
frame.addKeyListener(new Listener(player));
Timer timer = new Timer();
timer.schedule(new UpdateTimer(
entities.toArray(new Entity[entities.size()]),
gameObjects.toArray(new GameObject[gameObjects.size()]),
l), 0, 17);
}
That is all pretty straightforward, when I read key events I call methods in the player that simply change his velocity.
An example:
public void right(){
this.setVelocity(new Vector2(speed, 0));
this.setSprite(right);
}
The Timer at the end of the main method just calls this:
public UpdateTimer(Entity[] toUpdate, GameObject[] gameObjects, LevelViewer toRedraw){
this.toUpdate = toUpdate;
this.gameObjects = gameObjects;
this.toRedraw = toRedraw;
}
#Override
public void run() {
toRedraw.repaint();
if (timeAtLastUpdateCall == 0){
timeAtLastUpdateCall = System.currentTimeMillis();
for (Entity e : toUpdate){
e.start();
}
return;
}
long newTime = System.currentTimeMillis();
double deltaT = ((double)(newTime - timeAtLastUpdateCall)/1000.0);
timeAtLastUpdateCall = newTime;
for (Entity e : toUpdate){
for (GameObject g : gameObjects){
if (g.touching(e)){
e.setVelocity(e.getVelocity().multiply(-1));
e.free(g);
}
}
e.update(deltaT);
e.getSprite().nextFrame();
}
}
All this run function does is some collision checking and then calls the update method in each entity. In the case of the player it just calculates how much it should move since the last update call:
public void update(double deltaT){
this.setPosition(this.getPosition().add(velocity.multiply(deltaT)));
}
Now the problem is that when I press a key that changes the players velocity the change will usually occur instantaneously, but sometimes there can be noticeable lag (around a quarter of a second, much more than 17 ms).
Is this a problem with Java's KeyListener, or with my implementation?
I would like to fix this to have smooth user input.

I don't think this is an issue with KeyListener. I am not a game developer but some things to consider:
Have you timed how long it takes to do the TimerTask? If it's ever over 17ms then it could be a problem especially when you compare schedule to scheduleAtFixedRate
You may want to use a proper game loop particularly if you plan on this game being played on systems out side of your control.

Related

Java while loop too fast? [duplicate]

This question already has an answer here:
Loop doesn't see value changed by other thread without a print statement
(1 answer)
Closed 11 months ago.
I have some problem with my code. I want to implement a keylistener. I have a keyHandler class which takes care about keyinput and a while loop in the main class to check if a certain key is pressed or not. I dont understand the behavior of my code. the strange thing is that every thing works when I put the System.out.println("hello") command in front of my if statement. but when i comment it out my programm doesnt realize that i press the key Im checkin in my if statement. I think i could find a workaround. but i would be very glad to understand this strange behavior. why is this happening. Sorry for my bad english. I hope you guys can help me.
public static void main(String[] args) {
boolean running = true;
JFrame window;
KeyHandler k = new KeyHandler();
window = new JFrame();
window.setVisible(true);
window.addKeyListener(k);
while (running) {
//System.out.println("hello");
if (k.isKeyPressed(KeyEvent.VK_W)) {
System.out.println("--------------------------------------------------------------------------");
}
}
}
//here is the KeyHandler class
public class KeyHandler implements KeyListener {
private boolean[] keysPressed = new boolean[128];
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
keysPressed[e.getKeyCode()] = true;
System.out.println(e.getKeyChar());
System.out.println(keysPressed[e.getKeyCode()]);
}
#Override
public void keyReleased(KeyEvent e) {
keysPressed[e.getKeyCode()] = false;
System.out.println(e.getKeyChar());
System.out.println(keysPressed[e.getKeyCode()]);
}
public boolean isKeyPressed(int keyCode) {
return keysPressed[keyCode];
}
}
The whole purpose of events and event handling is you don't need a loop to listen for events. Simply start your UI, add the listeners to a list, and allow the listeners to handle the processing.
Create a listener
public interface MyListener extends EventListener {
public void doSomething();
}
Now use it. With this code it just spits out some text when W is pressed, but the listeners could be another component or anything that uses the interface. No need for extra loops.
public class Main {
private EventListenerList listenerList = new EventListenerList();
public Main() {
JFrame frame = new JFrame();
addListener(new MyListener() {
#Override
public void doSomething() {
System.out.println("Hello 1");
}
});
addListener(new MyListener() {
#Override
public void doSomething() {
System.out.println("Hello 2");
}
});
frame.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
fireMyEvent();
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void addListener(MyListener listener) {
listenerList.add(MyListener.class, listener);
}
private void fireMyEvent() {
MyListener[] listeners = listenerList.getListeners(MyListener.class);
if (listeners == null) {
return;
}
for (MyListener listener : listeners) {
listener.doSomething();
}
}
public static void main(String [] args) {
new Main();
}
}
Here's a link that might help. I would not check for a key being pressed by that method. You are creating a resource hog, first off: by having
boolean running = true;
you then enter a while loop,
while (running) {
do x;
}
this can create a spin lock on some systems, this is a very bad practice. As user Lei Yang stated it is really not needed especially with the classes we have today and modern GUI's, your creating an endless loop. One this most certainly is a way to slow down a system, two you really can't continue coding past that point as you have no way to exit the loop. Some IDE's also have a check that won't allow your application to start if you have a loop that is infinite, almost all will at least give you a warning. you should at least if you are looking for a certain key and have to implement it that way do:
while (running) {
//System.out.println("hello");
if (k.isKeyPressed(KeyEvent.VK_W) = "e") {
running = false;
}
}
at least that won't be an endless loop.

KeyListener in a new thread for game input capture

I am making an old school Snake game in Java with Swing. I've read that in order to capture input in real time I need to run my game loop in a new thread so that It's wait() method won't interfere with the input capture. I've made InputCapture class implementing KeyListener and I've implemented keyPressed() method like that:
public class InputCapture implements KeyListener {
private Direction capturedDirection;
//Methods
#Override
public void keyPressed(KeyEvent e) {
boolean inputConsoleDebug = true;
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
capturedDirection = Direction.left;
if (inputConsoleDebug) System.out.println("LEFT");
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
capturedDirection = Direction.right;
if (inputConsoleDebug) System.out.println("RIGHT");
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
capturedDirection = Direction.up;
if (inputConsoleDebug) System.out.println("UP");
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
capturedDirection = Direction.down;
if (inputConsoleDebug) System.out.println("DOWN");
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
public Direction getCapturedDirection() {
return capturedDirection;
}
}
Then I've made Game class extending Thread and I've put game loop code into run() method:
public class Game extends Thread {
private Board board;
private Snake snake;
private JFrame frame;
private long waitTime;
private int difficultyStep;
private Direction inputDirection;
private InputCapture inputManager;
//Constructors
Game(Dimension boardSize) {
//Set difficulty
int applesToWin = boardSize.width * boardSize.height - 1;
final int easiestWaitTime = 1000;
final int hardestWaitTime = 100;
difficultyStep = (easiestWaitTime - hardestWaitTime) / applesToWin;
waitTime = easiestWaitTime;
//Set starting point
final int startingPointX = boardSize.width / 2;
final int startingPointy = boardSize.height / 2;
//Set board and snake
board = new Board(boardSize);
snake = new Snake(board, startingPointX, startingPointy);
//Set window Frame
frame = new JFrame(SnakeApplication.getApplicationName());
frame.setContentPane(board);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setResizable(false);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
interrupt();
}
});
//Set input manager
inputManager = new InputCapture();
frame.addKeyListener(inputManager);
inputDirection = null;
}
//Methods
public void run() {
board.spawnApple();
while (!isWon()) {
try {
sleep(waitTime);
} catch (InterruptedException e) {
return;
}
try {
inputDirection = inputManager.getCapturedDirection();
snake.move(inputDirection);
} catch (LosingMove e) {
showGameOverDialog();
return;
}
board.repaint();
}
showWinDialog();
}
JFrame getFrame() {
return frame;
}
private boolean isWon() {
for (int row = 0; row < board.getFields().length; row++) {
for (int col = 0; col < board.getFields()[0].length; col++) {
if (!(board.getFields()[row][col].getContent() instanceof Snake.SnakeNode)) return false;
}
}
return true;
}
private void showGameOverDialog() {
JFrame gameOverFrame = new JFrame();
JOptionPane.showMessageDialog(gameOverFrame, "Game Over!");
}
private void showWinDialog() {
JFrame gameOverFrame = new JFrame();
JOptionPane.showMessageDialog(gameOverFrame, "You Win!");
}
}
In my MainMenu class I've made startNewGame() method that is called when New Game button is clicked. This method creates Game object and starts a new thread by calling start() method.
public class MainMenu {
//Form components references
private JButton exitButton;
private JFrame frame;
private JPanel mainPanel;
private JButton newGameButton;
private JLabel titleLabel;
//Constructors
MainMenu() {
//Set window Frame
frame = new JFrame(SnakeApplication.getApplicationName());
frame.setContentPane(mainPanel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setResizable(false);
frame.pack();
newGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
startNewGame();
}
});
exitButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
exitGame();
}
});
}
JFrame getFrame() {
return frame;
}
private Dimension showBoardSizeDialog() {
Frame boardSizeFrame = new Frame();
int width = Integer.parseInt(JOptionPane.showInputDialog(boardSizeFrame, "Set board's width:"));
int height = Integer.parseInt(JOptionPane.showInputDialog(boardSizeFrame, "Set board's height:"));
return new Dimension(width, height);
}
private void startNewGame() {
Dimension boardSize = showBoardSizeDialog();
frame.setVisible(false);
Game game = new Game(boardSize);
game.getFrame().setVisible(true);
//Starting game loop in a new thread
game.start();
try {
game.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
frame.setVisible(true);
}
}
But when testing the app it gets stuck in the game loop and doesn't capture input at all. Why? I was trying to debug It, but every time the new thread is started it gets stuck in game loop. The Board itself is painted only when main thread ends its execution. Why? Shouldn't It be repainted many times during game loop if execution is stucked there?
Also, I've made thread interrupt when frame's close button is clicked (red X button) so execution could get back to MainMenu and reappear it, but clicking red close button has no effect.
The program freezes because of the call to game.join() in startNewGame. join keeps the thread it was called from from continuing execution until the thread it was called on dies. In your situation, join defeats the purpose of using another thread, so you should just remove that.
There are other issues, though. You probably shouldn't use a thread. You should probably use a Swing Timer. Swing isn't thread-safe, and I can already see a few places where your code isn't thread-safe either. (For example, you need to declare capturedDirection as volatile.) Writing correct multi-threaded code with Swing is a bit complicated and it would be much simpler to just use a timer.
Otherwise, if you don't use a timer, you need to use e.g. synchronization between the game thread (which writes to shared game state) and the Swing thread which does painting (and presumably reads from shared game state). If you don't, you may run in to problems that are hard to diagnose.
Also see The Use of Multiple JFrames: Good or Bad Practice?
You should make your Game class extending Runnable instead of Thread.
Then to have the game in a different thread:
Game theGame = ... // initialization code here
new Thread(theGame).start();

Can't repaint a JPanel while my program is busy

I'm trying to make a visualization of algorithms. I had a working program, but is was really dirty and I decided to re-orgranize it before I continued. The problem is that it won't repaint anymore before the algorithm finishes. (and I have tried using revalidate instead/in combination with repaint)
The option menu:
public class BootScreen extends JPanel implements ActionListener {
GridBagConstraints c = new GridBagConstraints();
JFrame frame = new JFrame();
SpinnerNumberModel arraySizeModel = new SpinnerNumberModel(50, 0, 100000, 1);
SpinnerNumberModel speedModel = new SpinnerNumberModel(20, 0, 10000, 1);
SpinnerNumberModel algSelectModel = new SpinnerNumberModel(1, 1, 5, 1);
JSpinner arraySizeSpinner = new JSpinner(arraySizeModel);
JSpinner speedSpinner = new JSpinner(speedModel);
JSpinner algSelectSpinner = new JSpinner(algSelectModel);
JButton start = new JButton("Start");
BootScreen() {
frame.setTitle("Settings");
frame.setSize(500, 250);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this);
//just creating the options menu, nothing special here (deleted for simplicity)
//...
}
#Override
public void actionPerformed( ActionEvent e ) {
Algorithm alg = new Algorithm(Integer.parseInt(arraySizeModel.getValue().toString()), Integer.parseInt
(speedModel.getValue().toString()));
switch(Integer.parseInt(algSelectModel.getValue().toString())) {
case 1:
alg.alg1();
break;
case 2:
alg.alg2();
break;
case 3:
alg.alg3();
break;
case 4:
alg.alg4();
break;
case 5:
alg.alg5();
break;
}
}
public static void main(String[] Args) {new BootScreen();}
}
The main algorithm stuffs:
public class Algorithm {
int[] A;
GUI gui;
int type;
int[] pointers;
int delay;
int max;
Random r = new Random();
Algorithm( int arraySize, int delaySet) {
A = new int[arraySize];
delay = delaySet;
gui = new GUI(this);
}
void generate(int maxIntSize, int pointersAmount, int typeSet) {
max = maxIntSize;
for( int i = 0; i < A.length; i++ ) {
A[i] = r.nextInt(max);
}
pointers = new int[pointersAmount];
for( int i = 0; i < pointers.length; i++ ) {
pointers[i] = -1;
}
type = typeSet;
}
void step(boolean sleep, int updatePointer, int updatePointerVal) {
pointers[updatePointer] = updatePointerVal;
gui.revalidate();
gui.repaint();
if( sleep ) {
try {
Thread.sleep(delay);
}catch( InterruptedException e ) {
}
}
}
//alg1(), alg2(), .... would be here. They first call generate() and call step() a couple of times. (deleted for simplicity)
}
The GUI:
public class GUI extends JPanel {
JFrame frame = new JFrame();
Algorithm alg;
GUI( Algorithm algIn ) {
alg = algIn;
frame.setTitle("Algorithmizer");
frame.setSize(1080, 720);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this);
}
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent(g);
//drawing alg.A as a bar-graph by looping through it (deleted for simplicity)
}
}
When I put System.out.println("1") before the repaint() call, System.out.println("2") in the paintComponent() function and System.out.println("3") after the repaint() call it will only print:
1
3
1
3
1
3
...
I also tried printing the stack, didn't get anything usefull from that either.
The program does repaint when the algorithm is done, but that isn't usefull to me.
Maybe take an approach like this. This will do all your calculations and stuff in a worker thread off the Event Dispatch Thread, and then call done() where you can do you GUI updates on the Event Dispatch Thread.
#Override
public void actionPerformed( ActionEvent e ) {
new SwingWorker<Object, Object>() {
#Override
protected Object doInBackground() throws Exception {
runAlg();
return null
}
#Override
protected void done() {
//any of you gui stuff here
}
}.execute();
}
}
public void runAlg(){
Algorithm alg = new Algorithm(Integer.parseInt(arraySizeModel.getValue().toString()), Integer.parseInt
(speedModel.getValue().toString()));
switch(Integer.parseInt(algSelectModel.getValue().toString())) {
case 1:
alg.alg1();
break;
case 2:
alg.alg2();
break;
case 3:
alg.alg3();
break;
case 4:
alg.alg4();
break;
case 5:
alg.alg5();
break;
}
}
The problem is that it won't repaint anymore before the algorithm finishes.
Your algorithm is executing on the Event Dispatch Thread (EDT) which is the Thread the GUI uses to paint itself, therefore the GUI can't repaint itself until the algorithm is finished.
The solution is to use a separate Thread for the algorithm.
One way to do this is to use a Swing Worker which executes on a separate Thread and allows you to "publish" results as the algorithm is executing.
Read the section from the Swing tutorial on Concurrency for more details. The tutorial has a Swing Worker example.

Java, is there a better way to do key detection?

So I'm just learning java, and from my work in C++, key dectection was much easier with SDL. But when I try to do it in Java it never seems to work. Could someone please check my code and make sure all of this is alright? And yes I did import everything....
public class start extends JFrame{
JLabel label = new JLabel();
Screen circle = new Screen();
int x = 0;
int y = 0;
static boolean m = false;
boolean running = true;
public static void main(String args[]){
start gui = new start();
gui.go();
}
public void go(){
JFrame j = new JFrame();
j.setDefaultCloseOperation(EXIT_ON_CLOSE);
j.setSize(400,400);
pannel p = new pannel();
Player player = new Player();
j.add(player);
j.setVisible(true);
player.addKeyListener(new key());
//try{
// for(int i = 0; i < 300; i ++){
// player.update();
// Thread.sleep(50);
// j.repaint();
// }
//}
//catch(Exception e){}
This try block was me testing to make sure that player.update() worked.... which it did! So I know the problem isn't with the update function
while(running){
//Updates
if(m)
player.update();
System.out.println(m);
I have the program printing out the value of m to see if its ever true... which it never is...
//Rendering
player.repaint();
}
}
public class pannel extends JPanel{
public void pannel(){
JPanel p = new JPanel();
Player player = new Player();
p.add(player);
p.setVisible(true);
}
}
public class key implements KeyListener{
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_SPACE:
m = true;
break;
}
}
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_SPACE:
m = false;
break;
}
}
public void keyTyped(KeyEvent e) {
}
}
}
In C++ there was a function that you had to call in order to detect if there has been any sort of key detection. You used this function in the main loop and it was really easy... Is there a function like this for Java? Or do I really need to put a key listener on EVERY object that I want to be effected by that key?
The simplest solution, and generally the more powerful, would be to use the key bindings API
The main problem with KeyListener is that is focus contextual. That is, the component it is registered to must be focusable AND have focus before it will raise key events

Java repaint() method doesn't always work

There is a problem with the repaint() method in Java. I made a new thread that constantly repaints the screen. When I release the spacebar I want my player to fall smoothly by setting its position and then waiting for 50 milliseconds and looping that 20 times. Instead, it waits the whole amount of time in the loop, then repaints. I am wondering why it doesn't constantly repaint the changes in the players co-ordinates. Thank you.
(Edit) Thanks everyone for the help. This is my first time using stack overflow, and I am only 13 and still learning java, so I probably will go back to the tutorials again.
My 'a' class (main):
public class a {
public static void main(String[] args) {
JFrame frame = new JFrame("StickFigure Game");
frame.setSize(740, 580);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
b board = new b();
frame.add(board);
frame.addKeyListener(board);
}
}
My 'b' class (JPanel/drawing):
public class b extends JPanel implements KeyListener {
c player = new c();
public class MyRunnable implements Runnable {
public void run() {
while (true)
repaint();
}
}
MyRunnable run = new MyRunnable();
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(player.getImage(), player.getX(), player.getY(), 80, 140,
null);
}
public b() {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
public static void slow(int n) {
long t0, t1;
t0 = System.currentTimeMillis();
do {
t1 = System.currentTimeMillis();
} while (t1 - t0 < n);
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_D) {
player.setPos(player.getX() + 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_A) {
player.setPos(player.getX() - 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
player.setPos(player.getX(), player.getY() - 60);
}
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
for (int i = 0; i < 20; i++) {
slow(50);
player.setPos(player.getX(), player.getY() + 2);
}
}
}
public void keyTyped(KeyEvent e) {
}
}
my 'c' class (player):
public class c {
private ImageIcon i = new ImageIcon("guy.png");
private Image img = i.getImage();
private int x = 0;
private int y = 100;
public void wait(int what) {
try {
Thread.sleep(what);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public c() {
}
public Image getImage() {
return img;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setPos(int mx, int my) {
x = mx;
y = my;
}
}
I haven't gone through all the code here but here are some pointers:
Swing has its own concurrency mechanisms which allow you to handle UI updates. You can use a Swing Timer rather than a raw Thread. Related is the use of Thread.sleep - don't do this, it only blocks the EDT and prevents UI updates.
The Swing paint chain mechanism requires you to override paintComponent rather than paint.
Always use Key Bindings rather than KeyListeners in Swing. KeyListeners require component focus to work to interact with the KeyEvents. Key Bindings do not have this limitation.
"There is a problem with the repaint() method in java." Did you consider that perhaps the problem is with your code instead? You are blocking the event thread and giving the system no time to do the intermediate repaints. In particular, this method:
public static void slow (int n){
long t0,t1;
t0=System.currentTimeMillis();
do{
t1=System.currentTimeMillis();
}
while (t1-t0<n);
}
and this loop:
for(int i = 0;i<20;i++){
slow(50);
player.setPos(player.getX(), player.getY()+2);
}
do not relinquish control to the system so that repaints can actually happen. Rewrite those using Swing timers. Look at this tutorial for an introduction on how to use these.
Also, your thread that constantly calls repaint() in a tight loop:
public void run(){
while(true) repaint();
}
is a terrible idea. You don't need to call repaint() at full CPU speed. Once every 30 milliseconds or so is fine for animation. Again, consider using Swing utilities to do this rather than writing your own looping thread.
The repaint is only a "request" to paint as soon as possible. so when you call it it causes a call to the paint method as soon as possible.
from here
So basically you just flooding the scheduled calls of paint or update with while(true) repaint();.
Oracle's stance on painting in AWT and Swing
One way you could do it, or should I say how I would do it, is to make your c class implement KeyListener, so that when a key is pressed (and only when it is pressed) you update it's location.
So move your KeyListener methods to class c, in your class b constructor you can add the call this.addKeyListener(player) or make a method void addPlayer(c player) that adds it.

Categories

Resources