I've recently started learning Java and am attempting to make a basic game. To render the map I have two boolean array's configured (arr_mapx, arr_mapy), these are then checked using two for loops and two if statements to determine if both are true. If both are true the image should render, but it does not. I have successfully rendered the image outside the loop so it is not a problem with the image variable or file.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(img_player1, int_player1_x, int_player1_y, this);
if (map_drawn == false)
{
map_drawn = true;
for(int int_x = 0; int_x < 20; int_x ++)
{
if(arr_mapx[int_x] == true)
{
for(int int_y = 0; int_y < 20; int_y ++)
{
if(arr_mapy[int_y] == true)
{
g2.drawImage(img_obstacle, (int_x + 1)*32, (int_y + 1)*32, this);
//Appears to do nothing
showStatus("It Works!" + int_x + int_y +map_drawn);
//Draws fine, with correct variables
}
}
}
}
}
}
I refactored the code to reduce nesting. (See code block below. Caveat: Just using a text editor so don't expect bug free code.) The first thing that popped out at me is that map_drawn is never set to false. It draws once and is set to true and on subsequent entries into the function you exit before drawing anything in the loops because the value of map_drawn never changed to false anywhere.
Tough to say where it should change since we're just seeing a small part of your game. Is there logic in another function to set the value of map_drawn?
By the way, be careful about what you do in the paint handler (that is what you're working in here). Custom animations like this can tough to debug because of the behavior of the function. For example, if you stop in it with a debugger and then run the paint function will fire off again right away because the display needs updating. I don't know what showStatus does but it should not be a dialog. It should be logging to console or somewhere else. You definitely do not want the user to interact with UI launched from paint!
Good luck!
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(img_player1, int_player1_x, int_player1_y, this);
if (map_drawn) return;
map_drawn = true;
for(int int_x = 0; int_x < 20; int_x ++)
{
if(!arr_mapx[int_x]) continue;
for(int int_y = 0; int_y < 20; int_y ++)
{
if(!arr_mapy[int_y]) break;
g2.drawImage(img_obstacle, (int_x + 1)*32, (int_y + 1)*32, this);
//Appears to do nothing
showStatus("It Works!" + int_x + int_y +map_drawn);
//Draws fine, with correct variables
}
}
}
Related
I am trying to convert the contents of a JPanel into a BufferedImage. After looking around I have got this code.
BufferedImage image = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
this.paint(g);
I iterate through the image looking for pixels that are colored black using the following.
for(int i = 0; i < image.getWidth(); i++){
for(int j = 0; j < image.getHeight(); j++){
Color tempColor = new Color(image.getRGB(i, j));
if(tempColor == Color.BLACK){
System.out.println(tempColor); //Debugging
}
}
}
The JPanel contains many pixels that were painted using Color.BLACK (so yes they are black), although when running this code, it never prints the debugging line.
I believe the error in my code has to do with the way I am copying the contents of the JPanel into the BufferedImage, I can't seem to figure out what I am doing wrong. Any help is greatly appreciated, thanks.
You are performing a reference equality test when testing tempColor == Color.BLACK. But new Color(…) always creates a new object which can never be the same object as the predefined Color.BLACK instance, thus the == check will always be false.
Use equals or simply omit dealing with Color objects at all and just check whether image.getRGB(i, j) == 0 or if you don't want to use zero for black you can also use image.getRGB(i, j) == Color.BLACK.getRGB()
Thanks to #Holger for this answer.
for(int i = 0; i < image.getWidth(); i++){
for(int j = 0; j < image.getHeight(); j++){
Color tempColor = new Color(image.getRGB(i, j));
if(tempColor.equals(Color.BLACK)){ // Error was here
System.out.println(tempColor); //Debugging
}
}
}
Originally I had the code
if(tempColor == Color.BLACK)
instead of
if(tempColor.equals(Color.BLACK))
What I had to begin with will always evaluate to false, which was the error.
I am working on science research and am getting strange results from my code, and as a visual learner I thought it efficient to print my data to screen as it is analysed to try and see where the code is going wrong. For reference, I am analyzing a nonlinear waveform.
Here is the code for analysis:
public void getMachineCode(int trial, int wave){
double[] tempwave = new double[5000];
int index = 0;
for(int x = 0; x < 5000; x++){
tempwave[x] = waves[trial][wave][x];
}
for(int repeat = 5; repeat > 0; repeat--){
int tempstart = index;
if(tempwave[index] > 0){
while(tempwave[index] > 0){
index++;
}
}else{
while(tempwave[index] < 0){
index++;
}
}
int midwave = index - tempstart;
if(tempwave[midwave] > 0){
System.out.println(0);
}else{
System.out.println(1);
}
}
}
Here, all I want is to print the (x,y) coordinates of my trial to the screen as the index increases so it is a constantly changing graph, something like:
if(tempwave[index] > 0){
while(tempwave[index] > 0){
index++;
printpixel(index,y); //something to show where the code is scanning
}
}else{
while(tempwave[index] < 0){
index++;
printpixel(index,y);
}
I am not very familiar with java graphics and was looking into using a JFrame but it is hard for me to implement. There will be about 4000 data points to plot, so I might also have to only print every few points or is there a way to make a better visual? Any ideas? Thanks!
This should give you basic idea.
Normally with Swing graphics, you override the paintComponent() method and draw from some shared state
Instead you can just draw to a large image, and then draw that image using paintComponent()
I've compressed this into a single self contained example, this can be separated out in a larger application
Example
public static void main(String[] args) throws Exception {
final BufferedImage image = new BufferedImage(1280, 768,
BufferedImage.TYPE_INT_RGB);
JPanel canvas = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
};
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout()); // <== make panel fill frame
frame.add(canvas, BorderLayout.CENTER);
frame.setSize(500, 500);
frame.setVisible(true);
// do you drawing somewhere else, maybe a different thread
Graphics g = image.getGraphics();
g.setColor(Color.red);
for (int x = 0; x < 100; x += 5) {
for (int y = 0; y < 100; y += 5) {
g.drawRect(x, y, 1, 1);
}
}
g.dispose();
canvas.repaint();
}
I am making a game. It is a 2D game, and the map is drawn by a double loop like this:
for(int i = 0; i < mapArray.length; i++){
for(int j = 0; j < mapArray[1].length; j++){
//if statement for if its on the screen
g.drawImage(tiles.get(mapArray[i][j]).getImage(), j * textureSize, i * textureSize, null);
}
}
They are 32x32 images, and I was wondering stitching them all together to create one large image at the beginning would be more efficient in drawing. The resulting image would only be around 1500x1500.
I was thinking of making it stitched into one image (Especially since I am planning on making the images smaller which would make the loop need ever more time.) so that it didn't have to run through the double for loop every time it renders (shooting for 60 FPS). But I don't know how to do this, and would it actually improve the performance if I did?
Also, I could just stitch them into rows, and only render the rows that are on the screen(to remove the large image problem) So it would still be much less intensive than that crazy loop I've got right now.
Edit: And one last thing, if you can provide an example of how to do this without extra libraries that would be optimal.
I currently have this code for stitching:
Edit: now works. Leaving this here for future readers:
public void stitchImages(){
BufferedImage temp = new BufferedImage( <Width> , <height> , BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) temp.getGraphics();
for (int b = 0; b < mapArray.length; b++) {
for (int a = 0; a < mapArray[b].length; a++) {
g.drawImage(tiles.get(mapArray[b][a]).getImage(),
(int) mapX + a * textureSize, (int) mapY + b
* textureSize, null);
}
}
mapImage = temp;
}
Create a new Image to encapsulate all of your images. Draw your images when you load up, then just draw that in paintComponent()
BufferedImage im = new BufferedImage(1500,1500,BufferedImage.TYPE_INT_RGB);
private void init() {
Graphics g = im.getGraphics();
for(int i = 0; i < mapArray.length; i++){
for(int j = 0; j < mapArray[1].length; j++){
//if statement for if its on the screen
g.drawImage(tiles.get(mapArray[i][j]).getImage(), j * textureSize, i * textureSize, null);
}
}
}
public void paintCompoent(Graphics g) {
super.paintComponent(g);
g.drawImage(im,0,0,null);
}
EDIT:
As for your idea about just painting the lines that are on the screen, you can do that by creating an Image the size of the window and just drawing to that. But in general, it's not a big problem to paint a big Image (as long as the Image fits in memory and you don't get an OutOfMemoryException) as your GPU's capabilities smoke your CPU's
So I am making a snake game, but if I move from one direction to the other really fast then it says I made a collision with the body and ends the game (for example, if I am going left and I hit down and then left again really fast or down and right really fast, etc.)
I've tried a few things already. I changed the way that it checked for a collision by making it a .intersects rather than checking if (x == body[i][0] && y = body[i][1]). I also did a few System.out.println()'s to see if maybe something was going wrong there. I noticed that sometimes one of the values repeats (either x or y depending on the direction), but I can't figure out why... I figure that the repeating is why it messes up the collision, but I can't find the spot where it would be making it repeat.
x and y are being changed by a thread.
Here is the "Drawing" portion of my code. If you need any other snippets of code please let me know.
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (numOfFood == 1) {//Checks wether there is food on the GUI
g.setColor(Color.BLUE);
g.fillRect(foodX,foodY,12,12);
}
else {
foodX = random.nextInt(103)*12; //Both this and the below line get a random x or y value to put on GUI for food placement
foodY = random.nextInt(57)*12;
numOfFood = 1;
}
Rectangle headRect = new Rectangle( x, y, 12, 12 ); //Actual rectangle of the head
Rectangle foodRect = new Rectangle(foodX, foodY, 12, 12); //Food rectangle
g.setColor(Color.RED);
g.fillRect(x,y,12,12); //Draws head of Snake
g.setColor(Color.WHITE);
g.fillRect(x+2,y+2,8,8); //Draws a white square in the head of the snake
for (int i = 0; i < n; ++i) { //Collision Checker
Rectangle bodyRect = new Rectangle(body[i][0],body[i][1],12,12);
if ( headRect.intersects(bodyRect)) {
for (int j = 0; j < n; ++j) {
body[j][0] = -1;
body[j][1] = -1;
}
numOfFood = 1;
n = 0;
x = 624;
y = 348;
endGame = true;
}
}
g.setColor(Color.RED);
if (n > 0) { //Puts the snakes body behind the head
for (int i = 0;i < n; ++i) {
g.fillRect(body[i][0],body[i][1],12,12);
}
}
for (int i = n-1;i >= 0; --i) { //Inserts the head at the first part of the array so that the body moves
if (body[i][0] != -1 && body[i][1] != -1) {
body[i+1][0] = body[i][0];
body[i+1][1] = body[i][1];
}
if (i == 0) {
body[i][0] = x;
body[i][1] = y;
}
}
if (headRect.intersects(foodRect)) { //If the food rectangle and the head rectangle intersect then the snake got the food.
numOfFood = 0;
n++;
}
}
When do you call paintComponent? I suspect that you have one method that continuously moves the snake forward in regular intervals, but paintComponent is responsible for making the snake longer.
You should move collision and moving the snake into the same method that is responsible for moving the head in the direction the snake is moving.
Otherwise, paintComponent might be called many times on one move update, and this is responsible for duplicates of x and y in your array.
I'm trying to create a program which will visualize different sorting algorithms by drawing a set of bars representing an array along for each time the sort loops. However, when I set the array from within the sorter class which in turn repaints the panel, it seems that it only calls paintComponent() for the first and last iteration, not showing the steps in between.
Here is the sort code which calls the setNumberArray() method:
public void bubbleSort() {
int[] x = getNumberArray();
boolean doMore = true;
while (doMore) {
doMore = false;
for (int count = 0; count < x.length - 1; count++) {
if (x[count] > x[count+1]) {
int temp = x[count]; x[count] = x[count+1]; x[count+1] = temp;
doMore = true;
}
}
// Update the array
SorterGUI.getSorterPanel().setNumberArray(x);
// Pause
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Sorter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Which calls:
public void setNumberArray(int[] numberArray) {
this.numberArray = numberArray;
repaint();
}
Finally drawing the bars:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int length = numberArray.length;
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.white);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.gray);
for(int count = 0; count < length; count++) {
g2d.fill3DRect((getWidth() / length) * (count + 1), 0,
getWidth() / length, getHeight() - (numberArray[count] * 3),
true);
playSound(numberArray[count]);
}
System.out.print(".");
}
I know it's not repainting in between (with or without the delay) because it only prints one "." when I start sorting.
Forget the paintImmediately as that won't solve your problem. The issue is that you're calling Thread.sleep on the EDT, the main Swing thread known as the event dispatch thread, and this will put your Swing app to sleep (as you're finding out). Instead use a Swing Timer for your delay and all will work well. Either that or do your Thread.sleep in a background thread.
You can use JComponent.paintImmediately to force immediate painting