I am trying to make a message based collision system for a game test, where all objects are stored in an ArrayList that is iterated through and all objects are checked against all other objects. If two objects collide a Message is sent to both of them and currently it does a sysout (print) when this happens. My code so far is below, I have included the classes I think are relevant but if another class is involved I can post that as well. (I am using Slick2D and LWJGL)
public class MyGame extends BasicGame {
private ArrayList<GameObject> objects = new ArrayList<GameObject>();
MessageQueue messageQueue = new MessageQueue();
CollisionSystem collisionSystem = new CollisionSystem(objects, messageQueue);
public MyGame() {
super("My Game");
}
public static void main(String[] arguments) {
// Start up the game window
try {
AppGameContainer app = new AppGameContainer(new MyGame());
app.setDisplayMode(500, 400, false);
app.start();
app.setVSync(false);
app.setTargetFrameRate(60);
} catch (SlickException e) {
e.printStackTrace();
}
}
#Override
public void init(GameContainer container) throws SlickException {
Image img = new Image("myimage.png", false, Image.FILTER_NEAREST);
objects.add(new ObjSpriteObject(20, 20, img));
objects.add(new ObjSpriteObject(15, 20, img));
}
#Override
public void update(GameContainer container, int delta) throws SlickException {
collisionSystem.update();
messageQueue.update();
for (GameObject object : objects) {
object.update(container, delta);
}
}
#Override
public void render(GameContainer container, Graphics g) throws SlickException {
g.scale(3, 3);
for (GameObject object : objects) {
object.render(container, g);
}
}
}
MessageQueue
public class MessageQueue {
public ArrayList<Message> messages;
public MessageQueue() {
messages = new ArrayList<Message>();
}
public void update() {
while (messages.iterator().hasNext()) {
Message m = messages.iterator().next();
process(m);
}
}
private void process(Message m) {
switch (m.getType()) {
case 'p':
System.out.println("MESSAGE: " + m.getData());
break;
case 'c':
m.getReciever().processMessage(m);
}
}
}
ObjSpriteObject
public class ObjSpriteObject extends GameObject {
private Image sprite;
protected float vspeed = 0f;
protected float hspeed = 0f;
public boolean hasBB = true;
public ObjSpriteObject(int x, int y, Image sprite) {
super(x, y);
this.sprite = sprite;
this.boundingBox = new BoundingBox(this, x, y, 16, 16);
}
#Override
public void update(GameContainer container, int delta) {
super.update(container, delta);
}
#Override
public void render(GameContainer container, Graphics g) {
sprite.draw(x, y);
g.setColor(Color.red);
g.draw(getBoundingBox());
}
#Override
public void processMessage(Message m) {
if (m.getType() == 'c') {
delete = true;
}
}
}
CollisionSystem
public class CollisionSystem {
private ArrayList<GameObject> objects;
private MessageQueue messageQueue;
public CollisionSystem(ArrayList<GameObject> objects, MessageQueue messageQueue) {
this.objects = objects;
this.messageQueue = messageQueue;
}
public void update() {
// Iterate through each BB with each BB
for (int i = 0; i < objects.size(); i++) {
// If object has bounding box
GameObject ob1 = objects.get(i);
if (ob1.getBoundingBox() != null) {
for (int k = 0; k < objects.size(); k++) {
// If second object has bounding box
GameObject ob2 = objects.get(i);
if (ob2.getBoundingBox() != null) {
// Check both bounding boxes and send messages if they collide
BoundingBox bb1 = objects.get(i).getBoundingBox();
BoundingBox bb2 = objects.get(k).getBoundingBox();
// System.out.println(bb1 + " " + bb2);
if (bb1.collides(bb2) && bb1 != bb2) {
messageQueue.messages.add(new Message(bb1.getParent(), bb2.getParent(), 'p', "Collision!"));
}
}
}
}
}
}
}
When I run this code the game freezes. I feel like it has something to do with it running like a while loop that never exits, but I don't see why it would be doing that if the main game loop is making sure things run on a timer. Why is this code freezing the program?
Related
I have an application, in which a car is moving on a panel and it creates sound waves - circles. I want to :
1) have a few circles at the moment of opening the frame
2) when the Start button is selected I want them to move and I want more circles to be created, one after another, until the stop button is selected
the problem is:
1) when the frame is opened there are 5 circles, but they totally do not move
2) 5 new circles appears, but from the same XY position, they are just bigger - I want one circle after another, it grows, and next one appears
here is my code, I would appreciate some helpful sample or could you tell me where my mistake is. I used amount of 5 just to have some samples of waves.
public class WaveParameters {
int xPos=0;
int yPos = 375;
int width=60;
int height=60;
int velX = 0 ;
private Color color = Color.WHITE;
public int getVelX() {
return velX;
}
public void setVelX(int velX) {
this.velX = velX;
}
public int getX() {
return xPos;
}
public void setX(int xPos) {
this.xPos = xPos;
}
public int getWidth(){
return width;}
public int getHeight(){
return height;}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public void paint(Graphics g){
g.setColor(getColor());
g.drawOval(xPos,yPos,width/2,height/2);
}
}
Here is the panel of animation:
public class PanelAnimation extends JPanel implements ActionListener{
List<WaveParameters> waves = new ArrayList<WaveParameters>();
public PanelAnimation(ResourceBundle bundle) {
super();
resourceBundle = bundle;
t.start();
try {
imageBackground = ImageIO.read(newFile("bg.png"));
} catch (IOException ex) {
// handle exception...
}
}
CarParametrs pAuto = new CarParametrs();
HumanParametrs pHuman = new HumanParametrs() ;
Timer t = new Timer(60,this);
//WaveParameters pWave = new WaveParameters();
private BufferedImage imageBackground;
MainFrame mf;
public void addAuto(){
CarParametrs ap = new CarParametrs();
ap.setX(0);
pAuto = ap;
}
public void addHuman(){
HumanParametrs acz = new HumanParametrs();
acz.setX(0);
pHuman = acz;
}
public void addWave() {
for (int i=0; i<5; i++) {
WaveParameters wave = new WaveParameters();
// wave.setX(pAuto.xPos);
wave.setColor(Color.white);
wave.setWidth(wave.width*i);
wave.setHeight(wave.height*i);
waves.add(wave);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(imageBackground, 0, 0, null);
pAuto.paint(g);
pHuman.paint(g);
//if(mf.buttonStart.isSelected()) {
addWave();
//for (int i=0; i<5; i++) {
for (WaveParameters w : waves) {
// waves.add(new WaveParameters());
w.setX(pAuto.xPos);
w.paint(g);
//}
}
//}
}
public void actionPerformed(ActionEvent e) {
CarParametrs pa = pAuto;
pa.xPos += pa.velX;
/*//WaveParameters wp = pWave;
wp.xPos = pa.xPos;
wp.xPos+=wp.velX;
wp.height+=wp.velX;
wp.width+=wp.velX;
wp.yPos-=wp.velX/5 ;*/
for (WaveParameters w : waves) {
w.xPos = pa.xPos;
w.xPos+=w.velX;
w.height+=w.velX;
w.width+=w.velX;
w.yPos-=w.velX/5 ;
}
repaint();
}
and here is a wave-part of action listener for Start Button:
List<WaveParameters> wave = panelAnimation.waves;
for (WaveParameters w : wave) {
for (int i=0;i<5;i++) {
wave.add(new WaveParameters());
w.velX = Integer.parseInt(button2.getName());
w.xPos += w.velX;
w.width++;
w.height++;
w.yPos-=w.velX/5;
}
}
panelAnimation.repaint();
The five new bigger circles that appear are likely due to the last chunk of code where you iterate through all the waves in panel animation.
The "wave.add(new WaveParameters());" seems unnecessary, and may be the reason why your old waves are staying. Delete that line, and it may work.
My question is how can i implement the MouseMotionListener for list of objects can follow my mouse? I guess, I couldn't get the idea so far. I tried to do that my second part of the worm will follow the head. So it will become like a train. But in first second seems like Ok but all objects suddenly converge to a point.
Basically my code is above;
My Worm.class is like that;
public class Worm {
Random rd = new Random();
int xWorm;
int yWorm;
int Speed = 100;
int size = 10; // default
Worm()
{
xWorm = rd.nextInt(250);
yWorm = rd.nextInt(250);
}
Worm(int xNew, int yNew){
xWorm = xNew;
yWorm = yNew;
}
public int getxWorm() {
return xWorm;
}
public void setxWorm(int xWorm) {
this.xWorm = xWorm;
}
public int getyWorm() {
return yWorm;
}
public void setyWorm(int yWorm) {
this.yWorm = yWorm;
}
public int getSpeed() {
return Speed;
}
public void setSpeed(int speed) {
Speed = speed;
}
public void move (int dx, int dy) {
xWorm+=dx;
yWorm+=dy;
}
public void setPosition(int x,int y) {
this.xWorm = x;
this.yWorm = y;
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Ellipse2D Ellipse= new Ellipse2D.Double(xWorm,yWorm,10,10);
g2.setColor(Color.GREEN);
g2.draw(Ellipse);
g2.fill(Ellipse);
// g.fillOval(this.xWorm, this.yWorm, 30, 30);
}
public boolean iscollision(Food f) {
Rectangle2D RectangleforFood = new Rectangle2D.Double(f.xFood,f.yFood,5,5);
Rectangle2D RectangleforWormHead = new Rectangle2D.Double(this.xWorm,this.yWorm,10,10);
if (RectangleforWormHead.intersects(RectangleforFood)) {
return true;
}
else {
return false;
}
}
}
and my Panel.class the tracker function like that ;
public void createlongworm() {
for(int i = 0; i < 100 ; i++) {
wormBody.add(new Worm(wormBody.get(i).xWorm+10,wormBody.get(i).yWorm));
}}
public void tracker(ArrayList <Worm> TracktheWorm) {
for(int i = 0; i < TracktheWorm.size()-1 ; i++) {
TracktheWorm.get(i+1).xWorm = TracktheWorm.get(i).xWorm;
TracktheWorm.get(i+1).yWorm = TracktheWorm.get(i).yWorm;
}
repaint();
}
I am creating a Space Invaders Game with just one Invader. The images are stuttering and are only visible ~10% of the time. What is wrong with my code?
package spaceinvader;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class spaceInvaders extends JApplet implements KeyListener, ActionListener
{
//Declare components and variables
JPanel mainPanel = new JPanel();
ImageIcon carImage = new ImageIcon("ship.png");
ImageIcon invaderImage = new ImageIcon("invader.png");
int intPosX = 240;
int intPosY = 330;
int intXAmount = 15;
boolean shipMoveLeft = false;
boolean shipMoveRight = false;
Timer shipTimer = new Timer(100,this);
int intBulletX = -50;
int intBulletY = -50;
boolean bulletMove = false;
boolean bulletActive = false;
Timer bulletTimer = new Timer(50,this);
int intInvaderX = 0;
int intInvaderY = 0;
int invaderXAmount = 10;
boolean invaderMove = true;
Timer invaderTimer= new Timer(1000,this);
public void init()
{
addKeyListener(this);
setFocusable(true);
resize(600,400);
setContentPane(mainPanel);
shipTimer.start();
bulletTimer.start();
invaderTimer.start();
}
public void actionPerformed(ActionEvent e)
{
requestFocus();
if(shipMoveLeft)
intPosX += intXAmount;
else if(shipMoveRight)
intPosX -= intXAmount;
if(bulletMove && bulletActive){
intBulletY -= 15;
if(intBulletY <= -50){
bulletMove = false;
bulletActive = false;
}
}
if(invaderMove){
intInvaderX += invaderXAmount;
if(intInvaderX > getWidth() - 60 || intInvaderX < 0){
intInvaderY += 40;
invaderXAmount *= -1;
}
}
repaint();
}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == 37){
shipMoveRight = true;
}
else if (key == 39){
shipMoveLeft = true;
}
else if (key == 32){
if(bulletActive == false){
intBulletX = intPosX;
intBulletY = intPosY;
}
bulletMove = true;
bulletActive = true;
}
}
public void keyReleased(KeyEvent e)
{
shipMoveLeft = false;
shipMoveRight = false;
}
public void keyTyped(KeyEvent e)
{
}
public void paint(Graphics gr)
{
super.paint(gr);
gr.setColor(Color.red);
gr.fillOval(intBulletX, intBulletY, 10, 25);
carImage.paintIcon(this,gr, intPosX, intPosY); //Draw image in new spot
invaderImage.paintIcon(this,gr, intInvaderX, intInvaderY);
}
}
So there are a number of issues which jump out immediately...
Applets are a dead end, most browsers will actively block them and/or have dropped support for the plugin
You're adding a JPanel to the applet, but overriding the applet's paint method, because of the way painting can work, the panel can be painted independently of the applet, causing it to paint over whatever you might have painted
You don't need multiple timers, you just need to have better delta values (smaller been faster)
KeyListener is a poor choice for detecting keyboard input, it's rather low level and has focus related issues which are easily overcome by using the Key Bindings API
I'd start by having a look at Performing Custom Painting, Painting in AWT and Swing and How to Use Key Bindings for more details.
So how would you start fixing it? Start by using a JPanel as you basic container, override it's paintComponent and place all your paint logic here.
Use a single Timer to manage the updates
There are any number of approaches you can take, but I'd start with defining some contracts that define what can go on in the game
public interface GameSpace extends ImageObserver {
public Dimension getGameSpace();
public boolean hasInput(Input input);
}
public interface Entity {
public void paint(GameSpace gameSpace, Graphics2D g2d);
public boolean update(GameSpace gameSpace);
public Rectangle getBounds();
}
public abstract class AbstractEntity implements Entity {
private int x;
private int y;
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
protected abstract int getWidth();
protected abstract int getHeight();
public Rectangle getBounds() {
return new Rectangle(getX(), getY(), getWidth(), getHeight());
}
}
public abstract class AbstractImageEntity extends AbstractEntity {
protected abstract BufferedImage getImage();
#Override
protected int getWidth() {
return getImage().getWidth();
}
#Override
protected int getHeight() {
return getImage().getHeight();
}
}
This separates some of management of key elements, allowing for a more flexible design. You might have a lot more entities, one's which could move, ones which could not, some which are painted, some which are not, but all which provide support for the core engine to get work done.
Once you have that you can start defining some core entities you need
public class ShipEntity extends AbstractImageEntity {
private BufferedImage ship;
public ShipEntity(GameSpace gameSpace) throws IOException {
ship = ImageIO.read(getClass().getResource("/resources/ship.png"));
setY(gameSpace.getGameSpace().height - getBounds().height);
setX((gameSpace.getGameSpace().width - getBounds().width) / 2);
}
#Override
public BufferedImage getImage() {
return ship;
}
#Override
public void paint(GameSpace gameSpace, Graphics2D g2d) {
g2d.drawImage(ship, getX(), getY(), gameSpace);
}
#Override
public boolean update(GameSpace gameSpace) {
int x = getX();
if (gameSpace.hasInput(Input.LEFT)) {
x -= 2;
}
if (gameSpace.hasInput(Input.RIGHT)) {
x += 2;
}
if (x < 0) {
x = 0;
} else if (x + getWidth() > gameSpace.getGameSpace().width) {
x = gameSpace.getGameSpace().width - getWidth();
}
setX(x);
return true;
}
}
public class InvaderEntity extends AbstractImageEntity {
private BufferedImage invader;
public InvaderEntity() throws IOException {
invader = ImageIO.read(getClass().getResource("/resources/Invader.png"));
}
#Override
protected BufferedImage getImage() {
return invader;
}
#Override
public void paint(GameSpace gameSpace, Graphics2D g2d) {
g2d.drawImage(invader, getX(), getY(), gameSpace);
}
#Override
public boolean update(GameSpace gameSpace) {
return true;
}
}
public class ProjectileEntity extends AbstractEntity {
private int delta;
public ProjectileEntity(int delta) {
this.delta = delta;
}
#Override
protected int getWidth() {
return 10;
}
#Override
protected int getHeight() {
return 10;
}
#Override
public void paint(GameSpace gameSpace, Graphics2D g2d) {
g2d.setColor(Color.RED);
int width = getWidth();
int height = getHeight();
g2d.fillOval(getX() - width / 2, getY() - height / 2, width, height);
}
#Override
public boolean update(GameSpace gameSpace) {
int y = getY() + delta;
setY(getY() + delta);
return y + getHeight() >= 0 && y + getHeight() <= gameSpace.getGameSpace().height;
}
}
Basically, they contain the logic required for getting their jobs done.
Finally, you need to setup the actual game UI
public class GamePane extends JPanel implements GameSpace {
private Set<Input> inputs;
private Entity playerEntity;
private List<Entity> projectileEntities;
private List<Entity> invaderEntities;
private long timeOfLastProjectile = -1;
public GamePane() throws IOException {
setBackground(Color.BLACK);
inputs = new HashSet<>(2);
playerEntity = new ShipEntity(this);
projectileEntities = new ArrayList<>(25);
invaderEntities = new ArrayList<>(25);
InvaderEntity invader = new InvaderEntity();
invader.setX((getGameSpace().width - invader.getBounds().width) / 2);
invader.setY((getGameSpace().height - invader.getBounds().height) / 2);
invaderEntities.add(invader);
addKeyBinding(Input.LEFT, "left", KeyEvent.VK_LEFT);
addKeyBinding(Input.RIGHT, "right", KeyEvent.VK_RIGHT);
addKeyBinding(Input.SPACE, "space", KeyEvent.VK_SPACE);
Timer timer = new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateState();
processCollisions();
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void updateState() {
playerEntity.update(this);
if (hasInput(Input.SPACE)) {
long time = System.currentTimeMillis() - timeOfLastProjectile;
if (time < 0 || time > 1000) {
timeOfLastProjectile = System.currentTimeMillis();
Rectangle bounds = playerEntity.getBounds();
ProjectileEntity projectile = new ProjectileEntity(-1);
int x = bounds.x + ((bounds.width - projectile.getWidth()) / 2);
int y = bounds.y - projectile.getHeight();
projectile.setX(x);
projectile.setY(y);
projectileEntities.add(projectile);
}
}
for (Entity entity : invaderEntities) {
entity.update(this);
}
List<Entity> outOfBounds = new ArrayList<>(25);
for (Entity entity : projectileEntities) {
if (!entity.update(this)) {
outOfBounds.add(entity);
}
}
projectileEntities.removeAll(outOfBounds);
}
protected void processCollisions() {
Set<Entity> hitInvaders = new HashSet<>(25);
Set<Entity> hitProjectiles = new HashSet<>(25);
for (Entity invader : invaderEntities) {
for (Entity projectile : projectileEntities) {
if (projectile.getBounds().intersects(invader.getBounds())) {
// Maybe lots of cool explosiions
hitInvaders.add(invader);
hitProjectiles.add(projectile);
}
}
}
invaderEntities.removeAll(hitInvaders);
projectileEntities.removeAll(hitProjectiles);
}
protected void addKeyBinding(Input input, String name, int virtualKey) {
ActionMap am = getActionMap();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name + ".pressed");
im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name + ".released");
am.put(name + ".pressed", new KeyAction(inputs, input, true));
am.put(name + ".released", new KeyAction(inputs, input, false));
}
#Override
public Dimension getGameSpace() {
return getPreferredSize();
}
#Override
public boolean hasInput(Input input) {
return inputs.contains(input);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
playerEntity.paint(this, g2d);
g2d.dispose();
for (Entity entity : invaderEntities) {
g2d = (Graphics2D) g.create();
entity.paint(this, g2d);
g2d.dispose();
}
for (Entity entity : projectileEntities) {
g2d = (Graphics2D) g.create();
entity.paint(this, g2d);
g2d.dispose();
}
}
}
public class KeyAction extends AbstractAction {
private Input input;
private Set<Input> inputs;
private boolean pressed;
public KeyAction(Set<Input> inputs, Input input, boolean pressed) {
this.input = input;
this.inputs = inputs;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
if (pressed) {
inputs.add(input);
} else {
inputs.remove(input);
}
}
}
Now you could go a little further and generate an "engine" class which controls the entities and performs all the required updating, but I'm lazy ;)
That's a really rough idea of some the basic concepts you need to develop to be able to move forward, hope it helps
Few days ago I started to work on a 'simple' 2D game. I tried to do it with 'entity system'. I got class 'GameObject' which extends all other objects like trees, enemies (slimes) and so on. All of these game objects are stored in a arrayList called 'gameObjects'. Then I'm using a for loop to iterate through all objects from a list and calling their basic functions like update() and draw(). Until now everything works even though I'm not 100% sure why. The problem is that for some reason I can't do the same with collisions.
I know this topic was discussed many times here but even though after a many days I can't solve this. Can someone help me please? Also, I apologize for my English.
Game class:
public class Game extends BasicGame
{
public Game()
{
super("Game");
}
public void init(GameContainer gameContainer) throws SlickException
{
World.init();
}
public void update(GameContainer gameContainer, int delta) throws SlickException
{
World.update();
}
public void render(GameContainer gameContainer, Graphics g) throws SlickException
{
World.draw(g);
}
}
GameObject class:
public abstract class GameObject
{
protected void update()
{
}
protected void draw()
{
}
}
Tree class:
public class Tree extends GameObject
{
public float x, y;
private static Image tree;
public Tree(float x, float y)
{
this.x = x;
this.y = y;
tree = Resources.miscSheet.getSprite(2, 0);
}
public void draw()
{
tree.draw(x, y)
}
}
Slime class:
public class Slime extends GameObject
{
public static float x;
public static float y;
private static Animation slimeAnim;
public Slime(int x, int y)
{
this.x = x;
this.y = y;
// My own method for loading animation.
slimeAnim = Sprite.getAnimation(Resources.slimeSheet, 0, 0, 5, 300);
}
public void update()
{
// *Random movement here*
}
public void draw()
{
slimeAnim.draw(x, y);
}
}
World class:
public class World
{
public static List<GameObject> gameObjects = new ArrayList<GameObject>();
public static void init()
{
Tree tree = new Tree(0, 0);
Tree tree2 = new Tree(200, 200);
Slime slime = new Slime(80, 80);
gameObjects.add(tree);
gameObjects.add(tree2);
gameObjects.add(slime);
}
public static void update()
{
for (int i = 0; i < gameObjects.size(); i++)
{
GameObject o = gameObjects.get(i);
o.update();
}
}
public static void draw(Graphics g)
{
g.setBackground(new Color(91, 219, 87));
for (int i = 0; i < gameObjects.size(); i++)
{
GameObject o = gameObjects.get(i);
o.draw();
}
}
}
Main class:
public class Main
{
public static AppGameContainer container;
public static void main(String[] args) throws SlickException
{
container = new AppGameContainer(new Game());
container.setDisplayMode(1024, 600, false);
container.setShowFPS(false);
container.start();
}
}
I deleted all my previous collision attempts and I skipped some other unnecessary things. How can I now please implement collisions for example between trees and slimes?
What I usually do is inheriting from Rectangle on my top objectClass. If your GameObject class inherits from Rectangle you'll have intersects method in every instance which gives you a way to easily detect collision.
class GameObject extends Rectangle{}
The problem will be to do the test between all your objects. It will be quite heavy but still possible.
for (int i = 0; i < gameObjects.size(); i++) {
for (int j = i; j < gameObjects.size(); j++) {
if (gameObjects.get(i-1).intersects(gameObjects.get(j)) {
// I don't know what you want to do here
}
}
}
This way you compare object A to object B only once.
I have a problem with a simple Java game I am creating right now. I want a dot (a car) to be movable across the game screen, but instead of this all I can see on the screen is the long "snake" created by the dot moved by me:
Other problem is that activity manager on my Mac shows that the game uses huge amount of CPU power - my laptop gets very hot very fast. I suspect that there is something wrong with my game loop, but since now I haven't found any solution:
BoardPanel.java:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class BoardPanel extends JPanel implements KeyListener, Runnable {
public static final int WIDTH = 600;
public static final int HEIGHT = 600;
private Thread thread;
private boolean running;
private BufferedImage image;
private Graphics2D g;
private int FPS = 30;
private int targetTime = 1000/FPS;
private Map map;
private Car car;
public BoardPanel() {
super();
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}
public void run() {
init();
long startTime;
long reTime;
long waitTime;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
reTime = System.nanoTime() - startTime;
waitTime = targetTime - reTime;
try {
Thread.sleep(waitTime);
}
catch(Exception e) {
}
}
}
private void init() {
running = true;
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
map = new Map();
car = new Car(map);
car.setxpos(50);
car.setypos(50);
}
private void update() {
map.update();
car.update();
}
private void render() {
map.draw(g);
car.draw(g);
}
private void draw() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
public void keyTyped(KeyEvent key) {
}
public void keyPressed(KeyEvent key) {
int code = key.getKeyCode();
if(code == KeyEvent.VK_LEFT) {
car.setLeft(true);
}
if(code == KeyEvent.VK_RIGHT) {
car.setRight(true);
}
if(code == KeyEvent.VK_UP) {
car.setUp(true);
}
if(code == KeyEvent.VK_DOWN) {
car.setDown(true);
}
}
public void keyReleased(KeyEvent key) {
int code = key.getKeyCode();
if(code == KeyEvent.VK_LEFT) {
car.setLeft(false);
}
if(code == KeyEvent.VK_RIGHT) {
car.setRight(false);
}
if(code == KeyEvent.VK_UP) {
car.setUp(false);
}
if(code == KeyEvent.VK_DOWN) {
car.setDown(false);
}
}
}
Car.java
import java.awt.*;
public class Car {
private double xpos;
private double ypos;
//private int xsize;
//private int ysize;
private boolean left;
private boolean right;
private boolean up;
private boolean down;
private Map map;
public Car(Map m) {
map = m;
}
public void setxpos(int i) {
xpos = i;
}
public void setypos(int i) {
ypos = i;
}
public void setLeft (boolean b) {
left = b;
}
public void setRight (boolean b) {
right = b;
}
public void setUp (boolean b) {
up = b;
}
public void setDown (boolean b) {
down = b;
}
public void update() {
if(left) {
xpos--;
}
if(right) {
xpos++;
}
if(up) {
ypos--;
}
if(down) {
ypos++;
}
}
public void draw(Graphics2D g) {
int mx = map.getx();
int my = map.gety();
g.setColor(Color.BLUE);
g.fillOval((int)(mx+xpos-20/2), (int)(my+ypos-20/2), 20, 20);
}
}
Map.java (I haven't created map yet, right now only want the dot to move properly)
import java.awt.*;
public class Map {
public int x;
public int y;
public int getx() {
return x;
}
public int gety() {
return y;
}
public void setx(int i) {
x = i;
}
public void sety(int i ) {
y = i;
}
public void update() {
}
public void draw(Graphics2D g) {
}
}
RacerMain.java
import javax.swing.JFrame;
public class RacerMain {
public static void main (String[]args) {
//MainFrame mf = new MainFrame();
JFrame mf = new JFrame("Racer");
mf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mf.setContentPane(new BoardPanel());;
mf.pack();
mf.setVisible(true);
}
}
Many thanks for your help!!!
In addition to what camickr said: Be careful with your usage of the Graphics. A rule of thumb:
Never call getGraphics on a Component!
Additionally, you are never disposing the Graphics that you are fetching from the BufferedImage. You are instead disposing the Graphics that you obtained from the Component, which may be even worse than fetching it in the first place!
I wonder why you are overriding the addNotify method. You should not implement any functionality based on averriding this method....
So you should change the respecive parts of your BoardPanel class roughly as follows:
public class BoardPanel extends JPanel implements KeyListener, Runnable {
...
// private Graphics2D g; // Don't store this here
public BoardPanel() {
...
// Create the thread here instead of in the "addNotify" method!
if(thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}
public void run() {
...
while (running) {
...
//draw(); // Don't call this method
repaint(); // Trigger a repaint instead!
}
}
private void render() {
Graphics2D g = image.createGraphics();
// Clear the background (see camickrs answer)
g.setColor(Color.BLACK);
g.fillRect(0,0,image.getWidth(),image.getHeight());
try
{
map.draw(g);
car.draw(g);
}
finally
{
g.dispose(); // Dispose the Graphics after it has been used
}
}
/** Don't call "getGraphics" on a component!
private void draw() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
*/
// Override the paintComponent method instead:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image,0,0,null);
}
In your draw() method you need to clear the BufferedImages background before your invoke the fillOval method. Something like:
g.setColor( Color.BLACK );
g.fillRect(...);
g.setColor( Color.BLUE );
g.fillOval(...);
Print out your "waitTime" to make sure you are waiting a reasonable time.