I have this very weird problem that when I draw my tilemap the thread slows down by a big margin.
Here is tile map code:
public class TileMap {
Tile[][] map;
int x = 30, y = 0;
public TileMap(String map) {
String sCurrentLine;
try {
List<String> list = new ArrayList<String>();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/" + map)));
while (true) {
/** Starts reading the buffered line **/
String line = bufferedReader.readLine();
/** Closes the loop if there is no line to read **/
if (line == null) {
bufferedReader.close();
break;
}
/** Adds a line to the list if it is not commented **/
if (!line.startsWith("#")) {
list.add(line);
}
}
readClass(list);
} catch (IOException e) {
e.printStackTrace();
}
}
public void readClass(List<String> stringList){
int x = 0;
int y = 0;
map = new Tile[stringList.get(0).length()][stringList.size()];
for(String s : stringList){
int i = 0;
while (i < s.length()){
char c = s.charAt(i);
// if(c == 'A'){
map[x][y] = new Tile();
// }
i++;
x++;
}
x = 0;
y++;
}
}
public void draw(Graphics g){
for (int x = 0; x < map.length; x++) {
for (int y = 0; y < map[0].length; y++) {
g.drawImage(map[x][y].getImage(), (x * 20), (y * 20), null);
}
}
}
}
Now here is my code for the main class which basic checks the method that draws my character and draws the tilemap before the character (Note: When I change the wait speed when the tilemap is slowing the game down, nothing happens):
public class GamePane extends JPanel implements Runnable {
public static GameStateHandler gameStateHandler = new GameStateHandler();
private static final long serialVersionUID = 1L;
public static int WIDTH, HEIGHT;
public static boolean running;
Graphics graphics;
Thread thread;
public GamePane(Graphics g) {
thread = new Thread(this);
this.graphics = g;
init();
thread.start();
setFocusable(true);
requestFocusInWindow();
addKeyListener(new GameKeyListener(this));
}
public void init() {
WIDTH = 800;
HEIGHT = 600;
setPreferredSize(new Dimension(WIDTH, HEIGHT));
running = true;
}
public void run() {
while (running) {
try {
Thread.sleep(10);
gameStateHandler.update();
Main.drawOffScreen();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void keyPress(int keyCode) {
gameStateHandler.keyPressed(keyCode);
}
public void keyReleased(int keyCode){
gameStateHandler.keyReleased(keyCode);
}
}
Please let me know if you need anything else! Thanks in advance!
Can you post the code for the Tile class? My best guess is that the problem may be in Tile's getImage routine. For starters, I don't see anything besides you calling new for the Tiles, are you setting what image they are using somehow? Is the getImage returning an image pre-read into memory or is it pulling the image from the hard drive every time you call getImage (this can take a long time, especially if you're trying to do it many times per frame)?
Also, could you post the file that you are reading in for the map? How many characters are in it? If it has an entire "world" map, then you're effectively re-drawing the entire world every frame. Typically, games will only draw what can be currently seen and some border beyond what can be visually seen, this cuts down draw time dramatically for large worlds.
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.
I want to ask if why am getting error loading any sprite images into the object
here is how I get the image in.
import java.awt.image.BufferedImage;
import java.io.IOException;
public class SpriteSheet {
public BufferedImage sprite;
public BufferedImage[] sprites;
int width;
int height;
int rows;
int columns;
public SpriteSheet(int width, int height, int rows, int columns, BufferedImage ss) throws IOException {
this.width = width;
this.height = height;
this.rows = rows;
this.columns = columns;
this.sprite = ss;
for(int i = 0; i < rows; i++) {
for(int j = 0; j < columns; j++) {
sprites[(i * columns) + j] = ss.getSubimage(i * width, j * height, width, height);
}
}
}
}
here is how I'm implementing it
public BufferedImage[] init(){
BufferedImageLoader loader = new BufferedImageLoader();
BufferedImage spriteSheet = null;
SpriteSheet ss = null;
try {
spriteSheet = loader.loadImage("planet.png");
ss = new SpriteSheet(72,72,4,5,spriteSheet);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ss.sprites;
}
I also want to ask about my way of using the sprites array. I want to use in the timer by changing the image drawn by action event by changing the current sprite image
tmr = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for(Rock r:rocks){
r.move();
r.changeSprite();
}
repaint();
}
});
with the method
public void changeSprite(){
if(ds==12)
ds=0;
ds++;
currentSprite = sprite[ds];
}
Each rock object has a sprite array full of Buffered image received from the sprite image loaded in and a current image which get drawn. the timer will change the current image and redrawn it on the object so that the whole sprite get drawn but it doesn't seem to work. So it is my loadingSpriteImage that have the problem or my way of drawing it causing the problem?
Okay, so there a lots of things we need to know.
How many images make up the sprite sheet, how they are laid out (rows/cols), if there is an uneven number of images (count != rows * cols) and possibly even the size of each sprite
How far we are through a given cycle (let's say a second)
So based on your image from a previous question...
we know there are 5 columns, 4 rows but only 19 images. Now you could spend a lot of time, write lots of code for each possible sprite sheet, or you could try and commensalism some of those problems...
public class SpriteSheet {
private final List<BufferedImage> sprites;
public SpriteSheet(List<BufferedImage> sprites) {
this.sprites = new ArrayList<>(sprites);
}
public int count() {
return sprites.size();
}
public BufferedImage getSprite(double progress) {
int frame = (int) (count() * progress);
return sprites.get(frame);
}
}
So, this is pretty basic, it's simply a list of images. The special part is the getSprite method, which takes a progression through the current animation cycle and returns an image based on the number of images you have available. This basically decouples the concept of time from the sprite and allows you to define the meaning of a "cycle" externally.
Now, because the actual process of building a SpriteSheet involves a lot of possible variables, a builder would be a good idea...
public class SpriteSheetBuilder {
private BufferedImage spriteSheet;
private int rows, cols;
private int spriteWidth, spriteHeight;
private int spriteCount;
public SpriteSheetBuilder withSheet(BufferedImage img) {
spriteSheet = img;
return this;
}
public SpriteSheetBuilder withRows(int rows) {
this.rows = rows;
return this;
}
public SpriteSheetBuilder withColumns(int cols) {
this.cols = cols;
return this;
}
public SpriteSheetBuilder withSpriteSize(int width, int height) {
this.spriteWidth = width;
this.spriteHeight = height;
return this;
}
public SpriteSheetBuilder withSpriteCount(int count) {
this.spriteCount = count;
return this;
}
protected int getSpriteCount() {
return spriteCount;
}
protected int getCols() {
return cols;
}
protected int getRows() {
return rows;
}
protected int getSpriteHeight() {
return spriteHeight;
}
protected BufferedImage getSpriteSheet() {
return spriteSheet;
}
protected int getSpriteWidth() {
return spriteWidth;
}
public SpriteSheet build() {
int count = getSpriteCount();
int rows = getRows();
int cols = getCols();
if (count == 0) {
count = rows * cols;
}
BufferedImage sheet = getSpriteSheet();
int width = getSpriteWidth();
int height = getSpriteHeight();
if (width == 0) {
width = sheet.getWidth() / cols;
}
if (height == 0) {
height = sheet.getHeight() / rows;
}
int x = 0;
int y = 0;
List<BufferedImage> sprites = new ArrayList<>(count);
for (int index = 0; index < count; index++) {
sprites.add(sheet.getSubimage(x, y, width, height));
x += width;
if (x >= width * cols) {
x = 0;
y += height;
}
}
return new SpriteSheet(sprites);
}
}
So, again, based on your sprite sheet, this means I could build a SpriteSheet using something like...
spriteSheet = new SpriteSheetBuilder().
withSheet(sheet).
withColumns(5).
withRows(4).
withSpriteCount(19).
build();
but it gives me the power to build any number of SpriteSheets, all of which might be made up of different matrices
But now that we have a SpriteSheet, we need some way to animate them, but what we really need is some way to calculate the progression through a given cycle (let's say a second is a cycle), we could use a simple "engine", something like...
public class SpriteEngine {
private Timer timer;
private int framesPerSecond;
private Long cycleStartTime;
private TimerHandler timerHandler;
private double cycleProgress;
private List<ActionListener> listeners;
public SpriteEngine(int fps) {
framesPerSecond = fps;
timerHandler = new TimerHandler();
listeners = new ArrayList<>(25);
}
public int getFramesPerSecond() {
return framesPerSecond;
}
public double getCycleProgress() {
return cycleProgress;
}
protected void invaldiate() {
cycleProgress = 0;
cycleStartTime = null;
}
public void stop() {
if (timer != null) {
timer.stop();
}
invaldiate();
}
public void start() {
stop();
timer = new Timer(1000 / framesPerSecond, timerHandler);
timer.start();
}
public void addActionListener(ActionListener actionListener) {
listeners.add(actionListener);
}
public void removeActionListener(ActionListener actionListener) {
listeners.remove(actionListener);
}
protected class TimerHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (cycleStartTime == null) {
cycleStartTime = System.currentTimeMillis();
}
long diff = (System.currentTimeMillis() - cycleStartTime) % 1000;
cycleProgress = diff / 1000.0;
ActionEvent ae = new ActionEvent(SpriteEngine.this, ActionEvent.ACTION_PERFORMED, e.getActionCommand());
for (ActionListener listener : listeners) {
listener.actionPerformed(ae);
}
}
}
}
Now, this is basically just a wrapper class for a Swing Timer, but what it does is it calculates the cycle progression for us. It also has nice ActionListener support, so we can be notified when a tick occurs
Okay, but how does that all work together?
Basically, given one or more SpriteSheets and a SpriteEngine, we can paint the sheets doing something like...
public class TestPane extends JPanel {
private SpriteSheet spriteSheet;
private SpriteEngine spriteEngine;
public TestPane() {
try {
BufferedImage sheet = ImageIO.read(...);
spriteSheet = new SpriteSheetBuilder().
withSheet(sheet).
withColumns(5).
withRows(4).
withSpriteCount(19).
build();
spriteEngine = new SpriteEngine(25);
spriteEngine.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
spriteEngine.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
BufferedImage sprite = spriteSheet.getSprite(spriteEngine.getCycleProgress());
int x = (getWidth() - sprite.getWidth()) / 2;
int y = (getHeight() - sprite.getHeight()) / 2;
g2d.drawImage(sprite, x, y, this);
g2d.dispose();
}
}
Now, okay, that's pretty basic, but it gives an idea.
For entities you want to move (or rotate), I would create another class which contained that information AND the spriteSheet. This might contain a "paint" method or you could use the properties of the object to then paint the individual frames...
Something like...
public interface PaintableEntity {
public void paint(Graphics2D g2d, double progress);
}
public class AstroidEntity implements PaintableEntity {
private SpriteSheet spriteSheet;
private Point location;
private double angel;
public AstroidEntity(SpriteSheet spriteSheet) {
this.spriteSheet = spriteSheet;
location = new Point(0, 0);
angel = 0;
}
public void update() {
// Apply movement and rotation deltas...
}
public void paint(Graphics2D g2d, double progress) {
g2d.drawImage(
spriteSheet.getSprite(progress),
location.x,
location.y,
null);
}
}
Which could be created using something like...
private List<PaintableEntity> entities;
//...
entities = new ArrayList<>(10);
try {
BufferedImage sheet = ImageIO.read(new File("..."));
SpriteSheet spriteSheet = new SpriteSheetBuilder().
withSheet(sheet).
withColumns(5).
withRows(4).
withSpriteCount(19).
build();
for (int index = 0; index < 10; index++) {
entities.add(new AstroidEntity(spriteSheet));
}
} catch (IOException ex) {
ex.printStackTrace();
}
Note, I only created the sprite sheet once. This might require you to supply a random "offset" to the AstroidEntity which will change which frame it returns for a given progress value...
A simple way might be to add...
public SpriteSheet offsetBy(int amount) {
List<BufferedImage> images = new ArrayList<>(sprites);
Collections.rotate(images, amount);
return new SpriteSheet(images);
}
to SpriteSheet, then in AstroidEntity you could create an offset SpriteSheet using something like...
public AstroidEntity(SpriteSheet spriteSheet) {
this.spriteSheet = spriteSheet.offsetBy((int) (Math.random() * spriteSheet.count()));
Painting might be done using something like...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (PaintableEntity entity : entities) {
entity.paint(g2d, spriteEngine.getCycleProgress());
}
g2d.dispose();
}
Basically, the key factor here is, try and decouple your code from a concept of "time".
For example, I changed the frames-per-second the engine was using to 60 and saw no change in the animation of the sprites, because it was working on the concept of a single cycle of time been 1 second. This would, however allow you to change the speed at which the objects moved relatively simply.
You could also set the engine up to have a concept of "cycle-length" and make it half a second or 5 seconds which would then affect the animation speed - but the rest of you code would remain unchanged!
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 9 years ago.
Improve this question
I created a GUI named Menu class for my Start, Help and Exit button. my problem is my game won't start, it hangs everytime I press the Start button. Can someone check my error and explain it to me because its my case study at school.
package spaceSip;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class Menu extends JFrame implements ActionListener
{
private JLabel titleL;
private JButton startB, helpB, exitB;
static JFrame frame1 = new JFrame();
public Menu()
{
frame1.setSize(500,250);
Container mainP = frame1.getContentPane();
mainP.setLayout(null);
titleL = new JLabel("WELCOME");
startB = new JButton("Start");
helpB = new JButton("Help");
exitB = new JButton("Exit");
mainP.add(titleL);
titleL.setFont(new Font("Chiller",Font.BOLD,50));
titleL.setBounds(100, 30, 200, 50);
mainP.add(startB);
startB.setMnemonic(KeyEvent.VK_S);
startB.setBounds(200, 80, 100, 20);
mainP.add(helpB);
helpB.setMnemonic(KeyEvent.VK_H);
helpB.setBounds(200, 100, 100, 20);
mainP.add(exitB);
exitB.setMnemonic(KeyEvent.VK_E);
exitB.setBounds(200, 120, 100, 20);
startB.addActionListener(this);
helpB.addActionListener(this);
exitB.addActionListener(this);
frame1.setVisible(true);
frame1.setResizable(false);
}
public void actionPerformed(ActionEvent e)
{
String key = e.getActionCommand();
if(key == "Start")
{
frame1.dispose();
new SpaceShipMain();
}
else if(key == "Help")
{
}
else
System.exit(0);
}
public static void main(String[]args)
{
new Menu();
}
}
package spaceSip;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class SpaceShipMain implements MouseListener
{
private JFrame background;
private SpaceShipPanel back;
public static boolean paused;
public static boolean crashed;
public static boolean started;
public static boolean playedOnce;
public boolean goingUp;
private double upCount;
public static int distance;
public static int maxDistance;
public final int XPOS;
public final int NUMRECS;
public final int RECHEIGHT;
public final int RECWIDTH;
private int moveIncrement;
private int numSmoke;
private ArrayList<SpaceShipImage> toprecs;
private ArrayList<SpaceShipImage> bottomrecs;
private ArrayList<SpaceShipImage> middlerecs;
private ArrayList<SpaceShipImage> smoke;
private SpaceShipImage helicopter;
public SpaceShipMain()
{
NUMRECS = 100;
RECHEIGHT = 70;
RECWIDTH = 30;
XPOS = 200;
playedOnce = false;
maxDistance = 0;
load(new File("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/Best.txt"));
initiate();
}
public void load(File file)
{
try
{
Scanner reader = new Scanner(file);
while(reader.hasNext())
{
int value = reader.nextInt();
if(value > maxDistance)
maxDistance = value;
}
}
catch(IOException i )
{
System.out.println("Error. "+i);
}
}
public void save()
{
FileWriter out;
try
{
out = new FileWriter("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/Best.txt");
out.write("" + maxDistance);
out.close();
}
catch(IOException i)
{
System.out.println("Error: "+i.getMessage());
}
}
public void initiate()
{
if(!playedOnce)
{
background = new JFrame("Space Ship Game");
background.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //closes the program when the window is closed
background.setResizable(false); //don't allow the user to resize the window
background.setSize(new Dimension(800,500));
background.setVisible(true);
back = new SpaceShipPanel("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/starfield.jpg");
background.add(back);
back.addMouseListener(this);
}
playedOnce = true;
goingUp = false;
paused = false;
crashed = false;
started = false;
distance = 0;
upCount = 0;
moveIncrement = 2;
numSmoke = 15;
toprecs = new ArrayList<SpaceShipImage>();
middlerecs = new ArrayList<SpaceShipImage>();
bottomrecs = new ArrayList<SpaceShipImage>();
smoke = new ArrayList<SpaceShipImage>();
helicopter = new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rocketship.GIF",XPOS,200);
for(int x = 0; x < NUMRECS; x++)
toprecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,5));
for(int x = 0; x < NUMRECS; x++)
bottomrecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,410));
middlerecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid2.jpg",1392,randomMidHeight()));
middlerecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid2.jpg",1972,randomMidHeight()));
drawRectangles();
}
public void drawRectangles()
{
long last = System.currentTimeMillis();
long lastCopter = System.currentTimeMillis();
long lastSmoke = System.currentTimeMillis();
long lastSound = System.currentTimeMillis();
int firstUpdates = 0;
double lastDistance = (double)System.currentTimeMillis();
while(true)
{
if(!paused && !crashed && started && (double)System.currentTimeMillis() - (double)(2900/40) > lastDistance)
{
lastDistance = System.currentTimeMillis();
distance++;
}
if(!paused && !crashed && started && System.currentTimeMillis() - 10 > lastCopter)
{
lastCopter = System.currentTimeMillis();
updateCopter();
updateMiddle();
}
if(!paused && !crashed && started && System.currentTimeMillis() - 100 > last)
{
last = System.currentTimeMillis();
updateRecs();
}
if(!paused && !crashed && started && System.currentTimeMillis() - 75 > lastSmoke)
{
lastSmoke = System.currentTimeMillis();
if (firstUpdates < numSmoke)
{
firstUpdates++;
smoke.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/smoke.GIF",187,helicopter.getY()));
for(int x = 0; x < firstUpdates; x++)
smoke.set(x,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/smoke.GIF",smoke.get(x).getX() - 12, smoke.get(x).getY()));
}
else
{
for(int x = 0; x < numSmoke - 1; x++)
smoke.get(x).setY(smoke.get(x+1).getY());
smoke.set(numSmoke - 1,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/smoke.GIF",187,helicopter.getY()));
}
}
back.updateImages(middlerecs,helicopter,smoke);
}
}
public void updateRecs()
{
for(int x = 0; x < (NUMRECS - 1); x++) //move all but the last rectangle 1 spot to the left
{
toprecs.set(x,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,toprecs.get(x+1).getY()));
bottomrecs.set(x,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,bottomrecs.get(x+1).getY()));
}
}
public void randomDrop()
{
toprecs.get(26).setY(toprecs.get(26).getY() + (463 - bottomrecs.get(26).getY()));
bottomrecs.get(26).setY(463);
}
public int randomMidHeight()
{
int max = 10000;
int min = 0;
for(int x = 0; x < NUMRECS; x++)
{
if(toprecs.get(x).getY() > min)
min = (int)toprecs.get(x).getY();
if(bottomrecs.get(x).getY() < max)
max = (int)bottomrecs.get(x).getY();
}
min += RECHEIGHT;
max -= (RECHEIGHT + min);
return min + (int)(Math.random() * max);
}
//moves the randomly generated middle rectangles
public void updateMiddle()
{
if(middlerecs.get(0).getX() > -1 * RECWIDTH)
{
middlerecs.set(0,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(0).getX() - (RECWIDTH/5), middlerecs.get(0).getY()));
middlerecs.set(1,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(1).getX() - (RECWIDTH/5), middlerecs.get(1).getY()));
}
else
{
middlerecs.set(0,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(1).getX() - (RECWIDTH/5), middlerecs.get(1).getY()));
middlerecs.set(1,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(0).getX() + 580,randomMidHeight()));
}
}
public boolean shoot()
{
for(int x = 3; x <= 7; x++)
if(helicopter.getY() >= bottomrecs.get(x).getY())
return true;
for(int y = 3; y <= 7; y++)
if(helicopter.getY() <= toprecs.get(y).getY())
return true;
for(int z = 0; z <= 1; z++)
if(isInMidRange(z))
return true;
return false;
}
public boolean isInMidRange(int num)
{
Rectangle middlecheck = new Rectangle((int)middlerecs.get(num).getX(),(int)middlerecs.get(num).getY(),RECWIDTH,RECHEIGHT);
Rectangle coptercheck = new Rectangle((int)helicopter.getX(),(int)helicopter.getY(),70,48); //asteroid X and y bump
return middlecheck.intersects(coptercheck);
}
public void crash()
{
crashed = true;
if(distance > maxDistance)
{
maxDistance = distance;
save();
}
int reply = JOptionPane.showConfirmDialog(null, " RESTART ?", "GAME OVER", JOptionPane.YES_NO_OPTION);
if(reply == JOptionPane.YES_OPTION)
initiate();
else
System.exit(0);
initiate();
}
//moves the spaceship
public void updateCopter()
{
upCount += .10;
if(goingUp)
{
if(upCount < 3.5)
helicopter.setPosition(XPOS,(double)(helicopter.getY() - (.3 + upCount)));
else
helicopter.setPosition(XPOS,(double)(helicopter.getY() - (1.2 + upCount)));
helicopter.setImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rocketship.GIF");
}
else
{
if(upCount < 1)
helicopter.setPosition(XPOS,(double)(helicopter.getY() + upCount));
else
helicopter.setPosition(XPOS,(double)(helicopter.getY() + (1.2 + upCount)));
helicopter.setImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rocketship.GIF");
}
if(shoot())
crash();
}
//Called when the mouse exits the game window
public void mouseExited(MouseEvent e)
{
paused = true;
}
//Called when the mouse enters the game window
public void mouseEntered(MouseEvent e)
{
}
//Called when the mouse is released
public void mouseReleased(MouseEvent e)
{
goingUp = false;
upCount = -1;
if(paused)
paused = false;
}
//Called when the mouse is pressed
public void mousePressed(MouseEvent e)
{
if (!started)
started = true;
goingUp = true;
upCount = 0;
}
//Called when the mouse is released
public void mouseClicked(MouseEvent e)
{
}
}
package spaceSip;
import java.awt.Image;
import javax.swing.ImageIcon;
public class SpaceShipImage
{
private Image image; //The picture
private double x; //X position
private double y; //Y position
//Construct a new Moving Image with image, x position, and y position given
public SpaceShipImage(Image img, double xPos, double yPos)
{
image = img;
x = xPos;
y = yPos;
}
//Construct a new Moving Image with image (from file path), x position, and y position given
public SpaceShipImage(String path, double xPos, double yPos)
{
this(new ImageIcon(path).getImage(), xPos, yPos);
//easiest way to make an image from a file path in Swing
}
//They are set methods. I don't feel like commenting them.
public void setPosition(double xPos, double yPos)
{
x = xPos;
y = yPos;
}
public void setImage(String path)
{
image = new ImageIcon(path).getImage();
}
public void setY(double newY)
{
y = newY;
}
public void setX(double newX)
{
x = newX;
}
//Get methods which I'm also not commenting
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public Image getImage()
{
return image;
}
}
package spaceSip;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
class SpaceShipPanel extends JPanel
{
private Image background;
private ArrayList<SpaceShipImage> middle;
private SpaceShipImage copter;
private ArrayList<SpaceShipImage> smoke;
//Constructs a new ImagePanel with the background image specified by the file path given
public SpaceShipPanel(String img)
{
this(new ImageIcon(img).getImage());
//The easiest way to make images from file paths in Swing
}
//Constructs a new ImagePanel with the background image given
public SpaceShipPanel(Image img)
{
background = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
//Get the size of the image
//Thoroughly make the size of the panel equal to the size of the image
//(Various layout managers will try to mess with the size of things to fit everything)
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
middle = new ArrayList<SpaceShipImage>();
smoke = new ArrayList<SpaceShipImage>();
}
//This is called whenever the computer decides to repaint the window
//It's a method in JPanel that I've overwritten to paint the background and foreground images
public void paintComponent(Graphics g)
{
//Paint the background with its upper left corner at the upper left corner of the panel
g.drawImage(background, 0, 0, null);
//Paint each image in the foreground where it should go
for(SpaceShipImage img : middle)
g.drawImage(img.getImage(), (int)(img.getX()), (int)(img.getY()), null);
for(SpaceShipImage img : smoke)
g.drawImage(img.getImage(), (int)(img.getX()), (int)(img.getY()), null);
if(copter != null)
g.drawImage(copter.getImage(), (int)(copter.getX()), (int)(copter.getY()), null);
drawStrings(g);
}
public void drawStrings(Graphics g)
{
g.setColor(Color.WHITE);
g.setFont(new Font("Arial",Font.BOLD,20));
g.drawString("Distance: " + SpaceShipMain.distance,30,440);
g.setFont(new Font("Arial",Font.BOLD,20));
if (SpaceShipMain.distance > SpaceShipMain.maxDistance)
g.drawString("Best: " + SpaceShipMain.distance,650,440);
else
g.drawString("Best: " + SpaceShipMain.maxDistance,650,440);
if(SpaceShipMain.paused)
{
g.setFont(new Font("Chiller",Font.BOLD,72));
g.drawString("Paused",325,290);
g.setFont(new Font("Chiller",Font.BOLD,30));
g.drawString("Click to unpause.",320,340);
}
}
//Replaces the list of foreground images with the one given, and repaints the panel
public void updateImages(ArrayList<SpaceShipImage> newMiddle,SpaceShipImage newCopter,ArrayList<SpaceShipImage> newSmoke)
{
copter = newCopter;
middle = newMiddle;
smoke = newSmoke;
repaint(); //This repaints stuff... you don't need to know how it works
}
}
You compare Strings with equals(value equality) not with ==(reference equality). For more information read this previous question How do I compare strings in Java?
As #AndrewThompson always advice don't use NullLayout
Java GUIs might have to work on a number of platforms, on different
screen resolutions & using different PLAFs. As such they are not
conducive to exact placement of components. To organize the
components for a robust GUI, instead use layout managers, or
combinations of
them1, along
with layout padding & borders for white
space2.
As you say that your gui is blocked, that may be cause some of your execution code takes too more time and it's executed in the same thread as gui stuff (The Event Dispatch Thread). As you have a while(true) that's block the gui so use a SwingTimer for execute repeatidly task or if that code that takes long time execute it in a background thread using a Swing Worker. Here you have a complete example. When you perform custom painting you should override paintComponent and in first line you have to call super.paintComponent(..) to follow correct chaining. Read more in Painting in AWT-Swing.
Another error i see is that you are adding components after calling setVisible(true) without calling revalidate() repaint(). So i recommend to first add components to container then call setVisible(true) at final step.
basically i have been following a Java tutorial to make a basic maze game where i generate a random maze which is saved to a file then i print it out using Jpanel however i keep getting this error when i compile.
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at maze.Map.getMap(Map.java:43)
at maze.Board.paint(Board.java:40)
However i am not sure why, i believe it is something to do with my Y integer but if i set it to 0 it causes my program to print out wrong(just prints the same column instead of changing rows).
I need the Y value in Map.java:43 to increase like my x value does however it provides me with the Exception error.
Code:
Map Class
package maze;
import java.awt.Image;
import java.io.File;
import java.util.*;
import javax.swing.ImageIcon;
public class Map {
private Scanner m;
private Image wall, ground;
//number of columns
private String Map[] = new String[20];
public Map() {
//sets ground to a texture
ImageIcon img = new ImageIcon("C:\\MazeGraphics\\ground.png");
ground = img.getImage();
//sets wall texture
img = new ImageIcon("C:\\MazeGraphics\\wall.png");
wall = img.getImage();
openFile();
readFile();
closeFile();
}
public Image getGround() {
// gives grass texture for screen
return ground;
}
public Image getWall() {
// gives wall texture for screen
return wall;
}
public String getMap(int x, int y) {
//compares string to find graphic to use
String index= Map[y].substring(x, x+1);
return index;
}
public void openFile() {
try {
m = new Scanner(new File("C:\\MazeGraphics\\Maze.txt"));
} catch (Exception e) {
System.out.println("Error Loading Map");
}
}
public void readFile() {
for(int i = 0; i < 20; i++)
{
while(m.hasNext()){
Map[i] = m.next();
}
}
}
public void closeFile() {
m.close();
}
}
Board Class:
package maze;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Board extends JPanel implements ActionListener {
private Timer timer;
private Map m;
private Player p;
public Board()
{
MazeGeneration maz=new MazeGeneration();
//runs Map class
m = new Map();
p = new Player();
//listens for key presses
addKeyListener(new Al());
//knows to add key listener to the frame
setFocusable(true);
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void paint(Graphics g) {
super.paint(g);
//paints the board row by row
for (int y = 0; y < 20; y++)
{
for (int x = 0; x < 20; x++)
{
//places graphic depending on the letter.
if (m.getMap(x, y).equals("g"))
{
g.drawImage(m.getGround(), y * 32, x * 32, null);
}
if (m.getMap(x, y).equals("w"))
{
g.drawImage(m.getWall(), y * 32,x * 32, null);
}
}
}
g.drawImage(p.getPlayer(), p.gettileX() * 32, p.gettileY() * 32, null);
}
public class Al extends KeyAdapter {
//moving the player
public void keyPressed(KeyEvent e, char maz[][], int r, int c) {
int keycode = e.getKeyCode();
if (keycode == KeyEvent.VK_W) {
//if not a wall can move
if (!m.getMap(p.gettileX(), p.gettileY() - 1).equals("w")) {
p.move(0, -1);
}
}
if (keycode == KeyEvent.VK_S) {
if (!m.getMap(p.gettileX(), p.gettileY() + 1).equals("w")) {
p.move(0, 1);
}
}
if (keycode == KeyEvent.VK_A) {
if (!m.getMap(p.gettileX() - 1, p.gettileY()).equals("w")) {
p.move(-1, 0);
}
}
if (keycode == KeyEvent.VK_D) {
if (!m.getMap(p.gettileX() + 1, p.gettileY() ).equals("w")) {
p.move(1, 0);
}
}
}
}
}
The issue is in this code: In your readFile() method, your issue arises during reading your file:
for(int i = 0; i < 20; i++)
{
while(m.hasNext()){
Map[i] = m.next();
}
}
You are assigning the value only to the 0'th index. To explain, for the first for loop, the index i is at zero. The inner loop "iterates" through the Scanner m until there is no element next. Therefore, Map[0] contains the last element on the file.
Your solution is as follows:
int i = 0;
while (m.hasNext()) {
Map[i++] = m.next();
}
I hope this helps.
What I know: Canvas is displayed properly, due to the yellow. However, graphics will not cover the canvas completely, nor will my law class recognized it past the end of the black area...
So what is causing this to happen? And how could I draw on my currently undrawable section of canvas(yellow part), or should I just implement my graphics another way?
EDIT: The UI class creates a canvas and a buffer, then the graphics class takes over and starts drawing on them, however for some reason it cannot in the yellow section, nor will the Law Class which handles collision with the red cube and walls of the app, regogize the yellow area as a valid place to go. Even through the same variables for dimensions, were used everywhere.
Main Class
package app;
public class Main {
static final int X = 1024;
static final int Y = 680;
static final int sanic = 10;
int fps = 0;
int frames = 0;
long totalTime = 0;
long curTime = System.currentTimeMillis();
long lastTime = curTime;
static int[] pos;
Graphics graphics;
Law physics;
static int status;
boolean holdState;
public Main() {
pos = new int[5];
pos[1] = X;
pos[2] = Y;
}
public void launch() {
// Audio sound = new Audio();
graphics = new Graphics();
physics = new Law();
graphics.draw();
// sound.play();
handle();
}
public void update() {
graphics.cache();
physics.check();
graphics.render();
try {
Thread.sleep(10);
} catch (Exception e) {
} finally {
graphics.dump();
}
}
public void handle() {
while (!isOver()) {
if (!isPaused()) {
update();
}
}
}
boolean isOver() {
if (status == 1) {
status = 0;
return true;
}
return false;
}
boolean isPaused() {
if (status == 2) {
status = 0;
if (!holdState) {
holdState = true;
pos[3] = pos[1];
pos[4] = pos[2];
} else if (holdState) {
holdState = false;
pos[1] = pos[3];
pos[2] = pos[4];
}
}
return holdState;
}
public static void main(String[] args) {
Main game = new Main();
game.launch();
}
}
UI Class
package app;
import java.awt.*;
import java.awt.image.*;
import java.net.URL;
import javax.swing.*;
public class UI extends Main {
JFrame mainWin;
Canvas wall;
URL pic;
Toolkit kit;
Image img;
BufferStrategy Buffer;
Graphics2D shell;
Graphics2D ball;
public UI() {
mainWin = new JFrame("Game");
wall = new Canvas();
wall.addKeyListener(new Input());
}
void draw() {
frame();
icon();
canvas();
show();
prep();
}
void frame() {
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.setBackground(Color.BLUE);
mainWin.setIgnoreRepaint(true);
}
void icon() {
pic = ClassLoader.getSystemResource("res/app.png");
kit = Toolkit.getDefaultToolkit();
img = kit.createImage(pic);
mainWin.setIconImage(img);
}
void canvas() {
wall.setBackground(Color.YELLOW);
wall.setIgnoreRepaint(true);
wall.setBounds(0, 0, X, Y);
}
void show() {
mainWin.add(wall);
mainWin.pack();
mainWin.setResizable(false);
mainWin.setLocationRelativeTo(null);
mainWin.setVisible(true);
}
void prep() {
wall.createBufferStrategy(2);
Buffer = wall.getBufferStrategy();
}
}
Graphics Class
package app;
import java.awt.*;
public class Graphics extends UI {
public void render() {
mask();
player();
fps();
Buffer.show();
}
void cache() {
shell = (Graphics2D) Buffer.getDrawGraphics();
ball = (Graphics2D) Buffer.getDrawGraphics();
}
void dump() {
shell.dispose();
ball.dispose();
}
void mask() {
shell.setColor(Color.black);
shell.fillRect(0, 0, X, Y);
}
void fps() {
lastTime = curTime;
curTime = System.currentTimeMillis();
totalTime += curTime - lastTime;
if (totalTime > 1000) {
totalTime -= 1000;
fps = frames;
frames = 0;
}
frames++;
shell.setColor(Color.GREEN);
shell.setFont(new Font("Courier New", Font.PLAIN, 12));
shell.drawString(String.format("FPS: %s", fps), 20, 20);
}
void player() {
ball.setColor(Color.RED);
ball.fillRect(pos[1], pos[2], 32, 32);
}
}
Law Class
package app;
public class Law extends Main {
public void check() {
out();
}
void out() {
if (pos[1] < 0) {
pos[1] = 0;
}
if (pos[2] < 0) {
pos[2] = 0;
}
if (pos[1] > X - 32) {
pos[1] = X - 32;
}
if (pos[2] > Y - 32) {
pos[2] = Y - 32;
}
}
}
This is a bug with Frame#setResizable. Don't ask me why, but it wants to add about 10 pixels to the width and height of the frame.
The best solution I know is to call setResizable BEFORE pack
void show() {
mainWin.add(wall);
mainWin.setResizable(false); // Call me first
mainWin.pack();
mainWin.setLocationRelativeTo(null);
mainWin.setVisible(true);
}
You're not interacting with Swing properly, and you're doing unsafe things with a default Graphics object you should not be touching. Here's the bare bones of what needs to happen, in order.
JFrame mainFrame = new JFrame()
MyCanvas myCanvas = new MyCanvas()
mainFrame.getContentPane().add(myCanvas, BorderLayout.CENTER)
mainFrame.setVisible(true);
new Thread(myThread).start()
Main.main() returns.
MyCanvas needs to look something like this:
public class MyCanvas extends Canvas {
public void paint(Graphics g) {
// drawing code goes here
} // g is now no longer valid. Don't hold onto it or dispose it or anything.
}
Here's the physics update thread:
public class MyThread implements Runnable {
public void run() {
while (keepRunning()) {
physics.update();
myCanvas.repaint(); // myCanvas.paint() will eventually be called
sleep(10);
}
System.exit(0);
}
}
You probably want to add constructors to these objects and pass in references to your other objects they need to reference.