So I'm making a game and I have the EnemyAI
as well as the player
and they both extend JPanel
. The world has a null
layout, so I'm using setBounds();
to "move" (I actually just move the world image) the entities
(player
and AI
) around and position them properly. But when I add (what seems like, I tested for the smallest number possible) 5, it invokes repaint()
completely on it's own. This causes the player to visually be walking in place. The more entities I add, the faster the interval gets (i.e. 5 entities invokes repaint()
a lot slower than 500 does).
NOTE: window
in the below class is just a JFrame
.
Main Class:
public class Game(){
public static boolean fighting = false;
public static void startGame(){
WorldPanel game = new WorldPanel();
game.setPreferredSize(new Dimension(window.getWidth(), window.getHeight()));
PlayerPane player = new PlayerPane(32,32, "Player 1");
game.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent arg0) {
if(fighting == false){
move(player, game, arg0.isShiftDown(), arg0.getKeyCode());
} else {
System.out.println("Fighting = " + fighting);
}
}
@Override
public void keyReleased(KeyEvent arg0) {
gameTimer.stop();
player.setIndex(0);
game.repaint();
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
window.add(game);
game.setLayout(null);
game.requestFocus();
setImages(player, PLAYER_DOWN);
player.setDrawY(349);
player.setDrawX(493);
player.updateBounds();
game.add(player);
entities.add(player);
addEntities(game);
redoWindow();
}
public static void updateEntityBounds(){
PlayerPane player = null;
EnemyAI enemy = null;
for(int i = 0; i < entities.size(); i++){
if(i == 0){
player = (PlayerPane) entities.get(i);
} else {
enemy = (EnemyAI) entities.get(i);
if(enemy.getBounds().intersects(player.getBounds())){
startFight(i);
}
}
if(player != null){
player.updateBounds();
}
if(enemy != null){
enemy.updateBounds();
}
}
}
public static void addEntities(WorldPanel game){
EnemyAI enemies[] = new EnemyAI[5];
for(int i = 0; i < enemies.length; i++){
if(i%6 == 0){
try{
enemies[i] = new EnemyAI(32,32, ImageIO.read(new File("H:\\Java\\Game\\src\\res\\SlimeLv3Still.png")));
enemies[i].start();
}catch(IOException e){
e.printStackTrace();
}
}else if (i % 2 == 0){
try{
enemies[i] = new EnemyAI(32,32, ImageIO.read(new File("H:\\Java\\Game\\src\\res\\SlimeLv2Still.png")));
enemies[i].setEnX(enemies[i].getRandomX());
enemies[i].setEnY(enemies[i].getRandomY());
enemies[i].start();
}catch(IOException e){
e.printStackTrace();
}
} else {
try{
enemies[i] = new EnemyAI(32,32, ImageIO.read(new File("H:\\Java\\Game\\src\\res\\SlimeLv1Still.png")));
enemies[i].setEnX(enemies[i].getRandomX());
enemies[i].setEnY(enemies[i].getRandomY());
enemies[i].start();
}catch(IOException e){
e.printStackTrace();
}
}
game.add(enemies[i]);
entities.add(enemies[i]);
}
}
public static void move(PlayerPane player, WorldPanel game, boolean shiftDown, int keyPressed){
gameTimer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e){
updateEntityBounds();
redoWindow();
gameTimer.stop();
}
});
if(gameTimer.isRepeats()){
gameTimer.setRepeats(false);
}
if(shiftDown){
if(keyPressed == KeyEvent.VK_W && ((game.getImageY() == 0 && player.getDrawY() > 10) || player.getDrawY() >= 349)){
player.setDrawY(player.getDrawY() - 2);
setImages(player, PLAYER_UP);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_S && ((game.getImageY() == -3868 && player.getDrawY() < 681) || player.getDrawY() <= 349)){
player.setDrawY(player.getDrawY() + 2);
setImages(player, PLAYER_DOWN);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_A && ((game.getImageX() == 0 && player.getDrawX() > 10) || player.getDrawX() > 493 )){
player.setDrawX(player.getDrawX() - 2);
setImages(player, PLAYER_LEFT);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_D && ((game.getImageX() == -5126 && player.getDrawX() < player.getHeight() - 10) || player.getDrawX() < 493 )){
player.setDrawX(player.getDrawX() + 2);
setImages(player, PLAYER_RIGHT);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_W && game.getImageY() < 0){
if(game.getImageY() == -1){
game.setImageY(game.getImageY() + 1);
} else {
game.setImageY(game.getImageY() + 2);
}
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnY(enemy.getEnY() + 2);
}
setImages(player, PLAYER_UP);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_A && game.getImageX() < 0){
if(game.getImageX() == -1){
game.setImageX(game.getImageX() + 1);
} else {
game.setImageX(game.getImageX() + 2);
}
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnX(enemy.getEnX() + 2);
}
setImages(player, PLAYER_LEFT);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_S && game.getImageY() > -3868){
if(game.getImageY() == -3867){
game.setImageY(game.getImageY() - 1);
} else {
game.setImageY(game.getImageY() - 2);
}
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnY(enemy.getEnY() - 2);
}
setImages(player, PLAYER_DOWN);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_D && game.getImageX() > -5126){
if(game.getImageX() == -5125){
game.setImageX(game.getImageX() - 1);
} else {
game.setImageX(game.getImageX() - 2);
}
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnX(enemy.getEnX() - 2);
}
setImages(player, PLAYER_RIGHT);
gameTimer.start();
}
} else {
if(keyPressed == KeyEvent.VK_W && ((game.getImageY() == 0 && player.getDrawY() > 10) || player.getDrawY() > 349)){
player.setDrawY(player.getDrawY() - 1);
setImages(player, PLAYER_UP);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_S && ((game.getImageY() == -3868 && player.getDrawY() < 681) || player.getDrawY() < 349)){
player.setDrawY(player.getDrawY() + 1);
setImages(player, PLAYER_DOWN);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_A && ((game.getImageX() == 0 && player.getDrawX() > 10) || player.getDrawX() > 493 )){
player.setDrawX(player.getDrawX() - 1);
setImages(player, PLAYER_LEFT);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_D && ((game.getImageX() == -5126 && player.getDrawX() < player.getHeight() - 10) || player.getDrawX() < 493 )){
player.setDrawX(player.getDrawX() + 1);
setImages(player, PLAYER_RIGHT);
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_W && game.getImageY() < 0){
game.setImageY(game.getImageY() + 1);
setImages(player, PLAYER_UP);
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnY(enemy.getEnY() + 1);
}
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_A && game.getImageX() < 0){
game.setImageX(game.getImageX() + 1);
setImages(player, PLAYER_LEFT);
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnX(enemy.getEnX() + 1);
}
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_S && game.getImageY() > -3868){
game.setImageY(game.getImageY() - 1);
setImages(player, PLAYER_DOWN);
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnY(enemy.getEnY() - 1);
}
gameTimer.start();
} else
if(keyPressed == KeyEvent.VK_D && game.getImageX() > -5126){
game.setImageX(game.getImageX() - 1);
setImages(player, PLAYER_RIGHT);
for(int i = 1; i < entities.size(); i++){
EnemyAI enemy = (EnemyAI)entities.get(i);
enemy.setEnX(enemy.getEnX() - 1);
}
gameTimer.start();
}
}
}
}
Player:
public class PlayerPane extends JPanel{
private static final long serialVersionUID = 8946273935579723365L;
private ImageIcon images[] = new ImageIcon[9];
public static final int PLAYER_FRAMES = 9;
private String name;
private int index;
private int imageX, imageY;
private int HP = 100;
public PlayerPane(int width, int height, String playerName) {
setPreferredSize(new Dimension(width, height));
imageX = imageY = 0;
name = playerName;
// Border border = BorderFactory.createBevelBorder(BevelBorder.RAISED);
// PlayerPane.this.setBorder(border);
}
public String getName(){
return name;
}
public void paintComponent(Graphics g){
try {
g.drawImage(ImageIO.read(new File("H:\\Java\\Game\\src\\res\\TransparentImg.png")),0,0,getWidth(),getHeight(), null);
} catch (IOException e) {
e.printStackTrace();
}
g.drawImage(images[index].getImage(), 0,0, null);
if(index == images.length-1){
index = 0;
} else {
index++;
}
}
public void setIndex(int index){
this.index = index;
}
public void stop(){
index = 0;
}
public void addImage(ImageIcon image, int x){
images[x] = image;
}
public void setDrawX(int x){
imageX = x;
}
public void setDrawY(int y){
imageY = y;
}
public int getDrawY(){
return imageY;
}
public int getDrawX(){
return imageX;
}
public void updateBounds(){
setBounds(imageX, imageY, 32,32);
}
public int getHP(){
return HP;
}
public void setHP(int hp){
HP = hp;
}
}
EnemyAI class:
public class EnemyAI extends AI{
private static final long serialVersionUID = -2417438750134536982L;
private Rectangle rect;
private BufferedImage backgroundImg;
private int HP = 1;
private int damage = 15;
public EnemyAI(int width, int height, BufferedImage backgroundImg){
super();
rect = new Rectangle(width, height);
this.backgroundImg = backgroundImg;
}
@Override
public void paintComponent(Graphics g) {
try{
g.drawImage(ImageIO.read(new File("H:\\Java\\Game\\src\\res\\TransparentImg.png")), 0,0,null);
}catch(IOException e){
e.printStackTrace();
}
g.drawImage(backgroundImg, 0, 0, null);
}
public boolean intersects(Rectangle r){
return rect.intersects(r);
}
public int getRandomX(){
Random ran = new Random();
int rand = ran.nextInt(6144);
return rand;
}
public int getRandomY(){
Random ran = new Random();
int rand = ran.nextInt(4608);
return rand;
}
public int getHP(){
return HP;
}
public void setHP(int hp){
HP = hp;
}
public int getDamage(){
return damage;
}
}
AI Class that the above class extends:
public abstract class AI extends JPanel implements Runnable{
private static final long serialVersionUID = 283692586329054555L;
private boolean running = false;
private Thread moveThread;
private int x = 0, y = 0;
public AI(){
moveThread = new Thread(this);
}
public void start(){
running = true;
moveThread.start();
}
public void stop(){
running = false;
}
public boolean isRunning(){
return running;
}
public void run(){
while(running){
if(Game.fighting == false){
Random direction = new Random();
int dir = direction.nextInt(4);
switch(dir){
case 1:
if(this.getX() < Game.getWindowWidth()){
this.setEnX(this.getX() + 1);
}
break;
case 2:
if(this.getX() > 0){
this.setEnX(this.getX() - 1);
}
break;
case 3:
if(this.getX() < Game.getWindowHeight()){
this.setEnY(this.getY() + 1);
}
break;
case 4:
if(this.getX() < 0){
this.setEnY(this.getY() - 1);
}
break;
}
updateBounds();
try{
Thread.sleep(200);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
public void setEnX(int x){
this.x = x;
}
public void setEnY(int y){
this.y = y;
}
public int getEnX(){
return x;
}
public int getEnY(){
return y;
}
public void updateBounds(){
setBounds(x, y, 32,32);
}
public abstract void paintComponent(Graphics g);
}
I understand that I just threw a lot of code at you guys. I tried not to, but looking back at it I'm trying to provide an example that runs. If I missed any code, tell me and I'll add it.
Anyways, what I need to know is how to make setBounds()
stop invoking repaint()
(Other than just using less than 5 entities
). Also, I've removed EnemyAI.start()
when adding entities
and it did stop it. So I have reason to believe that the problem is within the AI
class run()
method. Which pretty much just calls setBounds()
.
This is a normal behavior and it's why you can't modify state inside
paintComponent
. We don't have control over when repaints happen: the system does them on its own sometimes.Here's an example of what I mean, which you shouldn't be doing:
You need to go through all of your code looking for every place you've modified a variable like this inside
paintComponent
, and move it out to somewhere else.As a side note, you should also move your
ImageIO.read
calls so they are not insidepaintComponent
. Load your images once when the program starts, in tostatic
variables or something like that.And as a general tip, you should look in to animation with just painting instead of trying to animate components. It will do you huge favors in the long run for a game.
So in summary:
paintComponent
stateless.paintComponent
.Here's a minimal example which demonstrates this by animating shapes falling down the window:
(Images from here.)
Other useful examples of animation and painting: