I am trying to make a simple game with Java swing/awt.
I am have issue with lagging while printing and moving images on the screen.
Here is my code below:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.*;
public class StarDef extends JFrame implements Runnable, KeyListener{
private BufferedImage back;
private boolean start = false, end = false;
public int w = 1500, h = 800, commandx = 200, commandy = 100, ground = 500, mineral = 100;
private int mineralx = 0, mineraly = commandy + 104;
private int dronecnt = 0;
private ArrayList<Drone> DrList = null;
private ArrayList<Enemy> EnList = null;
private ArrayList<Building> BuildList = null;
private ArrayList<Allies> AlyList= null;
public Image imagearr[] = new Image[10];
private boolean makedrone = false, NeedMinerals = false;
public int picnum = 1;
private int OrderBuild = 0;
public static void main(String[] args){
Thread t = new Thread(new StarDef());
t.start();
}
public StarDef(){
back = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
DrList = new ArrayList<>();
BuildList = new ArrayList<>();
EnList = new ArrayList<>();
AlyList = new ArrayList<>();
this.addKeyListener(this);
this.setSize(w,h);
this.setTitle("Starcraft");
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
try {
imagearr[0] = ImageIO.read(new File("Char/Command.png"));
imagearr[1] = ImageIO.read(new File("Char/droneleft.png"));
imagearr[2] = ImageIO.read(new File("Char/droneright.png"));
imagearr[3] = ImageIO.read(new File("Char/Mineral.png"));
imagearr[4] = ImageIO.read(new File("Char/barracks.png"));
} catch (Exception e){
e.printStackTrace();
}
}
public void initGame(){
DrList.clear();
mineral = 100;
}
public void draw(){
Graphics gs = back.getGraphics();
gs.setColor(Color.white);
gs.fillRect(0,0,w,h);
gs.setColor(Color.DARK_GRAY);
gs.fillRect(0,ground,w,200);
if (!end) {
gs.drawImage(imagearr[0], commandx, commandy, null); // First Image-Command Center
gs.drawImage(imagearr[3], mineralx,mineraly,null); // Second Image-Mineral
for (int i = 0; i < DrList.size(); i++) { //Printing Drones
Drone m = DrList.get(i);
gs.drawImage(imagearr[m.state], m.x, m.y, null); //Drawing Drones
m.moveDr(); // Moving the Drone
}
for (int i = 0; i < BuildList.size(); i++){ //Printing Building
Building bd = BuildList.get(i);
if(bd.buildingtype == 'R'){
gs.drawImage(imagearr[4], bd.x, bd.y, null); // Drawing Building-Problem starts..?
}
}
gs.drawImage(imagearr[0], commandx, commandy, null);
}
gs.setColor(Color.black);
gs.drawString("mineral : " + mineral, 10,50);
gs.drawString("Drones : " + DrList.size(), 10, 70);
Graphics ge = this.getGraphics();
ge.drawImage(back, 0,0,w,h,this);
}
public void run(){
try{
int timer = 10;
while (true){
Thread.sleep(timer);
if(start){
if (makedrone) {
makedrone();
}
if (OrderBuild>0){
makeBuilding(OrderBuild);
}
}
draw();
}
} catch (Exception e){
e.printStackTrace();
}
}
public void makeBuilding(int buildingnumber){
int bdx, bdy;
char BuildingType;
if(buildingnumber == 1){
bdx = 500;
bdy = 100;
BuildingType = 'R';
Building barracks = new Building(this, bdx, bdy, BuildingType);
BuildList.add(barracks);
}
}
public void makedrone() {
if (mineral >= 50) {
int dronex = commandx;
int droney = commandy+129;
Drone dr = new Drone(this ,dronex, droney);
DrList.add(dr);
dronecnt++;
mineral -= 50;
makedrone = false;
} else if (mineral < 50) {
NeedMinerals = true;
makedrone = false;
}
}
public void keyPressed(KeyEvent ke){
switch (ke.getKeyCode()){
case KeyEvent.VK_ENTER:
start = true;
end = false;
break;
case KeyEvent.VK_D:
makedrone = true;
break;
case KeyEvent.VK_R:
OrderBuild = 1;
break;
}
}
public void keyReleased(KeyEvent ke){
switch ((ke.getKeyCode())){
}
}
public void keyTyped(KeyEvent ke) {
}
}
When you compile the code, the first few stationary images appear fine.
After moving images(Drones) appear nicely, but when you summon the next stationary image(Building), heavy lag starts to appear and the speed of the moving drones decrease visibly.
The building is about 300*150 pixels and the drones are 40*30 pixels.
What is the cause of this problem? Is this because of the code(the way of summoning the image), or the picture's size, or the computer(I am using a notebook(LG Gram 14in)).?
Start by not using Graphics ge = this.getGraphics();.
Because you're also using your own Thread, you're running the risk of thread race conditions which could result in dirty reads/ paints.
Start by understanding how Swing painting actually works and work within the API functionality.
Start by having a look at Performing Custom Painting, Painting in AWT and Swing and Concurrency in Swing
KeyListener is also a poor choice for monitor key input and you should be using the Key Bindings API - see How to use key bindings for more details.
Adding content to the ArrayList can cause the ArrayList to go through a growth cycle, which can consume time and force a longer GC cycle. Consider seeding the ArrayList with initial capacity, it will help reduce the interval between growth cycles.
Focus on separating the "update" logic from the "paint" logic, it can help you find performance issues
You could also have a look at
java what is heavier: Canvas or paintComponent()?
Swing animation running extremely slow
Rotating multiple images causing flickering. Java Graphics2D
Which demonstrate a verity of techniques for improving performance of rendering in Swing.
If these still are't getting your to the level you want, then you will want to explore using a BufferStrategy
Better draw in JPanel and use repaint() method to update your JPanel
Related
I'm trying to write a simple program: a bouncing ball that appears and starts bouncing after you press the "Start" button on the screen. The program should be closed by pressing "X".
For some reason, it runs very slowly. The ball is blinking, and I have to wait for a long time after I press the "X" for program to close.
Here is the code:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class Bounce
{
public static void main(String[] args)
{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
class BounceFrame extends JFrame
{
public BounceFrame()
{
setSize(WIDTH, HEIGHT);
setTitle("Bounce");
Container contentPane = getContentPane();
canvas = new BallCanvas();
contentPane.add(canvas, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start", new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
addBall();
}
});
contentPane.add(buttonPanel, BorderLayout.SOUTH);
}
public void addButton(Container c, String title, ActionListener listener)
{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}
public void addBall()
{
try
{
Ball b = new Ball(canvas);
canvas.add(b);
for (int i = 1; i <= 10000; i++)
{
b.move();
Thread.sleep(10);
}
}
catch (InterruptedException exception)
{
}
}
private BallCanvas canvas;
public static final int WIDTH = 300;
public static final int HEIGHT = 200;
}
class BallCanvas extends JPanel
{
public void add(Ball b)
{
balls.add(b);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (int i = 0; i < balls.size(); i++)
{
Ball b = (Ball)balls.get(i);
b.draw(g2);
}
}
private ArrayList balls = new ArrayList();
}
class Ball
{
public Ball(Component c) { canvas = c; }
public void draw(Graphics2D g2)
{
g2.fill(new Ellipse2D.Double(x, y, XSIZE, YSIZE));
}
public void move()
{
x += dx;
y += dy;
if (x < 0)
{
x = 0;
dx = -dx;
}
if (x + XSIZE >= canvas.getWidth())
{
x = canvas.getWidth() - XSIZE;
dx = -dx;
}
if (y < 0)
{
y = 0;
dy = -dy;
}
if (y + YSIZE >= canvas.getHeight())
{
y = canvas.getHeight() - YSIZE;
dy = -dy;
}
canvas.paint(canvas.getGraphics());
}
private Component canvas;
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private int x = 0;
private int y = 0;
private int dx = 2;
private int dy = 2;
}
The slowness comes from two related problems, one simple and one more complex.
Problem #1: paint vs. repaint
From the
JComponent.paint docs:
Invoked by Swing to draw components.
Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.
So the canvas.paint() line at the end of Ball.move must go.
You want to call
Component.repaint
instead...
but just replacing the paint with repaint will reveal the second problem, which prevents the ball from even appearing.
Problem #2: Animating inside the ActionListener
The ideal ActionListener.actionPerformed method changes the program's state and returns as soon as possible, using lazy methods like repaint to let Swing schedule the actual work for whenever it's most convenient.
In contrast, your program does basically everything inside the actionPerformed method, including all the animation.
Solution: A Game Loop
A much more typical structure is to start a
javax.swing.Timer
when your GUI starts, and just let it run
"forever",
updating your simulation's state every tick of the clock.
public BounceFrame()
{
// Original code here.
// Then add:
new javax.swing.Timer(
10, // Your timeout from `addBall`.
new ActionListener()
{
public void actionPerformed(final ActionEvent ae)
{
canvas.moveBalls(); // See below for this method.
}
}
).start();
}
In your case, the most important
(and completely missing)
state is the
"Have we started yet?"
bit, which can be stored as a boolean in BallCanvas.
That's the class that should do all the animating, since it also owns the canvas and all the balls.
BallCanvas gains one field, isRunning:
private boolean isRunning = false; // new field
// Added generic type to `balls` --- see below.
private java.util.List<Ball> balls = new ArrayList<Ball>();
...and a setter method:
public void setRunning(boolean state)
{
this.isRunning = state;
}
Finally, BallCanvas.moveBalls is the new
"update all the things"
method called by the Timer:
public void moveBalls()
{
if (! this.isRunning)
{
return;
}
for (final Ball b : balls)
{
// Remember, `move` no longer calls `paint`... It just
// updates some numbers.
b.move();
}
// Now that the visible state has changed, ask Swing to
// schedule repainting the panel.
repaint();
}
(Note how much simpler iterating over the balls list is now that the list has a proper generic type.
The loop in paintComponent could be made just as straightforward.)
Now the BounceFrame.addBall method is easy:
public void addBall()
{
Ball b = new Ball(canvas);
canvas.add(b);
this.canvas.setRunning(true);
}
With this setup, each press of the space bar adds another ball to the simulation.
I was able to get over 100 balls bouncing around on my 2006 desktop without a hint of flicker.
Also, I could exit the application using the 'X' button or Alt-F4, neither of which responded in the original version.
If you find yourself needing more performance
(or if you just want a better understanding of how Swing painting works),
see
"Painting in AWT and Swing:
Good Painting Code Is the Key to App Performance"
by Amy Fowler.
I would suggest you to use 'Timer' class for running your gameloop.It runs infinitely and you can stop it whenever you want using timer.stop()
You can also set its speed accordingly.
I testing to implement graphics into MVC structure but Im a bit stuck. Here is what I got so far. For now I just want to get the red ball to bounce back and forth. And use the button start to start the thread and button stop to stop the thread that runs the GameLoop in the controller.
But I think Im mixing this up a bit. Would very much appreciate some feedback!
Heres what I got so far:
GameModell
suppose to controll the bouncing. If the location of the ball is under 40 px or above 80 px - multiply the locationX with -1 to make the ball change direction
GameView
Here Im putting the labels on a JFrame. I also want to display the buttons start and stop to controll the thread but I guess they are hidden by the JPanel in TheGraphics class
GameController
Starts and stops the thread with ActionListeners. Contains the GameLoop
TheGraphics
Paints the ball and controll the direction
I guess I got a lot of thing that are all wrong but this is the best I can do at the moment. Would very much apreciate some help!
Thanks!
MAIN:
public class MVCgame {
public static void main(String[] args) {
GameModel gm = new GameModel();
GameView gv = new GameView();
GameController gc = new GameController(gm, gv);
}
}
MODEL:
public class GameModel {
private int multi = 1;
public void setMulti(int locX) {
if(locX < 40 || locX > 80) {
multi = multi * -1;
}
}
public int multi() {
return multi;
}
}
VIEW:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameView extends JFrame {
private JPanel jp = new JPanel();
private JButton start = new JButton("Start");
private JButton stop = new JButton("Stop");
TheGraphics gr = new TheGraphics();
public GameView() {
add(jp);
add(gr);
jp.add(start);
jp.add(stop);
setSize(250, 250);
setVisible(true);
}
public void addListener(ActionListener theListener) {
start.addActionListener(theListener);
stop.addActionListener(theListener);
}
public JButton getStart() {
return start;
}
public JButton getStop() {
return stop;
}
// GUESS I SHOULD PUT THIS IN THE VIEW???
public void paintEllipse(Graphics theG) {
Graphics2D g2d = (Graphics2D) theG;
g2d.setColor(new Color(255, 0, 0));
g2d.fillOval(0, 0, 10, 10);
}
}
CONTROLLER:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class GameController implements Runnable {
GameView gv;
GameModel gm;
private Thread thread;
private boolean running = false;
public GameController(GameModel gm, GameView gv) {
this.gv = gv;
this.gm = gm;
gv.addListener(theListener);
start();
}
ActionListener theListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == gv.getStart()) {
start();
System.out.println("PLAY = ");
} else if (e.getSource() == gv.getStop()) {
stop();
System.out.println("STOP = ");
}
}
};
public synchronized void start() {
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop() {
thread.interrupt();
running = false;
}
// GameLoop
public void run() {
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 10) {
// tick();
delta--;
// repainting the graphics
gv.gr.drawer();
gm.setMulti(gv.gr.drawer());
System.out.println("gv.gr.drawer() = " + gv.gr.drawer() + " gm.multi() " + gm.multi());
// I want to use this value in the model to change the direction
}
if (running) {
}
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
}
}
THE GRAPHICS:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class TheGraphics extends JPanel {
private int locX = 40;
public TheGraphics() {
}
public int drawer() {
locX++;
repaint();
return locX;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(255, 0, 0));
g2d.fillOval(locX, 30, 10, 10);
}
}
GameModell suppose to controll the bouncing. If the location of the ball is under 40 px or above 80 px - multiply the locationX with -1 to make the ball change direction
public void setMulti(int locX) {
if(locX < 40 || locX > 80) {
multi = multi * -1;
}
}
Really bad idea. You should always check position and direction (sign(speed)). Otherwise, your object might get stuck out of bounds always changing direction without moving from place forever.
Apart from this, using the MVC concept is overkill in my eyes and shouldn't be used in such a small project nor in a game. In a game, you should more or less put all three together. Of course you can, but the advantages and disadvantages of the MVC concept don't fit the needs of a game in many ways (except for the GUI, perhaps).
Your main loop might look something like this (you kind of did this already, but why is the tick() commented out in your code?):
while (running) {
update(); // Update all game objects
paint(); // Paint them all
}
Each game object will have its own update() and paint() implementation. You absolutely need to separate the logic of update and paint, even if they are in the same class. So this one:
public int drawer() {
locX++;
repaint();
return locX;
}
is an absolute no-go.
Edit: (Referring your update answer)
You are using the method location() for different purposes. According to the Java name convention, you should rename it getLocation() and setLocation() depending on the use to clarify the code.
(Even if this is not really MVC anymore, I'd let GameFrame implement ActionListener instead of specifying it as variable of GameController.)
One thing you should really change is this one:
private int locX = 0;
public void location(int loc) {
this.locX = (int) loc;
}
Basically, you are duplicating the location value every frame and create unused redundant data. Another problem is, that this might work fine for only one variable, but what if you add more than the position to your model later on? Instead TheGraphics has to render on an instance of the data model, not its values. As long you are using one GameModel
private GameModel model; // set value once at initialisation
and rendering its values in paintComponent will work fine, but if you want to add more than one GameModel (handling GameModel more like a GameObjectModel), you will need to pass it as parameter in the paint method.
public void update() {
repaint();
}
Remove it and try getting around without. A method called from one place forwarding to a different method is a bad idea most of the time, especially if it obfuscates the functionality with a different name.
gv.gr.update();
gv.gr.location(gm.location());
You are first repainting your image and then setting the location? Basically, your game runs one frame behind all the time. Swap that order.
gv.gr.location(gm.location());
gv.gr.repaint();
Will be fine (I already said about location()).
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Update: Just to specify, depending on how I change the rules I can set it so within a couple of generations all cells are either permanently alive or dead. I have checked this by echoing statements to console. HOWEVER, this doesn't reflect in the GUI which shows all cells as always the same color.
I am trying to implement a simple Cellular Automaton to replicate the game of life. This uses the MASON library. My three classes:
Cell.java
package sim.app.gol;
import sim.engine.SimState;
import sim.engine.Steppable;
import sim.field.grid.IntGrid2D;
import sim.util.IntBag;
public class Cell implements Steppable {
public IntGrid2D grid = new IntGrid2D(0,0);
public void step(SimState state) {
Matrix matrix = (Matrix) state;
grid.setTo(matrix.matrix);
for(int x = 0; x < grid.getWidth(); x++) {
for(int y = 0; y < grid.getHeight(); y++) {
IntBag nei = grid.getMooreNeighbors(x, y, 2, 0, false, new IntBag(), new IntBag(), new IntBag());
int count = 0;
for(int i = 0; i < nei.size(); i++) {
count += nei.get(i);
}
int currentState = grid.get(x, y);
if(currentState == 0) {
if(count > 3)
matrix.matrix.set(x, y, 1);
} else if(currentState == 1) {
matrix.matrix.set(x,y,0);
}
}
}
}
}
Matrix.java
package sim.app.gol;
import ec.util.MersenneTwisterFast;
import sim.engine.SimState;
import sim.field.grid.IntGrid2D;
public class Matrix extends SimState {
public final int HEIGHT = 10;
public final int WIDTH = 10;
public IntGrid2D matrix = new IntGrid2D(HEIGHT, WIDTH);
public final int NUM_CELLS = 80;
public Matrix(long seed) {
super(seed);
}
public void start() {
super.start();
// Utils for random number generator
MersenneTwisterFast g = new MersenneTwisterFast();
// We set everything to 0, no cells are active
matrix.setTo(0);
// Populating
for(int i = 0; i < NUM_CELLS; i++) {
int x = 0;
int y = 0;
// We don't want to mark as 'active' a cell that is already active
do {
x = g.nextInt(WIDTH);
y = g.nextInt(HEIGHT);
} while(matrix.get(x, y) == 1);
matrix.set(x, y, 1);
}
schedule.scheduleRepeating(new Cell());
}
public static void main(String[] args) {
doLoop(Matrix.class, args);
System.exit(0);
}
}
MatrixWithUI.java
package sim.app.gol;
import java.awt.Color;
import javax.swing.JFrame;
import sim.app.students.Students;
import sim.display.Console;
import sim.display.Controller;
import sim.display.Display2D;
import sim.display.GUIState;
import sim.engine.SimState;
import sim.portrayal.continuous.ContinuousPortrayal2D;
import sim.portrayal.grid.ObjectGridPortrayal2D;
import sim.portrayal.grid.ValueGridPortrayal2D;
import sim.portrayal.simple.OvalPortrayal2D;
public class MatrixWithUI extends GUIState {
public Display2D display;
public JFrame displayFrame;
public ValueGridPortrayal2D matrixPortrayal = new ValueGridPortrayal2D();
public static void main(String[] args) {
MatrixWithUI mwu = new MatrixWithUI();
Console c = new Console(mwu);
c.setVisible(true);
}
public void start() {
super.start();
setupPortrayals();
}
public void load(SimState state) {
super.load(state);
setupPortrayals();
}
public void setupPortrayals() {
Matrix matrix = (Matrix) state;
matrixPortrayal.setField(matrix.matrix);
matrixPortrayal.setPortrayalForAll(new OvalPortrayal2D());
display.reset();
display.setBackdrop(Color.white);
display.repaint();
}
public void init(Controller c) {
super.init(c);
display = new Display2D(600,600,this);
display.setClipping(true);
displayFrame = display.createFrame();
displayFrame.setTitle("Schoolyard Display");
c.registerFrame(displayFrame);
displayFrame.setVisible(true);
display.attach(matrixPortrayal, "Yard");
}
public void quit() {
super.quit();
if (displayFrame != null) displayFrame.dispose();
displayFrame = null;
display = null;
}
public MatrixWithUI() {
super(new Matrix (System.currentTimeMillis()));
}
public MatrixWithUI(SimState state) {
super(state);
}
public static String getName() {
return "Student Schoolyard Cliques";
}
}
However, for some reason all cells are continuously set to 0 (or off). Any thoughts?
Note: this is a tentative answer as I have no way of verifying it at the moment.
First, let's look at the documentation of ValueGridPortrayal2D. It says:
Like other FieldPortrayal2Ds, this class uses an underlying SimplePortrayal2D to draw each separate element in the grid. A default SimplePortrayal2D is provided which draws squares. In the default, the color for the square is determined by looking up the value of the square in a user-provided color-table, or if there is none, by interpolating it between two user-provided colors. See the setColorTable() and setLevels() methods.
So, if you settle for squares rather than ovals, you can drop this line:
matrixPortrayal.setPortrayalForAll(new OvalPortrayal2D());
And instead, add:
java.awt.Color[] colorTable = new java.awt.Color[2];
colorTable[0] = new java.awt.Color(1.0F,0.0F,0.0F,0.0F);
colorTable[1] = new java.awt.Color(1.0F,0.0F,0.0F,1.0F);
matrixPortrayal.setMap( new SimpleColorMap(colorTable) );
This should give you white squares (transparent on a white backdrop) for 0, and red squares for 1.
If you want to draw ovals, this default implementation of a SimplePortrayal2D that uses a map is not available. The documentation goes further to say:
You can also provide your own custom SimplePortrayal2D (use setPortrayalForAll(...) ) to draw elements as you see fit rather than as rectangles. Your SimplePortrayal2D should expect objects passed to its draw method to be of type MutableDouble.
So we need to override the draw() method and treat the passed object - the cell value - as a MutableDouble (by which I assume they mean the one from org.apache.commons.lang):
matrixPortrayal.setPortrayalForAll(new OvalPortrayal2D() {
public void draw(Object object, Graphics2D graphics, DrawInfo2D info) {
MutableDouble valueObj = (MutableDouble)object;
if ( valueObj.intValue() == 0 ) {
paint = new java.awt.Color(1.0F,0.0F,0.0F,0.0F);
} else {
paint = new java.awt.Color(1.0F,0.0F,0.0F,1.0F);
}
filled = true;
super.draw(object, graphics, info);
}
});
So we have created an anonymous subclass of OvalPortrayal2D. It inherits the fields paint, filled and scale from AbstractShapePortrayal2D. So we override paint (java.awt.Paint, which java.awt.Color extends) with the color we need for the particular value, and make sure the oval is filled.
I've got a problem with the jar file that's supposed to run the game i'm trying to create.
It's basicaly a major issue with the loading of Images so that it works in the Jar aswell as Netbeans' environment.
i'm currently using
imgIcon = new ImageIcon(Core.classLoader.getResource("Images/Boss1.png"));
img = imgIcon.getImage();
this works just fine 4 all my enemies the level background and the shots my various object fire. But if I try to use it on my player class the JAR file becomes unexecutablem, even thought it still works perfectly in NetBeans. So did i screw up in the Player or somewhere else because the Images seem to load with this particular code and only if it's also in the player the jar is unusable.
Player class to be seen here(I think it's got to be a problem in here that i overlooked over and over):
package Entity;
import Shots.PlayerShot;
import bullethellreloaded.Core;
import bullethellreloaded.Screen;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.ArrayList;
import javax.swing.ImageIcon;
public class Player extends Entity{
public int x,y; // public used to make an easy mouse controll;
int xDirection, yDirection;
int health;
private Image img;
private ImageIcon imgIcon;
private Rectangle hitbox;
public static ArrayList shots;
public Player(){
x = 250;
y = 400;
shots = new ArrayList();
health = 5;
imgIcon = new ImageIcon(Core.classLoader.getResource("Images/spaceship (Redshrike,Stephen Challener).png"));
img = imgIcon.getImage();
}
#Override
public void move(){
x += xDirection;
y += yDirection;
// Wallcollision detection, needs to be ajusted for the size of the object.
if(x <= 0)
x = 0;
if(x >= Screen.getScreenWidth())
x = Screen.getScreenWidth();
if(y <= 0)
y = 0;
if(y >= Screen.getScreenHeight())
y = Screen.getScreenHeight();
}
public void setXDirection(int xdir){
xDirection = xdir;
}
public void setYDirection(int ydir){
yDirection = ydir;
}
#Override
public Image getImage(){
return img;
}
#Override
public ImageIcon getImageIcon(){
return imgIcon;
}
#Override
public int getX(){
return x;
}
#Override
public int getY(){
return y;
}
#Override
public Rectangle getHitbox(){
return hitbox;
}
public static ArrayList getShots(){
return shots;
}
public void fire(){
PlayerShot shot = new PlayerShot(getX(), getY()-getImageIcon().getIconHeight()/2, 0, 1, 1,Core.classLoader.getResource("Images/PlayerShot.png"));
shots.add(shot);
}
#Override
public void removeHitbox(){
hitbox = null;
}
#Override
public Rectangle setHitbox(){
int width = getImageIcon().getIconWidth();
int height = getImageIcon().getIconHeight();
hitbox = new Rectangle(getX()+width/2-5, getY()+height/2-5, 1, 1);
return hitbox;
}
public void takeDamage(int dmg){
health -= dmg;
}
public int getHealth(){
return health;
}
drawn in my screen class which is an extended JFrame in the paintComponent that's invoked by the paint(doublebuffering and stuff^^)
in the following code segment:
}else{
g.drawImage(testlevel.getImage(),0,0,this);
g.drawImage(player.getImage(),player.getX(),player.getY(),this);
// painting the Score
g.setColor(Color.white);
g.setFont(new Font("Arial",Font.BOLD,14));
g.drawString("Score: "+ Score.getScore(), getScreenWidth()-100, 50);
// painting out the content of the ArrayLists shots, enemies, enemyShots
try{
for (int i = 0; i < Player.shots.size(); i++){
PlayerShot s = (PlayerShot) Player.shots.get(i);
if (s.getDeleted() != true){
g.drawImage(s.getImage(), s.getX(), s.getY(), this);
}
}
for (int i = 0; i < Enemy.enemies.size(); i++){
Enemy e = (Enemy) Enemy.enemies.get(i);
if (e.getDestroied() != true){
g.drawImage(e.getImage(), e.getX(), e.getY(), this);
}
}
for (int i = 0; i < Enemy.enemyShots.size(); i++){
EnemyShot es = (EnemyShot) Enemy.enemyShots.get(i);
if (es.getDeleted() != true){
g.drawImage(es.getImage(), es.getX(), es.getY(), this);
}
}
}catch(Exception e){}
}
repaint();
}
I hope thats enough information, if that's not the case please let me know.
EDIT:
The question is more like is there another way that might work with my programm or what did i screw up to get this useless JAR thats not even starting.
changeing this (this path only works in netbeans the jar can't find it with this path)
imgIcon = new ImageIcon("src/Images/spaceship (Redshrike,Stephen Challener).png");
to this:
imgIcon = new ImageIcon(Core.classLoader.getResource("Images/spaceship (Redshrike,Stephen Challener).png"));
makes the JAR not runnable (not even a process in the background) whereas it worked just fine with the old path except the fact that the player didn't had an image.
You should pack the resources into the final Jar.
For Eclipse there's a plugin called FatJar that does that (in newer Eclipse versions it's embedded as the option Export>Runnable Jar File).
Usually compilers will not pack resources into the bundle.
I found it out myself, its quite the ridiculous thing.
The Image's name seems to be to long 4 the classLoader if it's used in a jar shorting down the name of the Image let's the program run ^^
AnyWays thx to the ones trying to help me and to the JAR file that reminded me of looking into the simple stuff 1.
Snake
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class Images extends JFrame {
public static void main(String[] args) {
DisplayMode dm = new DisplayMode(800,600,32, DisplayMode.REFRESH_RATE_UNKNOWN);
Images I = new Images();
I.run(dm);
}
private Screen s;
private Image bg;
private Image pic;
private boolean nLoaded;
Animation a;
public void run(DisplayMode dm)
{
nLoaded = false;
s = new Screen();
try{
s.Setfullscreen(dm, this);
LoadPics();
MovieLoop();
try{
Thread.sleep(50000);
}catch(Exception ex){}
}finally{
s.restoreScreen();
}
}
public void MovieLoop(){
long startingtime = System.currentTimeMillis();
long cumTime = startingtime;
while(cumTime-startingtime < 5000)
{
long timepassed = System.currentTimeMillis() - cumTime;
cumTime += timepassed;
a.update(timepassed);
Graphics g = s.getFullScreenWindow().getGraphics();
draw(g);
g.dispose();
try{
Thread.sleep(20);
}catch(Exception ex){}
}
}
public void draw(Graphics g)
{
g.drawImage(bg, 0,0, null);
g.drawImage(a.getImage(),0,0,null); }
//Create Load Pictures
public void LoadPics()
{
bg = new ImageIcon("C:\\Gamepics\\BackgroundImage.jpg").getImage();
pic = new ImageIcon("C:\\Gamepics\\SmileyIcon3.png").getImage();
nLoaded = true;
repaint();
}
public void paint(Graphics g){
if(g instanceof Graphics2D){
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
if(nLoaded)
{
g.drawImage(bg, 0, 0, null);
g.drawImage(pic, 300,300, null);
}
}
}
Im not understanding what I did wrong ive overlooked everything the best I can. Im just practicing a simple animation and I keep getting 3 null pointer exceptions.
Ive researched the best I can and apparently NullPointerExceptions in java have to do with trying to get the size of null arrays? The compiler hasn't marked any of my lists as problems so im a little confused. Any help would be greatly appreciated. All of the errors are commented out. There are three of them and they are in the Images class
Errors:
Exception in thread "main" java.lang.NullPointerException
at Images.MovieLoop(Images.java:45)
at Images.run(Images.java:26)
at Images.main(Images.java:8)
Animation Class
import java.awt.Image;
import java.util.*;
public class Animation {
private ArrayList scenes;
private int sceneindex;
private long movietime;
private long totaltime;
//CONSTRUCTOR
public Animation(){
scenes = new ArrayList();
totaltime =0;
start();
}
//Add scene to array list and sets time for each scene
//For example. If you have 3 scenes you would add t to total time three times. So if you had
//3 Scenes, one for 1s, one for 2s, one for 3s. Total time would be 6s. and you would call addscene 3 times
public synchronized void addScene(Image i, long t)
{
totaltime+=t;
scenes.add(new OneScene(i, totaltime));
}
//start animation from beggininign inignngingingnig
public synchronized void start(){
movietime = 0;
sceneindex = 0;
}
//change scenes
//movie time is the sum of all the time passed from last update
//If you have more than one scene. timepassed gets added to movietime.
//if movietime is greater than or equal to total time(ie. animation is complete) restart the animation
public synchronized void update(long timepassed)
{
if(scenes.size() > 1){
movietime += timepassed;
if(movietime >= totaltime)
{
movietime = 0;
sceneindex = 0;
}
while(movietime > getScene(sceneindex).endTime)
{
sceneindex++;
}
}
}
public synchronized Image getImage(){
if(scenes.size() == 0){
return null;}
else{
return getScene(sceneindex).pic;
}
}
//Getscene
private OneScene getScene(int x){
return (OneScene)scenes.get(x);
}
//Scenes are gonna be
private class OneScene{
Image pic;
long endTime;
public OneScene(Image pic, long endTime)
{
this.pic = pic;
this.endTime = endTime;
}
}
}
I included the animation class because the compiler is highlighting these three method calls as the source of the problem
a.update(timepassed);
MovieLoop();
I.run(dm);
Please Note: This is a really long comment
Let's start with Graphics g = s.getFullScreenWindow().getGraphics(); - getGraphics is NEVER a good idea, this can return null.
You should NEVER try and update any UI component from any thread other the EDT and you should NEVER draw directly to it in this manner, instead, you should be using paintComponent.
You should NEVER dispose of any Graphics context that you did not create yourself, this will prevent other components from been painted.
You should avoid overriding paint, especially of a top level container, if for no other reason, it's not double buffered (the top level container), and you will also be painting over any other child components.
Check out Performing Custom Painting for more details.
You should try using ImageIO instead of ImageIcon. ImageIO will throw exceptions if it can't read the file, where as ImageIcon simply fails silently, no very helpful.