Animation stops after first frame - java

I try to implement an explosion into my game but the animation stops after the first frame. If the player loses his health the explosion should be triggered. I made 3 Classes. Animation Class, an Explosion Class and GameActivity Class. I tried to only post the relevant pieces.
I searched the internet but I could not find a solution to my problem. So I am asking you if you maybe see my mistake and could help me. If more code is needed I will edit it of course. Maybe I am just overlooking something.
Animation Class{
public void setFrames(Bitmap[] frames)
{
this.frames = frames;
currentFrame = 0;
startTime = System.nanoTime();
}
public void setDelay(long d){delay = d;}
public void setFrame(int i){currentFrame= i;}
public void work()
{
long elapsed = (System.nanoTime()-startTime)/1000000;
if(elapsed>delay)
{
currentFrame++;
startTime = System.nanoTime();
}
if(currentFrame == frames.length){
currentFrame = 0;
playedOnce = true; }}
public Bitmap getImage(){
return frames[currentFrame];
}
public int getFrame(){return currentFrame;}
public boolean playedOnce(){return playedOnce; }}
This is the relevant part of the Explosion Class:
Bitmap[] image = new Bitmap[numFrames];
//Bitmap has 4 * 4 frames
for (int i = 0; i < image.length; i++) {
if (i % 4 == 0 && i > 0) row++;
image[i] = Bitmap.createBitmap(spritesheet, (i - (4 * row)) * width,
row * height, width*4, height*4);
}
animation.setFrames(image);
animation.setDelay(10);
}
public void animating() {
if (!animation.playedOnce()) {
animation.work(); }}
If the player loses all his health I call the explosion in GameActivity
if (player.getHealth() < 0) {
// player bitmap disappears
dissapear = true;
//game ends
gameEnded=true;
explosion = new Explosion(BitmapFactory.decodeResource(getResources(),
R.drawable.images),player.getX(),player.getY()-10,9,9,25);
explosion.animating();

This is the main code I use to load the sprite sheet and grab the sprites:
package com.animation.main;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
public class Main extends JFrame{
BufferedImage sprite;
Animator player;
public Main(){
setSize(800, 600);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
init();
}
private void init(){
BufferedImageLoader loader = new BufferedImageLoader();
BufferedImage spriteSheet = null;
try {
spriteSheet = loader.loadImage("insert your sprite sheet path here");
} catch (IOException ex) {
// TODO Auto-generated catch block
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
SpriteSheet ss = new SpriteSheet(spriteSheet);
ArrayList<BufferedImage> sprites = new ArrayList<BufferedImage>();
sprites.add(ss.grabSprite(0, 16, 92, 109));
sprites.add(ss.grabSprite(94, 16, 92, 109));
sprites.add(ss.grabSprite(196, 12, 92, 115));
sprites.add(ss.grabSprite(387, 5, 92, 115));
sprites.add(ss.grabSprite(387, 5, 92, 115));
sprites.add(ss.grabSprite(487, 5, 92, 115));
sprites.add(ss.grabSprite(587, 5, 77, 115));
player = new Animator(sprites);
player.setSpeed(100);
player.start();
}
Image dbImage;
Graphics dbg;
public void paint(Graphics g){
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, null);
}
private void paintComponent(Graphics g) {
if(player != null) {
g.drawImage(player.sprite, 100, 100, null);
player.update(System.currentTimeMillis());
}
repaint();
}
public static void main(String[] args){
Main main = new Main();
}
}
The parameters for the 'sprites.add(ss.grabSprite(0, 16, 92, 109));' code are the x and y value of your sprites top left corner on the spriteSheet and its width and height.
Run the code in the paintComponent class when the players health is less than or equal to 0. It would look something like this:
private void paintComponent(Graphics g) {
if(player != null && playerHealth <= 0) {
g.drawImage(player.sprite, 100, 100, null);
player.update(System.currentTimeMillis());
}
repaint();
}
This is the code I use for the spritesheet:
package com.animation.main;
import java.awt.image.BufferedImage;
public class SpriteSheet {
public BufferedImage spriteSheet;
public SpriteSheet(BufferedImage ss){
this.spriteSheet = ss;
}
public BufferedImage grabSprite(int x, int y, int width, int height){
BufferedImage sprite = spriteSheet.getSubimage(x, y, width, height);
return sprite;
}
THE grab sprite function and the BufferedImage sprite gets the sprite from the spriteSheet.
This is the BufferedImageLoader:
package com.animation.main;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
public class BufferedImageLoader {
public BufferedImage loadImage(String pathRelativeToThis) throws IOException{
URL url = this.getClass().getResource(pathRelativeToThis);
BufferedImage img = ImageIO.read(url);
return img;
}
}
This is the animator class:
package com.animation.main;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
public class Animator {
ArrayList<BufferedImage> frames;
BufferedImage sprite;
private volatile boolean running = false;
private long previousTime, speed;
private int frameAtPause, currentFrame;
public Animator(ArrayList<BufferedImage> frames){
this.frames = frames;
}
public void setSpeed(long speed){
this.speed = speed;
}
public void update(long time){
if(running){
if(time - previousTime >= speed){
// Update animation
currentFrame++;
try{
sprite = frames.get(currentFrame);
} catch(IndexOutOfBoundsException e){
currentFrame = 0;
sprite = frames.get(currentFrame);
}
previousTime = time;
}
}
}
public void start(){
running = true;
previousTime = 0;
currentFrame = 0;
frameAtPause = 0;
}
public void stop(){
running = false;
previousTime = 0;
currentFrame = 0;
frameAtPause = 0;
}
public void pause(){
frameAtPause = 0;
}
public void resume(){
}
}

I solved it. The problem was my Game Activity. I had to change
if (player.getHealth() < 0) {
// player bitmap disappears
dissapear = true;
//game ends
gameEnded=true;
explosion = new Explosion(BitmapFactory.decodeResource(getResources(),
R.drawable.images),player.getX(),player.getY()-10,9,9,25);
explosion.animating();}
to
if (player.getHealth() < 0) {
// player bitmap disappears
dissapear = true;
//game ends
gameEnded=true;
explosion = new Explosion(BitmapFactory.decodeResource(getResources(),
R.drawable.images),player.getX(),player.getY()-10,9,9,25);
}
if (dissapear){explosion.animating();}

Related

Java Game in Fullscreen has way less fps than in window mode

The game runs at about 2000 to 3100 fps in normal window mode. If i set the JFrame component to fullscreen and scale up my JPanel to also the same resolution, the fps drops to 20-70.
(This is a prototype, hardcoded resolutions will be later swapped out)
This is my relevant code (if this is not enough, I can provide more):
Game.java
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame window = new JFrame("Platformer Test");
window.setContentPane(new GamePanel(window));
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(true);
//window.setUndecorated(true);
window.pack();
window.setVisible(true);
}
}
GamePanel.java
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
// custom imports
import GameState.GameStateManager;
#SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, KeyListener{
// dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// Graphic Device (used for fullscreen)
static GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
private JFrame frame;
// game Thread
private Thread thread;
private boolean running;
private double GameTicks = 60;
// image
private BufferedImage image;
private Graphics2D g;
boolean renderFPS = false;
int frames = 0;
// game state manager
private GameStateManager gsm;
public GamePanel(JFrame frame) {
super();
this.frame = frame;
// set Window Size
setFocusable(true);
setFullscreen(true);
}
private void setFullscreen(boolean t) {
if(t) {
setPreferredSize(new Dimension(1920, 1080));
device.setFullScreenWindow(frame);
requestFocus();
}else {
setSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
requestFocus();
}
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
// create image --> Game is drawn on here
image = new BufferedImage(
WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB
);
// get graphics component of game image
g = (Graphics2D) image.getGraphics();
// starts game clock
running = true;
// adds new GameStateManager
gsm = new GameStateManager();
}
#Override
public void run() {
init();
//game loop setup
double ns = 1000000000 / GameTicks;
double delta = 0;
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
int ticks = 0;
// game loop
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
update();
ticks++;
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames + ", ticks: " + ticks);
renderFPS = true;
frames = 0;
ticks = 0;
}
}
}
private void update() {
gsm.update();
}
private void render() {
gsm.render(g);
int fps = 0;
// Draw To Screen
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
//g2.drawImage(image, 0, 0, 1920, 1080, null);
if(renderFPS) {
fps = frames;
}
g2.setColor(Color.red);
g2.drawString("FPS: " + fps, 100,100);
g2.dispose();
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
gsm.keyPressed(e.getKeyCode());
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
gsm.keyReleased(e.getKeyCode());
}
}
Swing is not thread safe, you shouldn't be updating the UI, or the state the UI relies on, from outside the context of the Event Dispatching Thread. This means you shouldn't be using Thread as your "game loop".
See Concurrency in Swing for more details and How to Use Swing Timers for the most common solution.
Don't use JPanel#getGraphics, this is not how painting in Swing is done. Instead, override paintComponent. See Painting in AWT and Swing
and Performing Custom Painting for more details.
Don't use KeyListener, seriously, it's just not worth all the hacking around to make it work. Instead, use the key bindings API
The following, simple, example runs at roughly 171 updates a second (it separates the "timer ticks" and "paint ticks", as in Swing, it's not really possible to know when something is actually rendered to the screen) in both windowed and full screen
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
JFrame frame = new JFrame();
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// device.setFullScreenWindow(frame);
}
});
}
public class GamePanel extends JPanel {
private Timer timer;
private int ticksPerSecond = 0;
private int paintsPerSecond = 0;
private int xDelta = 1;
private Rectangle boxy = new Rectangle(0, 0, 50, 50);
// Graphic Device (used for fullscreen)
public GamePanel() {
timer = new Timer(5, new ActionListener() {
private Instant lastTick;
private int ticks = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick == null) {
lastTick = Instant.now();
}
if (Duration.between(lastTick, Instant.now()).toMillis() >= 1000) {
ticksPerSecond = ticks;
lastTick = Instant.now();
ticks = 0;
}
ticks++;
boxy.x += xDelta;
if (boxy.x + boxy.width > getWidth()) {
boxy.x = getWidth() - boxy.width;
xDelta *= -1;
} else if (boxy.x < 0) {
boxy.x = 0;
xDelta *= -1;
}
boxy.y = (getHeight() - boxy.height) / 2;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
timer.stop();
super.removeNotify();
}
private Instant lastPaint;
private int paints = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lastPaint == null) {
lastPaint = Instant.now();
}
if (Duration.between(lastPaint, Instant.now()).toMillis() >= 1000) {
paintsPerSecond = paints;
lastPaint = Instant.now();
paints = 0;
}
paints++;
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString("Ticks p/s " + ticksPerSecond, 10, 10 + fm.getAscent());
g2d.drawString("Paints p/s " + paintsPerSecond, 10, 10 + fm.getAscent() + fm.getHeight());
g2d.fill(boxy);
g2d.dispose();
}
}
}
If you "really" need absolute control over the painting process, then you should be using a BufferStrategy, see BufferStrategy and BufferCapabilities and the JavaDocs which has an excellent example of how it should be used.
Lots of things might effect the performance of the paint process, for example
A crazy experiment...
So, this example allows you to change the number of entities been rendered on the screen. Each entity is moving in it's own direction and is rotating (no collision detection).
I can get this to run up to roughly 20-25, 0000 entities before I start seeing a (significant) change in the number of updates per second. I rolled it to 100, 000 and it dropped to roughly 42 updates per second.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
GamePanel gamePanel = new GamePanel();
JSlider slider = new JSlider(1, 100000);
slider.setValue(100);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (slider.getValueIsAdjusting()) {
return;
}
gamePanel.setBoxCount(slider.getValue());
}
});
JFrame frame = new JFrame();
frame.add(gamePanel);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
device.setFullScreenWindow(frame);
}
});
}
public class GamePanel extends JPanel {
private Timer timer;
private int ticksPerSecond = 0;
private int paintsPerSecond = 0;
private List<Box> boxes = new ArrayList<>(100);
// Graphic Device (used for fullscreen)
public GamePanel() {
for (int index = 0; index < 100; index++) {
boxes.add(new Box());
}
timer = new Timer(5, new ActionListener() {
private Instant lastTick;
private int ticks = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick == null) {
lastTick = Instant.now();
}
if (Duration.between(lastTick, Instant.now()).toMillis() >= 1000) {
ticksPerSecond = ticks;
lastTick = Instant.now();
ticks = 0;
}
ticks++;
for (Box box : boxes) {
box.update(getSize());
}
repaint();
}
});
}
public void setBoxCount(int count) {
if (count < boxes.size()) {
boxes = boxes.subList(0, count);
return;
}
while (boxes.size() < count) {
boxes.add(new Box());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
timer.stop();
super.removeNotify();
}
private Instant lastPaint;
private int paints = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lastPaint == null) {
lastPaint = Instant.now();
}
if (Duration.between(lastPaint, Instant.now()).toMillis() >= 1000) {
paintsPerSecond = paints;
lastPaint = Instant.now();
paints = 0;
}
paints++;
Graphics2D g2d = (Graphics2D) g.create();
for (Box box : boxes) {
box.paint(g2d);
}
FontMetrics fm = g2d.getFontMetrics();
int yPos = 10 + fm.getAscent();
g2d.drawString("Ticks p/s " + ticksPerSecond, 10, yPos + fm.getAscent());
yPos += fm.getHeight();
g2d.drawString("Paints p/s " + paintsPerSecond, 10, yPos + fm.getAscent());
yPos += fm.getHeight();
g2d.drawString("Count " + boxes.size(), 10, yPos + fm.getAscent());
g2d.dispose();
}
}
private List<Color> colors = new ArrayList<>(Arrays.asList(new Color[]{
Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN,
Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE,
Color.YELLOW
}));
public class Box {
private Color fill;
private Rectangle shape;
private int x;
private int y;
private int xDelta;
private int yDelta;
private int rotationDelta;
private int angle = 0;
public Box() {
Random rnd = new Random();
int width = rnd.nextInt(45) + 5;
int height = rnd.nextInt(45) + 5;
x = rnd.nextInt(400) - width;
y = rnd.nextInt(400) - height;
xDelta = rnd.nextInt(2) + 1;
yDelta = rnd.nextInt(2) + 1;
if (rnd.nextBoolean()) {
xDelta *= -1;
}
if (rnd.nextBoolean()) {
yDelta *= -1;
}
rotationDelta = rnd.nextInt(5);
if (rnd.nextBoolean()) {
rotationDelta *= -1;
}
shape = new Rectangle(x, y, width, height);
Collections.shuffle(colors);
fill = colors.get(0);
shape = new Rectangle(0, 0, width, height);
}
public void update(Dimension bounds) {
x += xDelta;
y += yDelta;
angle += rotationDelta;
if (x + getWidth() > bounds.width) {
x = bounds.width - getWidth();
xDelta *= -1;
} else if (x < 0) {
x = 0;
xDelta *= -1;
}
if (y + getWidth() > bounds.height) {
y = bounds.height - getWidth();
yDelta *= -1;
} else if (y < 0) {
y = 0;
yDelta *= -1;
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return shape.width;
}
public int getHeight() {
return shape.height;
}
public void paint(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
g2d.translate(x, y);
g2d.rotate(Math.toRadians(angle), getWidth() / 2, getHeight() / 2);
g2d.setColor(fill);
g2d.fill(shape);
g2d.dispose();
}
}
}

