Reputation:
I wrote a code that generates the "snake" in Snake game for practice. (In case you aren't familiar with the snake in snake game, it's a 5-boxes long snake with a head(created with JLabel) and body parts(also created with JLabels) following the head. )
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;
public class SnakeGameFrame extends JFrame {
Thread snakeThread;
GroundPanel p;
public SnakeGameFrame() {
super("Moving Snake!");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
p = new GroundPanel();
setContentPane(p);
setSize(400,400);
setVisible(true);
p.requestFocus();
snakeThread = new Thread(p);
snakeThread.start();
}
class GroundPanel extends JPanel implements Runnable{
static final int LEFT = 0;
static final int RIGHT = 1;
static final int UP = 2;
static final int DOWN = 3;
int direction;
Image img;
SnakeBody snakeBody;
final int delay = 200;
public GroundPanel() {
setLayout(null);
snakeBody = new SnakeBody();
snakeBody.addIn(this);
direction = LEFT;
this.addKeyListener(new MyKeyListener());
ImageIcon icon = new ImageIcon("twilight.jpg");
img = icon.getImage();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0,0,getWidth(), getHeight(), null);
}
public void run() {
while(true) {
try {
Thread.sleep(delay);
snakeBody.move(direction);
}catch(InterruptedException e) {
return;
}
}
}
class MyKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_LEFT:
direction = LEFT;
break;
case KeyEvent.VK_RIGHT:
direction = RIGHT;
break;
case KeyEvent.VK_UP:
direction = UP;
break;
case KeyEvent.VK_DOWN:
direction = DOWN;
break;
}
}
}
}
class SnakeBody {
Vector<JLabel> v = new Vector<JLabel>();
public SnakeBody() {
ImageIcon head = new ImageIcon("head.jpg");
JLabel la = new JLabel(head);
la.setSize(head.getIconWidth(), head.getIconHeight());
la.setLocation(100, 100);
v.add(la);
ImageIcon body = new ImageIcon("body.jpg");
for(int i=1; i<10; i++) {
la = new JLabel(body);
la.setSize(body.getIconWidth(), body.getIconHeight());
la.setLocation(100+i*20, 100);
v.add(la);
}
}
public void addIn(JPanel p) {
for(int i=0; i<v.size(); i++)
p.add(v.get(i));
}
public void move(int direction) {
for(int i=v.size()-1; i>0; i--) {
JLabel b = v.get(i);
JLabel a = v.get(i-1);
b.setLocation(a.getX(), a.getY());
}
JLabel head = v.get(0);
switch(direction) {
case GroundPanel.LEFT :
head.setLocation(head.getX()-20, head.getY());
break;
case GroundPanel.RIGHT :
head.setLocation(head.getX()+20, head.getY());
break;
case GroundPanel.UP :
head.setLocation(head.getX(), head.getY()-20);
break;
case GroundPanel.DOWN :
head.setLocation(head.getX(), head.getY()+20);
break;
}
}
}
public static void main(String[] args) {
new SnakeGameFrame();
}
}
My question is in order to keep "updating" my snake, I thought I would have to use the repaint() method in my
public void run()
method. Like this
try { Thread.sleep(delay); snakeBody.move(direction); repaint(); }
but it turned out that it works just fine without it.. I've read many posts about repaint() but I still don't get when I need to use it. Could anyone please explain in noob-friendly terminologies ? Thank you :)
Upvotes: 1
Views: 262
Reputation: 205835
You must invoke repaint()
whenever the RepaintManager
does not already do so on your behalf, as by updating a bound property such as text or color. In your example, changing the size and location of a JLabel
triggers the repaint automatically.
More seriously, your example is incorrectly synchronized in two ways:
It creates the GUI on the initial thread rather than on the event dispatch thread.
It updates GUI components on a thread other than the event dispatch thread without synchronizing access to the shared data.
Instead, use a javax.swing.Timer
to pace the animation. Your implementation of actionPerformed()
will be called on the event dispatch thread.
Upvotes: 2