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.
Related
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;
}
}
We were giving a this program in class and asked to try it our at home but it doesn't work, I'm guessing there is something wrong with the algorithm. Can someone help me out?
import java.awt.Graphics;
import javax.swing.JApplet;
public class rotationalTransformation extends JApplet {
int[] x=new int[3];
int[] y=new int[3];
int[][] tMatrix=new int[3][3];
int no_pts=3;
public void start()
{
x[0]= 10;
x[1]= 20;
x[2]= 30 ;
y[0]= 10;
y[1]= 30;
y[2]= 10 ;
}
public void paint(Graphics g)
{
try {
System.out.println("Before Rotation");
g.drawPolygon(x, y, no_pts);
matrixIdentity(tMatrix);
System.out.println("Identity Matrix Created");
rotation(60,10,10);
System.out.println("Rotating");
for(int a=0; a<3;a++)
{
for(int c=0; c<3;c++)
{
System.out.print(tMatrix[a][c] + " ");
}
System.out.println();
}
for(int a=0; a<3;a++)
{
System.out.println(x[a] + " " + y[a]);
}
transformPoints();
System.out.println("After Rotation");
g.drawPolygon(x, y, no_pts);
}
catch(Exception e){}
}
void matrixIdentity(int[][] m)
{int i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
{ if(i==j)
{
m[i][j]=1;
}
else
m[i][j]=0;
}
}
void transformPoints()
{
int tmp;
for(int a=0; a<3;a++)
{
tmp=tMatrix[0][0]*x[a]+ tMatrix[0][1]*y[a]+tMatrix[0][2];
y[a]=tMatrix[1][0]*x[a]+tMatrix[1][1]*y[a]+tMatrix[1][2];
x[a]=tmp;
}
}
void rotation(double degree,int rx,int ry)
{ int a;
a = (int) (degree * 3.14/180);
tMatrix[0][0]= (int) Math.cos(a) ;
tMatrix[0][1]= (int) -Math.sin(a) ;
tMatrix[0][2]= (int) (rx*(1-Math.cos(a))+ ry*Math.sin(a));
tMatrix[1][0]= (int) Math.sin(a) ;
tMatrix[1][1]= (int) Math.cos(a);
tMatrix[1][2]= (int) (ry*(1-Math.cos(a))-rx*Math.sin(a));
}
}
It prints the original shape but does not print the rotated shape.
I think you should put one more line to be able to see the rotation.
transformPoints();
System.out.println("After Rotation");
g.drawPolygon(x, y, no_pts);
//this line will repaint with new position
this.repaint();
}
Hope this helps.
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];
I am making a tile based platformer game in java. I render a map which is stored in a 2 dimensional array but when this array is very big my game starts to become slow. I realised that I had to only render the part of the map that is viewable, I tried to do that but i wrote very hacky code that only worked partly so I removed it. How can I do this properly? Here is my code (without the hacky stuff). Also how could I use System.nanoTime() rather than System.currentTimeMillis()?
package sexy_robot_from_another_dimension;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Game extends JPanel
{
int playerX = 50;
int playerY = 50;
static boolean up = false;
static boolean down = false;
static boolean right = false;
static boolean left = false;
int playerSpeed = 1;
String[][] map;
int blockSize = 20;
int jumpLoop = 0;
int maxJumpLoop = 280;
static BufferedImage block, player;
int playerWidth = 20;
int playerHeight = 35;
int cameraX = 0;
int cameraY = 0;
long nextSecond = System.currentTimeMillis() + 1000;
int frameInLastSecond = 0;
int framesInCurrentSecond = 0;
public Game()
{
super();
try
{
map = load("/maps/map1.txt");
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
if(up)
{
if((!playerIsOnBlock(playerX, playerY).equals("0")) || (!playerIsOnBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
timeToJump();
}
}
if(down)
{
}
if(right)
{
if((playerIsLeftBlock(playerX, playerY).equals("0")) && (playerIsLeftBlock(playerX, playerY + (playerHeight/2 - 1)).equals("0")) && (playerIsLeftBlock(playerX, playerY + (playerHeight - 1)).equals("0")))
{
playerX += playerSpeed;
}
}
if(left)
{
if((playerIsRightBlock(playerX, playerY).equals("0")) && (playerIsRightBlock(playerX, playerY + (playerHeight/2 - 1)).equals("0")) && (playerIsRightBlock(playerX, playerY + (playerHeight - 1)).equals("0")))
{
playerX -= playerSpeed;
}
}
repaint();
}
};
timer.scheduleAtFixedRate(task, 0, 10);
Timer timerGrav = new Timer();
TimerTask taskGrav = new TimerTask()
{
#Override
public void run()
{
if((playerIsOnBlock(playerX, playerY).equals("0")) && (playerIsOnBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
playerY += playerSpeed;
repaint();
}
}
};
timerGrav.scheduleAtFixedRate(taskGrav, 0, 6);
}
void timeToJump()
{
if(jumpLoop == 0)
{
jumpLoop = 1;
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
if((playerIsBelowBlock(playerX, playerY).equals("0")) && (playerIsBelowBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
playerY -= playerSpeed;
jumpLoop++;
repaint();
}
else
{
jumpLoop = maxJumpLoop;
}
if(jumpLoop == maxJumpLoop)
{
jumpLoop = 0;
cancel();
}
}
};
timer.scheduleAtFixedRate(task, 0, 3);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
long currentTime = System.currentTimeMillis();
if (currentTime > nextSecond)
{
nextSecond += 1000;
frameInLastSecond = framesInCurrentSecond;
framesInCurrentSecond = 0;
}
framesInCurrentSecond++;
g.drawString(frameInLastSecond + " fps", 10, 20);
cameraX = -playerX + getWidth()/2;
cameraY = -playerY + getHeight()/2;
g.translate(cameraX, cameraY);
for (int x = 0; x < map.length; x++)
{
for (int y = 0; y < map[0].length; y++)
{
switch(map[x][y])
{
case "0":
break;
case "1":
if(block != null)
{
TexturePaint tp0 = new TexturePaint(block, new Rectangle(0, 0, blockSize, blockSize));
g2.setPaint(tp0);
}
g.fillRect(y*blockSize, x*blockSize, 20, 20);
break;
}
}
}
g.setColor(Color.BLACK);
if(player != null)
{
TexturePaint tp0 = new TexturePaint(player, new Rectangle(playerX, playerY, playerWidth, playerHeight));
g2.setPaint(tp0);
}
g.fillRect(playerX, playerY, playerWidth, playerHeight);
g.setColor(Color.black);
g.setFont(new Font("Droid Sans Mono", Font.PLAIN, 12));
g.drawString("Sexy!", playerX - 5, playerY - 10);
}
boolean outOfMap(int x, int y)
{
y -= blockSize - 1;
x -= blockSize - 1;
if((y/blockSize <= map.length - 2) && (y/blockSize >= 0) && (x/blockSize <= map[0].length-2) && (x/blockSize >= 0))
{
return false;
}
return true;
}
String playerIsOnBlock(int x, int y)
{
y += playerHeight;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsBelowBlock(int x, int y)
{
y -= playerSpeed;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsLeftBlock(int x, int y)
{
x += playerWidth;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsRightBlock(int x, int y)
{
x -= playerSpeed;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String[][] load(String file) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
int lines = 1;
int length = br.readLine().split(" ").length;
while (br.readLine() != null) lines++;
br.close();
br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
String[][] map = new String[lines][length];
for (int i = 0; i < lines; i++)
{
String line = br.readLine();
String[] parts = line.split(" ");
for (int y = 0; y < length; y++)
{
map[i][y] = parts[y];
}
}
br.close();
return map;
}
}
Thank you!
It seems your camera is centered on the player, then there are two ways of doing this, I like the first way, it is a bit cleaner:
1th: Create a rectangle that bounds your cameras view, and check if the map x,y is within this view, render only if true.
Rectangle cameraView = new Rectangle(playerX - getWidth() / 2, playerY - getHeight() / 2, getWidth(), getHeight());
for (int x = 0; x < map.length; x++) {
for (int y = 0; y < map[0].length; y++) {
if (!cameraView.contains(x*blockSize, y*blockSize))
continue;
switch (map[x][y]) {
case "0":
break;
case "1":
if (block != null) {
TexturePaint tp0 = new TexturePaint(block, new Rectangle(0, 0, blockSize, blockSize));
g2.setPaint(tp0);
}
g.fillRect(y * blockSize, x * blockSize, 20, 20);
break;
}
}
}
The second option is to simply calculate the distance to the center of the screen (playerX,playerY) from each map[x][y] and skip all map[x][y] that falls outside your viewing bounds, this is a bit uglier to code and I really don't recommend this, the rectangle option above should be fast enough.
Edit:
#JasonC That is true, I didn't consider for instance when an x value is definitely outside the view, it will still go into the y loop through all the y values. One can simply create a dummy variable in the x-loop and do the following check
for (int x = 0; x < map.length; x++) {
int dummyY = playerY
if(!cameraView.contains(x,dummyY))
continue;
....
//rest of code ommitted
Another optimization you can do is considering not setting a TexturePaint (expensive operation) but instead simply drawing the image of the block:
g.fillRect(y * blockSize, x * blockSize, 20, 20);
Replaced with
g.drawImage(block, y*blockSize, x*blockSize, null);
The same with the playerimage.
Set the clipping region to the visible area with Graphics.setClip(), that will prevent most rendering operations from taking effect outside that region.
For drawing operations where this isn't sufficient (perhaps you also want to avoid doing calculations or something for objects outside the clipping area), test your objects bounds against the clipping rectangle and skip the object if it doesn't intersect.
See Graphics.setClip().
A further optimization can be done by, for example, calculating the range of blocks on your map that is definitely outside of the visible area, and excluding that from your for loop (no sense testing blocks against the clipping region if you know they are outside already). Take the clipping region, transform it to map index coordinates, then you will know where in your map the visible area is and you can just iterate over that subsection of the map.