How do I make a top down view with the ability to rotate with built in java graphics?

I'm trying to make a racing game with the top down view on a static player in the middle of the screen, so instead of moving the player through the map, the map would move around the player. Since it's a racing game, I wanted it to also be somewhat similar to a car, but I've been having trouble with rotating the map around the player and having that work with translations.
I've tried keeping track of the center by adding or subtracting from it, which is what I did for the translations, but it doesn't work with the rotate method. The rotate function wouldn't rotate about the player and instead would rotate the player around some other point, and the translations would snap to a different location from the rotations. I'm sure my approach is flawed, and I have read about layers and such, but I'm not sure what I can do with them or how to use them. Also, any recommendations as to how to use java graphics in general would be greatly appreciated!
This is what I have in my main:
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class game
{
public static void main(String []args)
{
JFrame frame = new JFrame();
final int FRAME_WIDTH = 1000;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Map b = new Map();
frame.add(b,BorderLayout.CENTER);
frame.setVisible(true);
b.startAnimation();
}
}
And this is the class that handles all the graphics
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Map extends JComponent implements Runnable, KeyListener
{
private int speed = 5;
private int xcenter = 500; // starts on player
private int ycenter = 300;
private double angle = 0.0;
private int[] xcords = {xcenter+10, xcenter, xcenter+20};
private int[] ycords = {ycenter-10, ycenter+20, ycenter+20};
private boolean moveNorth = false;
private boolean moveEast = false;
private boolean moveSouth = false;
private boolean moveWest = false;
public Map()
{
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void startAnimation()
{
Thread t = new Thread(this);
t.start();
}
public void paintComponent(Graphics g)
{
g.fillPolygon(xcords, ycords, 3);
// move screen
if(moveNorth)
{
ycenter += speed;
g.translate(xcenter, ycenter);
}
else if(moveEast)
{
angle += ((1 * Math.PI/180) % (2 * Math.PI));
((Graphics2D) g).rotate(angle, 0, 0);
}
else if(moveSouth)
{
System.out.println(xcenter + ", " + ycenter);
ycenter -= speed;
((Graphics2D) g).rotate(angle, 0, 0);
g.translate(xcenter, ycenter);
}
else if(moveWest)
{
angle -= Math.toRadians(1) % (2 * Math.PI);
((Graphics2D) g).rotate(angle, 0, 0);
}
for(int i = -10; i < 21; i++)
{
g.drawLine(i * 50, -1000, i * 50, 1000);
g.drawLine(-1000, i * 50, 1000, i * 50);
}
g.drawOval(0, 0, 35, 35);
}
public void run()
{
while (true)
{
try
{
if(moveNorth || moveEast || moveSouth || moveWest)
{
repaint();
}
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
}
}
public void keyPressed(KeyEvent e)
{
if(e.getExtendedKeyCode() == 68) // d
{
moveEast = true;
}
else if(e.getExtendedKeyCode() == 87) // w
{
moveNorth = true;
}
else if(e.getExtendedKeyCode() == 65) // a
{
moveWest = true;
}
else if(e.getExtendedKeyCode() == 83) // s
{
moveSouth = true;
}
}
public void keyReleased(KeyEvent e)
{
moveNorth = false;
moveEast = false;
moveSouth = false;
moveWest = false;
}
public void keyTyped(KeyEvent e)
{
}
}
You have to keep in mind that transformations are compounding, so if you rotate the Graphics context by 45 degrees, everything painted after it will be rotated 45 degrees (around the point of rotation), if you rotate it again by 45 degrees, everything painted after it will be rotated a total of 90 degrees.
If you want to paint additional content after a transformation, then you either need to undo the transformation, or, preferably, take a snapshot of the Graphics context and dispose of it (the snapshot) when you're done.
You also need to beware of the point of rotation, Graphics2D#rotate(double) will rotate the Graphics around the point of origin (ie 0x0), which may not be desirable. You can change this by either changing the origin point (ie translate) or using Graphics2D#rotate(double, double, double), which allows you to define the point of rotation.
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
enum Direction {
LEFT, RIGHT;
}
protected enum InputAction {
PRESSED_LEFT, PRESSED_RIGHT, RELEASED_LEFT, RELEASED_RIGHT
}
private BufferedImage car;
private BufferedImage road;
private Set<Direction> directions = new TreeSet<>();
private double directionOfRotation = 0;
public TestPane() throws IOException {
car = ImageIO.read(getClass().getResource("/images/Car.png"));
road = ImageIO.read(getClass().getResource("/images/Road.png"));
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), InputAction.PRESSED_LEFT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), InputAction.RELEASED_LEFT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), InputAction.PRESSED_RIGHT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), InputAction.RELEASED_RIGHT);
am.put(InputAction.PRESSED_LEFT, new DirectionAction(Direction.LEFT, true));
am.put(InputAction.RELEASED_LEFT, new DirectionAction(Direction.LEFT, false));
am.put(InputAction.PRESSED_RIGHT, new DirectionAction(Direction.RIGHT, true));
am.put(InputAction.RELEASED_RIGHT, new DirectionAction(Direction.RIGHT, false));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (directions.contains(Direction.RIGHT)) {
directionOfRotation += 1;
} else if (directions.contains(Direction.LEFT)) {
directionOfRotation -= 1;
}
// No doughnuts for you :P
if (directionOfRotation > 180) {
directionOfRotation = 180;
} else if (directionOfRotation < -180) {
directionOfRotation = -180;
}
repaint();
}
});
timer.start();
}
protected void setDirectionActive(Direction direction, boolean active) {
if (active) {
directions.add(direction);
} else {
directions.remove(direction);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(213, 216);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawRoadSurface(g2d);
drawCar(g2d);
g2d.dispose();
}
protected void drawCar(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
int x = (getWidth() - car.getWidth()) / 2;
int y = (getHeight() - car.getHeight()) / 2;
g2d.drawImage(car, x, y, this);
g2d.dispose();
}
protected void drawRoadSurface(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
// This sets the point of rotation at the center of the window
int midX = getWidth() / 2;
int midY = getHeight() / 2;
g2d.rotate(Math.toRadians(directionOfRotation), midX, midY);
// We then need to offset the top/left corner so that what
// we want draw appears to be in the center of the window,
// and thus will be rotated around it's center
int x = midX - (road.getWidth() / 2);
int y = midY - (road.getHeight() / 2);
g2d.drawImage(road, x, y, this);
g2d.dispose();
}
protected class DirectionAction extends AbstractAction {
private Direction direction;
private boolean active;
public DirectionAction(Direction direction, boolean active) {
this.direction = direction;
this.active = active;
}
#Override
public void actionPerformed(ActionEvent e) {
setDirectionActive(direction, active);
}
}
}
}

