I'm making a CUI game in java and want to make an FPS counter in the game. Can anyone give me the code for a simple FPS counter which updates constantly?
I have tried
package Main;
public class Main {
public static void main(String[] args) {
double old_time = System.nanoTime();
System.out.println("FPS: " + get_fps(old_time));
}
private double get_fps(double old_time) {
double new_time = System.nanoTime();
double delta = - old_time;
double fps = 1 / (delta * 1000);
old_time = new_time;
return fps;
}
}
NOTE: The output should be like
FPS: FPS
Just in case you really are starting out, I thought I'd share a really basic example of an FPS counter in a loop. I'm not sure what a 'CUI' game is, but this illustrates a basic render/update loop. Why a snow-storm? It's cold here today.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class FpsDemo extends JFrame implements Runnable {
// statics
private final static int SCREENHEIGHT = 800;
private final static int SCREENWIDTH = 1200;
private final static int NOF_SNOWFLAKES = 1000;
private final static int NOF_SPEEDS = 5;
private final static int BACKGROUND = colour(0xB, 0x16, 0x2C);
private final static int WHITE = colour(0xFF, 0xFF, 0xFF);
// for each snowflake [i], [i][0] = x coord, [i][1] = y coord and [i][2] = speed
private final static int snow[][] = new int[NOF_SNOWFLAKES][3];
// for each column, a height
private final static int pile[] = new int[SCREENWIDTH];
/* Code entry point */
public static void main(String[] args) {
new FpsDemo().run();
}
/* Instantiates an instance of this class */
public FpsDemo() {
super("My FPS Demo");
setSize(SCREENWIDTH, SCREENHEIGHT);
setVisible(true);
}
/* Contains the main loop */
#Override
public void run() {
// setup an off-screen buffer to draw to
BufferedImage screen = new BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_RGB);
int[] screenData = ((DataBufferInt) screen.getRaster().getDataBuffer()).getData();
Graphics gs = getGraphics();
gs.setColor(Color.white);
// prepare some colours
int[] shades = new int[NOF_SPEEDS+1];
for(int i=0; i<NOF_SPEEDS; i++) {
int j = 50 + i * ((255-50)/NOF_SPEEDS);
shades[NOF_SPEEDS-i] = colour(j, j, j);
}
// arrange our initial data
Random random = new Random();
Arrays.fill(pile, SCREENHEIGHT);
for(int i=0; i<NOF_SNOWFLAKES; i++) {
snow[i][0] = random.nextInt(SCREENWIDTH);
snow[i][1] = random.nextInt(SCREENHEIGHT);
snow[i][2] = random.nextInt(4) + 1;
}
// loop forever (well, until an ArrayOutOfBoundsException...)
double fps = 0;
int frameCount = 0;
long startTime = System.nanoTime();
int throttle = NOF_SPEEDS;
while(true) {
Arrays.fill(screenData, BACKGROUND);
// For each pile
for(int x=0; x<SCREENWIDTH; x++) {
for(int y=pile[x], j=x+pile[x]*SCREENWIDTH; y<SCREENHEIGHT; y++, j+=SCREENWIDTH) {
screenData[j] = WHITE;
}
}
// For each snow flake
for(int i=0; i<NOF_SNOWFLAKES; i++) {
// Render the snow flake to the off-screen buffer
int j = snow[i][0]+(snow[i][1]*SCREENWIDTH);
screenData[j] = shades[snow[i][2]];
// Update for next render
if(snow[i][2] < throttle) {
// check the next row down
if(snow[i][1]+1 < pile[ snow[i][0] ] && snow[i][1] < SCREENHEIGHT) {
snow[i][1]++;
// slide left
} else if(snow[i][0]>1 && snow[i][1]+1 < pile[ snow[i][0]-1 ] && snow[i][1] < SCREENHEIGHT) {
snow[i][1]++;
snow[i][0]--;
// slide right
} else if(snow[i][0]<SCREENWIDTH-1 && snow[i][1]+1 < pile[ snow[i][0]+1 ] && snow[i][1] < SCREENHEIGHT) {
snow[i][1]++;
snow[i][0]++;
// we hit the pile
} else {
pile[snow[i][0]]--;
snow[i][1] = random.nextInt(SCREENWIDTH);
snow[i][1] = 0;
snow[i][2] = random.nextInt(4) + 1;
}
}
}
// Flip the image buffer across in one go.
if (gs!=null) {
gs.drawImage(screen, 0, 0, null);
}
// Select less snow flakes each loop so we get a parallax effect
throttle--;
if(throttle == 0) {
throttle = NOF_SPEEDS;
}
// Update FPS
frameCount++;
long now = System.nanoTime();
// has it been more than 1 second (1bn nanos)?
long wallclock = now - startTime;
if(wallclock > 1000000000) {
fps = frameCount / (wallclock/1000000000);
startTime = now;
frameCount = 0;
this.setTitle("FPS: " + fps);
}
// You could try calculating a delay to achieve a specific FPS
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/* Convenience method to convert RGB values (in the range 0-255) into a single integer */
private static int colour(int r, int g, int b) {
return (r*65536) + (g*256) + b;
}
}
Related
//Generator class
public class Generator {
double jump;
double sizeModifier;
int iterationRate,range;
ComplexNumber c;
public Generator(double jump) {
this.jump=jump;
this.sizeModifier=40;
this.iterationRate=10;
this.range=100;
c=new ComplexNumber();
}
public void generateSet(){
DrawSet ds = new DrawSet();
int ticker=0;
for(double i=-2*range;i<=range;i+=jump){
for(double j=-2*range;j<=range;j+=jump){
c= new ComplexNumber((i/range),(j/range));
double fz=c.square().mod()+c.mod();
//System.out.println("c mod is: "+c.mod());
//System.out.println("fz is: "+fz);
if (fz < 2) {
for(int k=0;k<=iterationRate;k++) {
//System.out.println("nc:"+nc);
ticker++;
//System.out.println("ticker:"+ticker);
if(ticker==iterationRate) {
ds.addPoint(i + 450, j + 450);
}
if(fz>=2){
break;
}
else {
fz = Math.pow(fz, 2) + 1;
}
}
}
ticker=0;
}
}
}
//Drawset class
public class DrawSet extends JPanel {
private ArrayList<Point> Points;
private ArrayList<Point> nPoints;
GraphicWindow gw;
public DrawSet(){
this.Points=new ArrayList<>();
this.nPoints=new ArrayList<>();
gw = new GraphicWindow(1000,1000);
gw.add(this);
}
public void addPoint(double x,double y){
int ix=(int)x;
int iy=(int)y;
//int iwidth=(int)width*sizeModifier;
//int iheight=(int)height*sizeModifier;
Point a=new Point(ix,iy);
Points.add(a);
//System.out.println(Points.size());
}
public void paintComponent(Graphics g) {
int pointSize = 1;
super.paintComponents(g);
System.out.println(Points.size());
for (int i = 0; i < Points.size(); i++) {
g.setColor(Color.BLACK);
g.drawOval((int) Points.get(i).getX(), (int) Points.get(i).getY(), pointSize, pointSize);
}
}
The basic problem is that fz is never <2 after just 10 iterations, so no points are drawn. Why is this? What equation should I be implementing?
I am using a complex number to generate the value. The class which I use can be seen here:
https://github.com/abdulfatir/jcomplexnumber/
(or on the stack overflow forum Does Java have a class for complex numbers? by Mr Abdul Fatir)
Okay, observationally
Generator should not be creating a new instance of DrawSet
DrawSet should not be creating a new instance of GraphicWindow
These are the responsibilities of these classes or methods. This is highly coupling your code and generating side effects.
Instead, generate should pass back the points it creates and these should then be applied in what ever way you want, Generator shouldn't care.
Because I don't have access to your full code, I've just hacked together a random series of points instead to illustrate the point.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
DrawSet drawSet = new DrawSet();
Generator generator = new Generator(100);
drawSet.setPoints(generator.generateSet());
JFrame frame = new JFrame();
frame.add(drawSet);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawSet extends JPanel {
private ArrayList<Point> points;
private ArrayList<Point> nPoints;
public DrawSet() {
this.points = new ArrayList<>();
this.nPoints = new ArrayList<>();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public void setPoints(ArrayList<Point> points) {
this.points = points;
repaint();
}
// public void addPoint(double x, double y) {
// int ix = (int) x;
// int iy = (int) y;
// //int iwidth=(int)width*sizeModifier;
// //int iheight=(int)height*sizeModifier;
// Point a = new Point(ix, iy);
// Points.add(a);
// //System.out.println(Points.size());
// }
#Override
protected void paintComponent(Graphics g) {
int pointSize = 5;
super.paintComponents(g);
for (int i = 0; i < points.size(); i++) {
g.setColor(Color.BLACK);
g.drawOval((int) points.get(i).getX(), (int) points.get(i).getY(), pointSize, pointSize);
}
}
}
public class Generator {
double jump;
double sizeModifier;
int iterationRate, range;
//ComplexNumber c;
public Generator(double jump) {
this.jump = jump;
this.sizeModifier = 40;
this.iterationRate = 10;
this.range = 100;
//c = new ComplexNumber();
}
public ArrayList<Point> generateSet() {
Random rnd = new Random();
ArrayList<Point> points = new ArrayList<>(range);
for (int index = 0; index < range; index++) {
int x = (int)(490 * rnd.nextDouble());
int y = (int)(490 * rnd.nextDouble());
points.add(new Point(x, y));
}
return points;
// DrawSet ds = new DrawSet();
// int ticker = 0;
// for (double i = -2 * range; i <= range; i += jump) {
// for (double j = -2 * range; j <= range; j += jump) {
// c = new ComplexNumber((i / range), (j / range));
// double fz = c.square().mod() + c.mod();
// //System.out.println("c mod is: "+c.mod());
// //System.out.println("fz is: "+fz);
// if (fz < 2) {
// for (int k = 0; k <= iterationRate; k++) {
// //System.out.println("nc:"+nc);
// ticker++;
// //System.out.println("ticker:"+ticker);
// if (ticker == iterationRate) {
// ds.addPoint(i + 450, j + 450);
//
// }
// if (fz >= 2) {
// break;
// } else {
// fz = Math.pow(fz, 2) + 1;
// }
// }
// }
// ticker = 0;
// }
// }
}
}
}
I've also made the pointSize bigger, so you can actually see the results, although I'd consider filling the points.
If the Generator is takes a long time to perform its calculations, you might consider using a SwingWorker to perform the operation. This will allow to offload the operation to seperate thread and not block the main UI thread. You can then use the SwingWorker to either feed back each individual point as it's calculated or all the points when the whole generation is done, back to the main UI thread in a safe manner.
See Worker Threads and SwingWorker for more details
Why is this?
Swing is lazy. It won't make repaints or updates to the UI unless it thinks it needs to. So, if you change some state the UI is depending on, you need to nudge Swing to encourage it to make a new paint pass. In the simplest terms, this means calling repaint on the component which is has changed
I'm attempting to use these methods to find a color in a rectangular area on the screen. However there are sometimes millions of pixels on a screen, and I'm only achieving about 35 iterations of getColor a second at the moment. There must be something in my code causing this to run extremely slowly.
How can I scan my screen quicker than this? Ideally I'd like to scan the entire screen for a color in less than a second, not 8 hours as it stands now :P
Here are my two methods.
public static int getColor(int x, int y){
try{
return(robot.getPixelColor(x, y).getRGB() * -1);
}catch(Exception e){
System.out.println("getColor ERROR");
return 0;
}
}
//returns first instance of color,
//Begins top left, works way down to bottom right
public static Point findColor(Box searchArea, int color){
System.out.println("Test");
if(searchArea.x1 > searchArea.x2){
int temp = searchArea.x1;
searchArea.x1 = searchArea.x2;
searchArea.x2 = temp;
}
if(searchArea.y1 > searchArea.y2){
int temp = searchArea.y1;
searchArea.y1 = searchArea.y2;
searchArea.y2 = temp;
}
for(int i = searchArea.x1;i <=searchArea.x2; i++){
for(int j = searchArea.y1;j<=searchArea.y2;j++){
if(getColor(i, j) == color){
return new Point(i, j);
}
System.out.println(i + " " + j);
}
}
return new Point(-1, -1);
}
Robot#getColor will be very slow, especially when used in this manner.
A better solution would be to grab a screen shot (even in small chunks) and process the resulting BufferedImage.
Using the following example I got...
Took 0 seconds to scan image
Took 3 seconds to scan screen
For an area of 10x10
Example code...
import java.awt.AWTException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestColorGrab {
private static Robot robot;
public static void main(String[] args) {
try {
robot = new Robot();
} catch (AWTException ex) {
Logger.getLogger(TestColorGrab.class.getName()).log(Level.SEVERE, null, ex);
}
new TestColorGrab();
}
public TestColorGrab() {
Rectangle bounds = new Rectangle(0, 0, 10, 10);
long start = System.currentTimeMillis();
scanImageArea(bounds);
System.out.println("Took " + ((System.currentTimeMillis() - start) / 1000), TimeUnit.SECONDS) + " seconds to scan image");
start = System.currentTimeMillis();
scanRobotArea(bounds);
System.out.println("Took " + ((System.currentTimeMillis() - start) / 1000) + " seconds to scan screen");
}
public static int getColor(int x, int y) {
try {
return (robot.getPixelColor(x, y).getRGB() * -1);
} catch (Exception e) {
System.out.println("getColor ERROR");
return 0;
}
}
public static void scanRobotArea(Rectangle searchArea) {
for (int i = searchArea.x; i < searchArea.x + searchArea.width; i++) {
for (int j = searchArea.y; j < searchArea.y + searchArea.height; j++) {
getColor(i, j);
}
}
}
public static void scanImageArea(Rectangle searchArea) {
BufferedImage image = robot.createScreenCapture(searchArea);
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
image.getRGB(x, y);
}
}
}
}
Take out the print. You are writing to the console a million times.
I'm trying to write my first game in Java. I followed some tutorials and learned how to load and update a background using a Canvas and how to load and move a player sprite. I did these two separately and they worked fine, but when I put the two together and try to move the player, the game slows down to the point that it is unplayable. This only happens when I hold down an arrow key to move the player; the game actually runs "smoothly" if I rapidly tap the arrow key. After quite a bit of testing, I'm convinced that the problem occurs when the background is redrawn each frame. Any other improvements would also be appreciated.
Code (All of it):
Game.Java:
package Game;
import Level.Level;
import Player.Player;
import Sprites.SpriteSheetLoader;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
// Set dimensions of the game.
public static final int HEIGHT = 320;
public static final int WIDTH = 480;
public static final int SCALE = 2;
public static Dimension GAME_DIM = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
public SpriteSheetLoader loader;
public Screen screen;
public Level level;
public InputHandler input = new InputHandler(this);
public Player player = new Player();
private boolean running = false;
private boolean moving = true;
private int FPS = 60;
private long targetTime = 1000 / FPS;
// Set character's starting position at the center. I have no idea why I had to add the "- 50" to each value.
public int x = GAME_DIM.width / 2 - 50;
public int y = GAME_DIM.height / 2 - 50;
public int xScroll = 0;
public int yScroll = 0;
public int col = 0;
public int row = 0;
public int ticks = 0;
public int frame = 0;
public static void main(String[] args) {
Game game = new Game();
game.setPreferredSize(new Dimension(GAME_DIM));
game.setMaximumSize(new Dimension(GAME_DIM));
game.setMinimumSize(new Dimension(GAME_DIM));
JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
public void start() {
running = true;
new Thread(this).start();
}
public Game() {
}
public void init() {
loader = new SpriteSheetLoader();
screen = new Screen(WIDTH, HEIGHT);
level = new Level(16, 16);
}
public void run() {
init();
long start, elapsed, wait;
while (running) {
start = System.nanoTime();
render();
tick();
elapsed = System.nanoTime() - start;
//System.out.println("Elapsed: " + elapsed);
wait = targetTime - elapsed / 1000000;
if(wait < 0) {
wait = 5;
}
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
running = false;
}
public void tick() {
// Movement
if (input.right) {
xScroll++;
player.setAnimation(player.walkRight);
//x++;
row = 2;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.left) {
xScroll--;
player.setAnimation(player.walkLeft);
//x--;
row = 1;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.up) {
yScroll--;
player.setAnimation(player.walkUp);
//y--;
row = 3;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.down) {
yScroll++;
player.setAnimation(player.walkDown);
//y++;
row = 0;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
}
if (!input.down && !input.left && !input.right && !input.up) {
player.setAnimation(player.stand);
frame = 0;
ticks = 1;
moving = false;
}
//System.out.println("Tick: " + ticks);
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
requestFocus();
return;
}
do {
Graphics g = bs.getDrawGraphics();
try {
for (int i = 0; i < ticks; i++) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.drawImage(player.Player(frame, row), x, y, null);
level.renderBackground(xScroll, yScroll, screen);
for (int y = 0; y < this.screen.h; y++) {
for (int x = 0; x < screen.w; x++) {
pixels[x + (y * WIDTH)] = screen.pixels[x + (y * screen.w)];
}
}
}
} finally {
g.dispose();
}
bs.show();
this.update(bs.getDrawGraphics());
} while (bs.contentsLost());
// Graphics g = bs.getDrawGraphics();
//
// g.dispose();
// bs.show();
}
}
InputHandler.Java:
package Game;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class InputHandler implements KeyListener {
public boolean up = false;
public boolean down = false;
public boolean left = false;
public boolean right = false;
public InputHandler(Game game) {
game.addKeyListener(this);
}
public void toggle(KeyEvent ke, boolean pressed) {
int keyCode = ke.getKeyCode();
if(keyCode == KeyEvent.VK_UP) up = pressed;
if(keyCode == KeyEvent.VK_DOWN) down = pressed;
if(keyCode == KeyEvent.VK_LEFT) left = pressed;
if(keyCode == KeyEvent.VK_RIGHT) right = pressed;
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
toggle(e, true);
}
public void keyReleased(KeyEvent e) {
toggle(e, false);
}
}
Screen.Java:
package Game;
import Sprites.Sprite;
public class Screen {
public int w, h;
int xOffset = 0;
int yOffset = 0;
public int[] pixels;
public Screen(int w, int h) {
this.w = w; // 480
this.h = h; // 320
pixels = new int[w * h]; // 153600
}
public void renderSprite(int xPos, int yPos, Sprite sprite) {
int height = sprite.h;
int width = sprite.w;
xPos -= xOffset;
yPos -= yOffset;
for(int y = 0; y < height; y++) {
if(yPos + y < 0 || yPos + y >= h) continue;
for(int x = 0; x < width; x++) {
if(xPos + x < 0 || xPos + x >= w) continue;
int col = sprite.pixels[x + (y * height)];
if(col != -65281 && col < 0) pixels[(x + xPos) + (y + yPos) *w]= col;
}
}
}
public void setOffs(int xOffs, int yOffs) {
xOffset = xOffs;
yOffset = yOffs;
}
}
Level.Java:
package Level;
import Game.Screen;
import Sprites.Sprite;
import Sprites.Sprites;
import Tiles.Tile;
public class Level {
int w, h;
public int[] tiles;
public Level(int w, int h) {
this.w = w;
this.h = h;
tiles = new int[w * h];
loadMap(0, 0, 0, 0);
}
public void renderBackground(int xScroll, int yScroll, Screen screen) {
int xo = xScroll >> 4;
int yo = yScroll >> 4;
int w = (screen.w + 15) >> 4;
int h = (screen.h + 15) >> 4;
screen.setOffs(xScroll, yScroll);
for(int y = yo; y <= h + yo; y++) {
for(int x = xo; x <= w + xo; x++) {
getTile(x, y).render(x, y, screen);
}
}
screen.setOffs(0, 0);
}
public Tile getTile(int x, int y) {
if(x < 0 || y < 0 || x >= w || y >= h) return Tile.rockTile;
return Tile.tiles[tiles[x + y * w]];
}
public void loadMap(int x0, int y0, int x1, int y1) {
Sprite sprite = Sprites.level[x0][y0];
for(int y = 0; y < sprite.h; y++) {
for(int x = 0; x < sprite.w; x++) {
if(sprite.pixels[x + y * sprite.h] == -9276814) {
tiles[x + x1 + (y + y1) * h] = Tile.rockTile.id;
} else {
tiles[x + x1 + (y + y1) * h] = Tile.grassTile.id;
}
}
}
}
}
Player.Java:
package Player;
import Animation.Animation;
import Sprites.Sprite;
import java.awt.image.BufferedImage;
public class Player {
// Images for each animation
private BufferedImage[] walkingLeft = {Sprite.getSprite(0, 1), Sprite.getSprite(1, 1), Sprite.getSprite(2, 1)}; // Gets the upper left images of my sprite sheet
private BufferedImage[] walkingRight = {Sprite.getSprite(0, 2), Sprite.getSprite(1, 2), Sprite.getSprite(2, 2)};
private BufferedImage[] walkingUp = {Sprite.getSprite(0, 3), Sprite.getSprite(1, 3), Sprite.getSprite(2, 3)};
private BufferedImage[] walkingDown = {Sprite.getSprite(0, 0), Sprite.getSprite(1, 0), Sprite.getSprite(2, 0)};
private BufferedImage[] standing = {Sprite.getSprite(1, 0)};
// These are animation states.
public Animation walkLeft = new Animation(walkingLeft, 10);
public Animation walkRight = new Animation(walkingRight, 10);
public Animation walkUp = new Animation(walkingUp, 10);
public Animation walkDown = new Animation(walkingDown, 10);
public Animation stand = new Animation(standing, 10);
// This is the actual animation
public Animation animation = stand;
public BufferedImage Player(int x, int y) {
BufferedImage player = Sprite.getSprite(x, y);
return player;
}
public void update() {
animation.update();
}
public void render() {
}
public void setAnimation(Animation animation) {
this.animation = animation;
}
}
Sprite.Java:
package Sprites;
import Game.Game;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Sprite {
public int w, h;
public int[] pixels;
public static BufferedImage sprite = null;
public Sprite(int w, int h) {
this.w = w;
this.h = h;
pixels = new int[w * h];
}
public void clear(int color) {
for(int i = 0; i < pixels.length; i++) {
pixels[i] = color;
}
}
private static BufferedImage spriteSheet;
private static final int TILE_SIZE = 80;
public static BufferedImage loadSprite() {
try {
sprite = ImageIO.read(Game.class.getResource("/valkyrie.png"));
} catch (IOException e) {
e.printStackTrace();
}
return sprite;
}
public static BufferedImage getSprite(int xGrid, int yGrid) {
if(spriteSheet == null) {
spriteSheet = loadSprite();
}
// xGrid and yGrid refer to each individual sprite
return spriteSheet.getSubimage(xGrid * TILE_SIZE, yGrid * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
This is going to require double buffering. Any game with a lot going on needs double buffering.
How do you double buffer in java for a game?
Although I couldn't go through the code completely, it seems you do not do double buffering which affect performance drastically.
Try this in the relevant part of your program:
JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDoubleBuffered(true); //added line, rest is the same
game.start();
You really should use Timer. It will solve all your problems.
Every tick, you redraw all what you need.
And every tick, you should just check, which keys are pressed and which are not, instead of adding listeners. To keep tracking this, you always have to remember the keys pressed "before".
You can even create two Timers, one for graphic redraw and one for game logic.
Even timers can be delayed or something, the usual approach is to find out, how much time elapsed (System.nanoTime for example) and count how much of game logic you should forward to keep game always unlaggy and fluent.
I have a JPanel 200x200.
I trying to create a function that will generate random parabola's with the bounds of the JPanel, with a constraint that the height can't be lower than a 100 (middle of the screen), I basically want to move a shape around these parabolas
Here is some code I'm using to get started:
Random random = new Random(); int y; int x;
int size = random.nextInt(10);
int translation = random.nextInt(50);
int height = random.nextInt(100) - 200; //between 100 and 200
//Parabola functions : y = ((x/7 - 30))^2
// x and y are coordiates of where the shape is drawn
while(y != 200){
y = (float)Math.pow((float)xloc / size - translation ,2) + height;
x++;
}
I've been researching about FlatteningPathIterator but not too sure how to use them. my function
y = (float)Math.pow((float)xloc / size - translation ,2) + height;`
prints parabola's sometimes outside the bounds, how would i edit it to print parabola's inside the bounds?
There is a Java Swing shape generator for this called Quad2dCurve. The getPathIterator call gives you an enumerator for points on the curve.
Here is some example code:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.Random;
import javax.swing.*;
final class TestCanvas extends JComponent {
int size = 200;
int n = 10;
float[] ph = new float[n];
float[] pw = new float[n];
float[] px = new float[n];
Random gen = new Random();
TestCanvas() {
makeRandomParabolas();
setFocusable(true);
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
makeRandomParabolas();
repaint();
float [] coords = new float [6];
for (int i = 0; i < n; i++) {
PathIterator pi = getQuadCurve(i).getPathIterator(null, 0.1);
System.out.print(i + ":");
while (!pi.isDone()) {
switch (pi.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
System.out.print(" move to");
break;
case PathIterator.SEG_LINETO:
System.out.print(" line to");
break;
default:
System.out.print(" unexpected");
break;
}
System.out.println(" (" + coords[0] + "," + coords[1]+")");
pi.next();
}
System.out.println();
}
}
});
}
QuadCurve2D.Float getQuadCurve(int i) {
return new QuadCurve2D.Float(px[i] - pw[i], size,
px[i], size - (2 * ph[i]),
px[i] + pw[i], size);
}
void makeRandomParabolas() {
for (int i = 0; i < n; i++) {
float x = 0.2f + 0.6f * gen.nextFloat();
px[i] = size * x;
pw[i] = size * (Math.min(x, 1 - x) * gen.nextFloat());
ph[i] = size * (0.5f + 0.5f * gen.nextFloat());
}
}
#Override
protected void paintComponent(Graphics g0) {
Graphics2D g = (Graphics2D) g0;
for (int i = 0; i < n; i++) {
g.draw(getQuadCurve(i));
}
}
}
public class Main extends JFrame {
public Main() {
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(new TestCanvas());
getContentPane().setPreferredSize(new Dimension(200, 200));
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Main().setVisible(true);
}
});
}
}
I've got a problem, I'm programming in Java and when I went to run it, It came up with a list of about 6 errors. These
Exception in thread "Display" java.lang.ArrayIndexOutOfBoundsException: 64
at com.cmnatic.mld.graphics.Screen.clear(Screen.java:27)
at com.cmnatic.mld.Game.render(Game.java:107)
at com.cmnatic.mld.Game.run(Game.java:77)
at java.lang.Thread.run(Unknown Source)
If it helps, here is my code (ofc it does)
Game.java:
package com.cmnatic.mld;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import com.cmnatic.mld.graphics.Screen;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static int width = 300; // 300 * 3 = 900
public static int height = width / 16 * 9; //168.75 * 3 = 506.25
public static int scale = 3;
public static String title = "CMNatic's MLD Entry #49";
private Thread thread;
private JFrame frame;
private boolean running = false;
private Screen screen;
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private int[] pixels =((DataBufferInt)image.getRaster().getDataBuffer()).getData();
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPrefferedSize(size);
screen = new Screen(width, height);
frame = new JFrame();
this.setSize(900,506);
}
private void setPrefferedSize(Dimension size) {
}
public synchronized void start() {
running = true;
thread = new Thread(this , "Display");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
final double ns = 100000000.0 / 60.0; // nano-seconds = 1000000000 (9 0'S) / 60.0
double delta = 0;
int frames = 0;
int updates = 0;
while (running) {
long now = System.nanoTime();
delta += (now-lastTime) / ns; //nano-seconds (ns)
lastTime = now;
while (delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(updates + " ups, " + frames + " fps");
frame.setTitle(title + " | " + updates + "ups, " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
int x = 0, y = 0;
public void update() {
y++;
if (y % 10 == 0) x++;
x++;
//y++;
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Screen.Java
package com.cmnatic.mld.graphics;
import java.util.Random;
public class Screen {
private int width, height;
public int[] pixels;
public final int MAP_SIZE = 8;
public final int MAP_SIZE_MASK = MAP_SIZE - 1;
public int[] tiles = new int[MAP_SIZE * MAP_SIZE];
private Random random = new Random();
public Screen(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height]; // 50,400
for (int i = 0; i < MAP_SIZE * MAP_SIZE; i++) {
tiles[i] = random.nextInt(0xffffff);
}
}
public void clear() {
for (int i = 0; i < pixels.length; i++) {
tiles[i] = random.nextInt(0xffffff);
tiles[0] = 0;
}
}
public void render(int xOffset, int yOffset) {
for (int y = 0; y < height; y++) {
int yy = y + yOffset;
//if (yy < 0 || y >= height) break;
for (int x = 0; x < width; x++) {
int xx = x + xOffset;
//if (xx < 0 || x >= width) break;
int tileIndex = ((xx >> 4) + xOffset& MAP_SIZE_MASK) + ((yy >> 4)& MAP_SIZE_MASK) * MAP_SIZE;
pixels[x + y * width] = tiles[tileIndex];
}
}
}
}
If anyone could help, I would be forever grateful!
In Screen.clear() you have:
for (int i = 0; i < pixels.length; i++) {
tiles[i] = random.nextInt(0xffffff);
tiles[0] = 0;
}
But based on your comments, pixels is clearly larger than tiles. You probably meant tiles.length in that for loop (I'm presuming clear is supposed to be doing the same thing you are doing in that loop at the end of the Screen constructor).
In general, when you see an ArrayIndexOutOfBoundsException, it precisely means that an array index is out of bounds. When you run into that, look carefully at your code and try to find any opportunities for that to happen. In this case, the use of a different array's length in the index loop is a big red flag.
Also, incidentally, the tiles[0] = 0 in that loop looks like it isn't supposed to be there.
Your problem is that you're using pixels and tiles interchangeably in your clear method. The logical "board" size is 8x8, but your pixels array is sized based on the passed-in parameters. You then try to iterate over the 50k or so pixels in the 8x8 board and promptly run off the end.
Additionally, both of those arrays are very obviously representing two-dimensional concepts (a board and a screen), and it makes your code much clearer to use a two-dimensional array:
int pixels[][] = new int[width][height];