Why do the dimensions of my JPanel get messed up when I open my game from a PC with different screen resolution?

My laptop's resolution is 1920x1080. I recently opened my game from another laptop with 1360x768. The JPanel changed size and half of the game was outside the screen. I understand that my game dimensions x 4 are bigger than the second laptop's resolution. But, is there a way to make it adjust to every screen? Any ideas?
package Main;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import GameState.GameStateManager;
import Handlers.Keys;
#SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, KeyListener {
// dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 4;
// game thread
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000 / FPS;
// image
private BufferedImage image;
private Graphics2D g;
// game state manager
private GameStateManager gsm;
// other
private boolean recording = false;
private int recordingCount = 0;
private boolean screenshot;
public GamePanel() {
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
running = true;
gsm = new GameStateManager();
}
public void run() {
init();
long start;
long elapsed;
long wait;
// game loop
while (running) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
if (wait < 0) wait = 5;
try {
Thread.sleep(wait);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void update() {
gsm.update();
Keys.update();
}
private void draw() {
gsm.draw(g);
}
private void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
g2.dispose();
if (screenshot) {
screenshot = false;
try {
java.io.File out = new java.io.File("screenshot " + System.nanoTime() + ".gif");
javax.imageio.ImageIO.write(image, "gif", out);
} catch (Exception e) {
}
}
if (!recording)
return;
try {
java.io.File out = new java.io.File("C:\\out\\frame" + recordingCount + ".gif");
javax.imageio.ImageIO.write(image, "gif", out);
recordingCount++;
} catch (Exception e) { }
}
public void keyTyped(KeyEvent key) { }
public void keyPressed(KeyEvent key) {
if (key.isControlDown()) {
if (key.getKeyCode() == KeyEvent.VK_R) {
recording = !recording;
return;
}
if (key.getKeyCode() == KeyEvent.VK_S) {
screenshot = true;
return;
}
}
Keys.keySet(key.getKeyCode(), true);
}
public void keyReleased(KeyEvent key) {
Keys.keySet(key.getKeyCode(), false);
}
}
You should be basing the size of game on the resolution of the screen.
You can easily get the resolution by using:
System.out.println(Toolkit.getDefaultToolkit().getScreenSize());
The next issue is are you using a full screen for your game or is you game displayed in a frame. If you are using a frame with decorations, then you also need to account for the size of the title bar and borders around the frame. This information can only be accessed after the frame has been packed.
frame.pack();
System.out.println("Frame Insets : " + frame.getInsets() );
So now the size of your game board would be the size of the screen less the insets of the frame decorations.

NullPointerException - I don't understand

what is wrong with this code?
I keep getting the error
Exception in thread "Thread-2" java.lang.NullPointerException
at GUI.render(GUI.java:68)
at GUI.run(GUI.java:51)
please help
import graphics.*;
import javax.swing.*;
import java.awt.*;
import java.awt.Canvas;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
public class GUI extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
final static String LABEL_TEXT = "Game";
public static final int Width = 1020;
public static final int Height = 680;
private Thread thread;
private Screen screen;
private Render render;
private BufferedImage img;
private boolean running = false;
private int[] pixels;
public void display() {
screen = new Screen(Height, Width);
vimg = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
}
private void start() {
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
private void stop() {
if(!running)
return;
running = false;
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public void run() {
while (running){
tick();
render();
}
}
public void tick() {
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();;
if(bs == null) {
createBufferStrategy(3);
return;
}
screen.render();
for (int i = 0; i <Width*Height; i++){
pixels[i] = screen.pixels[i];
}
Graphics g = bs.getDrawGraphics();
g.drawImage(img, 0, 0, Width, Height, null);
g.dispose();
bs.show();
}
/**
* Create and show the GUI.
*/
public static void main(String[] args) {
/*Create Canvas*/
GUI game = new GUI();
/*Create and set up the frame*/
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
/*Add content pane to frame*/
frame.add(game);
/*Size and then display the frame.*/
frame.setSize(Width, Height);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
game.start();
}
}
package graphics;
public class Render {
public final int width;
public final int height;
public int[] pixels;
public Render(int height, int width) {
this.width = width;
this.height = height;
pixels = new int[width*height];
}
public void draw(Render render, int xOffset, int yOffset) {
for (int y =0; y<height; y++) {
int yPix = y + yOffset;
for (int x =0; x<width; x++) {
int xPix = x + xOffset;
pixels[xPix+yPix*width] = pixels[x+y*width];
}
}
}
}
package graphics;
import java.util.Random;
import graphics.Render;
public class Screen extends Render{
private Render test;
public Screen(int width, int height) {
super(width, height);
Random rand = new Random();
test = new Render(256, 256);
for (int i =0; i< 256*256; i++){
test.pixels[i] = rand.nextInt();
}
}
public void render() {
draw(test, 0, 0);
}
}
You haven't called display() to initialize your variables before you use them.

Can't get tiles to display

I am following a series of tutorials for Java game development tutorials. I already have a basic knowledge of Java, from thenewboston, if that helps. Anyway, I was stuck on this tutorial: http://www.youtube.com/watch?v=hN1v1ZhITDc&feature=c4-overview-vl&list=PL54DB126285ED0420 The program I am creating for this tutorial is sort of like a 2D Minecraft, and at this point, the program should display a window with several tiles of dirt, stone, and sky in specific order. When I run it, it just displays a blank JFrame and I get these errors:
at mineGameMain.World.draw(World.java:80)
at mineGameMain.GamePanel.draw(GamePanel.java:91)
at mineGameMain.GamePanel.gameRender(GamePanel.java:85)
at mineGameMain.GamePanel.run(GamePanel.java:51)
at java.lang.Thread.run(Unknown Source)
I have three classes, Main:
package mineGameMain;
import javax.swing.JFrame;
public class Main extends JFrame{
GamePanel gp;
public Main(){
gp = new GamePanel();
setSize(500, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(false);
add(gp);
}
public static void main(String[] args){
Main m = new Main();
}
}
GamePanel:
package mineGameMain;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GamePanel extends JPanel implements Runnable{
//Double Buffering
private Image dbImage;
private Graphics dbg;
//JPanel variables
static final int GWIDTH = 500, GHEIGHT = 400;
static final Dimension gameDim = new Dimension(GWIDTH, GHEIGHT);
//Game Variables
private Thread game;
private volatile boolean running = false;
//Game Objects
World world;
public GamePanel(){
world = new World();
setPreferredSize(gameDim);
setBackground(Color.WHITE);
setFocusable(true);
requestFocus();
//Handle all key inputs
addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
}
public void keyReleased(KeyEvent e){
}
public void Typed(KeyEvent e){
}
});
}
public void run(){
while(running){
gameUpdate();
gameRender();
paintScreen();
}
}
private void gameUpdate(){
if(running && game != null){
//Update game state
}
}
private void gameRender(){
if(dbImage == null){
dbImage = createImage(GWIDTH, GHEIGHT);
if(dbImage == null){
System.err.println("dbImage IS STILL NULL SO SHUTUPBEQUIET!");
return;
}else{
dbg = dbImage.getGraphics();
}
}
//Clear the screen
dbg.setColor(Color.WHITE);
dbg.fillRect(0, 0, GWIDTH, GHEIGHT);
//draw Game Methods
draw(dbg);
}
/*Draw all game content in this method*/
private void draw(Graphics g) {
world.draw(g);
}
private void paintScreen(){
Graphics g;
try{
g = this.getGraphics();
if(dbImage != null && g != null){
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
}catch(Exception e){
System.err.println(e);
}
}
public void addNotify(){
super.addNotify();
startGame();
}
private void startGame(){
if(game == null || !running){
game = new Thread(this);
game.start();
running = true;
}
}
public void stopGame(){
if(running){
running = false;
}
}
private void log(String s){
System.out.println(s);
}
}
And finally, World:
package mineGameMain;
import java.awt.Image;
import java.awt.*;
import javax.swing.ImageIcon;
public class World {
private Rectangle[] blocks;
private Image[] blockImg;
private final int arrayNum = 500;
//Block images
private Image BLOCK_DIRT_TOP, BLOCK_DIRT, BLOCK_STONE, BLOCK_SKY;
private int x, y;
public World(){
BLOCK_DIRT_TOP = new ImageIcon("/MineGame/src/mineGameMain/Tile_Grass.png").getImage();
BLOCK_DIRT = new ImageIcon("/MineGame/src/mineGameMain/Tile_Dirt.png").getImage();
BLOCK_STONE = new ImageIcon("/MineGame/src/mineGameMain/Tile_Stone.png").getImage();
BLOCK_STONE = new ImageIcon("/MineGame/src/mineGameMain/Tile_Sky.png").getImage();
blocks = new Rectangle[500];
blockImg = new Image[500];
loadArrays();
}
private void loadArrays(){
for(int i = 0; i < arrayNum; i++){
if(x >= 500){
x = 0;
y += 20;
}
if( i >= 0 && i < 100){
blockImg[i] = BLOCK_SKY;
blocks[i] = new Rectangle(x, y, 20, 20);
}
if( i >= 100 && i < 120){
blockImg[i] = BLOCK_DIRT_TOP;
blocks[i] = new Rectangle(x, y, 20, 20);
}
if( i >= 125 && i < 220){
blockImg[i] = BLOCK_DIRT;
blocks[i] = new Rectangle(x, y, 20, 20);
}
if( i >= 225 && i < 500){
blockImg[i] = BLOCK_STONE;
blocks[i] = new Rectangle(x, y, 20, 20);
}
x += 20;
}
}
public void draw(Graphics g){
for(int i = 0; i < arrayNum; i++){
g.drawImage(blockImg[i], blocks[i].x, blocks[i].y, null);
}
}
}
Thank you for your time. If I did something wrong by posting this or if I left out any details please let me know.
Find below a way to turn your question into an SSCCE (actually it is a fixed version of one)
There are quite a few mistakes you should avoid in your code
No need to implement double buffering yourself, Swing is double buffered by default
All access to the UI should be made on the EDT (the Event Dispacthing Thread), not from a random Thread you created. You should also start your UI from the EDT, using invokeLater
To paint a component, override paintComponent and invoke repaint() whenever you want it to be called.
Never use getGraphics on JComponent, always Graphics objects provided as method-arguments (like in paintComponent).
No need to extends JFrame here --> don't extend if you don't need to (you don't actually add behaviour to the JFrame)
Make sure to call setVisible(true) as your last statement for the JFrame.
You forgot to handle block 120 to 125 and block 220 to 225 (probably causing NullPointerException in your draw method (this is likely the root of your error).
When you post a Java error, make sure to post the whole stacktrace and the message provided above the stacktrace (make also sur that people can identify your line numbers, by adding a comment like // this is line 80 of the World class)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main extends JFrame {
GamePanel gp;
public Main() throws MalformedURLException {
gp = new GamePanel();
setSize(500, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(gp);
pack();
setVisible(true);
}
public static class World {
private Rectangle[] blocks;
private Image[] blockImg;
private final int arrayNum = 500;
// Block images
private Image BLOCK_DIRT_TOP, BLOCK_DIRT, BLOCK_STONE, BLOCK_SKY;
private int x, y;
public World() throws MalformedURLException {
BLOCK_DIRT_TOP = new ImageIcon(new URL("http://i909.photobucket.com/albums/ac298/XxEnoAsaIxX/grass2test.png?t=1303852677"))
.getImage();
BLOCK_DIRT = new ImageIcon(new URL(
"http://i909.photobucket.com/albums/ac298/XxEnoAsaIxX/sparsedirtandgrasstile.png?t=1300172998")).getImage();
BLOCK_STONE = new ImageIcon(new URL("http://www.stonetilesupply.com/v/vspfiles/photos/MARBL-BTTCNCLASSICO-2S.jpg")).getImage();
BLOCK_SKY = new ImageIcon(new URL("http://lacoste.scene7.com/is/image/lacoste/swatch_10_CH0783-00_SVH_24?$swatch$")).getImage();
blocks = new Rectangle[500];
blockImg = new Image[500];
loadArrays();
}
private void loadArrays() {
for (int i = 0; i < arrayNum; i++) {
if (x >= 500) {
x = 0;
y += 20;
}
if (i >= 0 && i < 100) {
blockImg[i] = BLOCK_SKY;
blocks[i] = new Rectangle(x, y, 20, 20);
}
if (i >= 100 && i < 120) {
blockImg[i] = BLOCK_DIRT_TOP;
blocks[i] = new Rectangle(x, y, 20, 20);
}
// Here missing block for 120 to 125
if (i >= 120 && i < 125) {
blockImg[i] = BLOCK_STONE;
blocks[i] = new Rectangle(x, y, 20, 20);
}
if (i >= 125 && i < 220) {
blockImg[i] = BLOCK_DIRT;
blocks[i] = new Rectangle(x, y, 20, 20);
}
// Here missing block for 220 to 225
if (i >= 220 && i < 225) {
blockImg[i] = BLOCK_SKY;
blocks[i] = new Rectangle(x, y, 20, 20);
}
if (i >= 225 && i < 500) {
blockImg[i] = BLOCK_STONE;
blocks[i] = new Rectangle(x, y, 20, 20);
}
x += 20;
}
}
public void draw(Graphics g) {
for (int i = 0; i < arrayNum; i++) {
g.drawImage(blockImg[i], blocks[i].x, blocks[i].y, null);
}
}
}
public static class GamePanel extends JPanel {
// JPanel variables
// Game Objects
World world;
public GamePanel() throws MalformedURLException {
world = new World();
setPreferredSize(new Dimension(500, 400));
setBackground(Color.WHITE);
setFocusable(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
world.draw(g);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new Main();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}

Categories