I'm trying to display a background image using g.drawImage within Canvas Jframe but i have nothing displayed and the screen still white , and when i try to fill the screen with any color it works using g.fillRect
here is the Main class :
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.ImageIcon;
public class Main extends Canvas implements Runnable, KeyListener {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 600, HEIGHT = WIDTH / 12 * 9;
// rate to convert meters to pixels in the physics engine
public static final float RATE = 30;
// counts how many loops are made in the main thread
private int counter = 49;
// image and graphics used for double buffering
private ImageIcon bgImage = new ImageIcon("res/bgImage.png");
private Image dbImage;
private Graphics dbg;
/* boolean to define when the game will be started and stopped */
private boolean running = false;
// variables for the Box2D world
Vec2 gravity = new Vec2(0.0f, 10.0f);
boolean doSleep = true;
World world = new World(gravity, doSleep);
// new array list to hold Ball references
ArrayList<Ball> balls = new ArrayList<Ball>();
// create a new player
Player character = new Player(world);
public Main() {
addKeyListener(this);
// add a ground floor to our Box2D world
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(300.0f / RATE, 400.0f / RATE);
Body groundBody = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
groundBox.setAsBox(300.0f / RATE, 0);
groundBody.createFixture(groundBox, 0.0f);
// wall fixture
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = groundBox;
fixtureDef.density = 2.0f;
fixtureDef.filter.groupIndex = -1;
// left wall
groundBodyDef.position.set(0.0f / RATE, 350.0f / RATE);
groundBody = world.createBody(groundBodyDef);
groundBox.setAsBox(0, 50.0f / RATE);
groundBody.createFixture(fixtureDef);
// right wall
groundBodyDef.position.set(600.0f / RATE, 350.0f / RATE);
groundBody = world.createBody(groundBodyDef);
groundBox.setAsBox(0, 50.0f / RATE);
groundBody.createFixture(fixtureDef);
/**
* #WIDHT : width of jpanel screen
* #HEIGHT : height of the jpanel screen
* #param : title of jpanel screen
* #this : refered to our main game instance
*/
new Window(WIDTH, HEIGHT, "Apocalypse 2D v0.0", this);
}
#Override
public int getWidth() { // Width of the CustomPanel
return WIDTH;
}
#Override
public int getHeight() { // Height of the CustomPanel
return HEIGHT;
}
#Override
public Dimension getPreferredSize() { // Dimension of the CustomPanel
return new Dimension(getWidth(), getHeight());
}
public void start() {
// starts a new thread
running = true;
Thread th = new Thread(this);
th.start();
}
public void stop() {
running = false;
}
public void destroy() {
}
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (running) {
counter++;
// Simulate the world
float timeStep = 1.0f / 60.0f;
int velocityIterations = 6;
int positionIterations = 2;
world.step(timeStep, velocityIterations, positionIterations);
// add new balls to the world every 50th loop
if (counter % 80 == 0)
balls.add(new Ball(world));
repaint();
// pause for 10 milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
// do nothing
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
}
public void paint(Graphics g) {
/*g.setColor(Color.black);*/
/*g.fillRect(0, 0, getWidth(), getHeight());*/
g.drawImage(bgImage, getWidth(), getHeight(), null);
// loop through each ball and call it's draw method
Iterator<Ball> itr = balls.iterator();
while (itr.hasNext()) {
Ball b = itr.next();
b.DrawBall(g);
// if the ball should be removed then remove it
if (b.shouldDelete())
itr.remove();
}
// draw the main character.
character.draw(g);
}
// sets up double buffering for graphics
public void update(Graphics g) {
if (dbImage == null) {
dbImage = createImage(this.getSize().width, this.getSize().height);
dbg = dbImage.getGraphics();
}
dbg.setColor(getBackground());
dbg.fillRect(0, 0, this.getSize().width, this.getSize().height);
dbg.setColor(getForeground());
paint(dbg);
g.drawImage(dbImage, 0, 0, this);
character.animplayer();
for (Ball ball : balls) {
ball.animBalls();
}
}
public void keyPressed(KeyEvent key) {
character.keyPress(key);
}
public void keyReleased(KeyEvent key) {
character.keyRelease(key);
}
public void keyTyped(KeyEvent key) {
}
public static void main(String[] args) {
new Main();
}
}
and this is the paint method where i'm trying to fill the image to the screen :
public void paint(Graphics g) {
/*g.setColor(Color.black);*/
/*g.fillRect(0, 0, getWidth(), getHeight());*/
g.drawImage(bgImage, getWidth(), getHeight(), null);
// loop through each ball and call it's draw method
Iterator<Ball> itr = balls.iterator();
while (itr.hasNext()) {
Ball b = itr.next();
b.DrawBall(g);
// if the ball should be removed then remove it
if (b.shouldDelete())
itr.remove();
}
// draw the main character.
character.draw(g);
}
The drawImage() method has several overloads, the one you are using is drawImage(BufferedImage img, int x, int y, ImageObserver observer), x and y being the top left coordinates on the canvas. Right now those are the width and the heigh of the canvas, so the image is being drawn outside of the panel. Try calling g.drawImage(dbImage, 0, 0, null).
edit:
Asuming your image is not the same size as your canvas, use g.drawImage(dbImage, 0, 0, getWidth(), getHeight(), null) to fill the canvas with your image.
Related
I am trying to make an animation where two rectangles will appear and disappear from frame when you type in: c with Turtle Graphics. But the problem I have is that I do not understand how to incorporate turtle graphics into a loop. For this, I have to use do-while loop. I am also suppose to have the rectangles that I made move horizontally across the screen. I have the general idea set out, but I do not know how to use turtle graphics with a loop. My code is not the most orderly when I tried to set it up here.
/**
* Write a description of class Animation here.
*
* #author (author)
* #version
*/
import java.util.Scanner;
import java.awt.*;
class Animation
{
//set conditions for turtle to start drawing
public void prepareTurtleToDraw(Turtle myrtle, Color color, int x, int y)
{
myrtle.hide();
myrtle.penUp(); //pick up the pen to avoid leaving a trail when moving the turtle
myrtle.setColor(color); //set myrtle's color
myrtle.moveTo(x, y); //move to coordinates
myrtle.penDown(); //put the pen down to start drawing
}//end of prepareTurtleToDraw method
//draw a line
public void drawLine(Turtle myrtle, int x1, int y1, int x2, int y2)//, int width)
{
myrtle.moveTo(x1, y1); //moves to this coordinate first
myrtle.moveTo(x2, y2); //then moves to this coordinate
//myrtle.setPenWidth(width); //this adjusts the size of the lines
}//end of drawLine method
public static void pressC()
{
String userInput = ""; //declare and initialize a String variable
char key = ' '; //declare and initialize a char variable
Scanner in = new Scanner(System.in); //construct a Scanner object
System.out.println("Please press the c key to watch the animation.");
//do-while loop to wait for the user to enter the letter c
do
{
userInput = in.next(); //accept one token from the keyboard
in.nextLine(); //flush the buffer
key = userInput.charAt(0); //picks off the first character from the userInput String variable
}
while(key != 'c'); //do-while condition statement
System.out.println("Thank you. You may continue");
}//end of main method
}
public class AnimationTester
{
public static void main(String[] args)
{
//Picture pictureObj = new Picture(""); //create a Picture object for the maze background image, has name and etc.
World worldObj = new World(); //create a World object to draw in
//worldObj.setPicture(pictureObj); //set the maze background image in the World object
Turtle lertle = new Turtle(300, 150, worldObj); //create a Turtle object to do the drawing
Animation turt = new Animation();
Turtle dyrtle = new Turtle(150, 150, worldObj);
turt.prepareTurtleToDraw(lertle, Color.BLACK, 250, 150);
turt.drawLine(lertle, 250, 150, 400, 150);
turt.drawLine(lertle, 400, 150, 400, 250);
turt.drawLine(lertle, 400, 250, 250, 250);
turt.drawLine(lertle, 250, 250, 250, 150);
turt.prepareTurtleToDraw(dyrtle, Color.RED, 150, 150);
turt.drawLine(dyrtle, 150, 150, 260, 75);
turt.drawLine(dyrtle, 260, 75, 335, 150);
turt.drawLine(dyrtle, 335, 150, 225, 225);
turt.drawLine(dyrtle, 225, 225, 150, 150);
System.out.println(worldObj);
}
}
Well, it seems I can't upload photos I took of my program because I don't have enough reputation. Thanks!
Figure 1. Before pressing C
Figure 2. After pressing C
I wrote this little program for you, it is running on Java Swing to create animations. I have three rectangles, two - red and blue - fading in and out according to how much seconds have elapsed, and the third appearing and disappearing upon pressing C.
I think what may be helpful for you when dealing with animations is a "game loop." You can find my implementation of it in MainFrame.java below. Loosely speaking, a game loop controls the update speed of animations so that the program runs consistently on both slow and fast computers. If a game loop is not implemented, a fast computer may finish an animation faster than a relatively slower computer.
This is a complete program, just compile the three .java files and run Main to bring up the game interface.
Main.java
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(() -> {
MainFrame mf = MainFrame.getMainFrame();
new Thread(mf).start();
});
} catch (InvocationTargetException | InterruptedException e) {
System.out.println("Could not create GUI");
}
}
}
MainFrame.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
class MainFrame extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
private DisplayPanel dp;
private boolean isRunning;
private double secondsPerFrame = 1.0 / 60.0;
private static MainFrame mf;
private MainFrame() {
super("Title");
dp = new DisplayPanel();
add(dp, BorderLayout.CENTER); // Add display to the center
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
/**
* Static factory
*
* #return A singleton MainFrame
*/
static MainFrame getMainFrame() {
return mf == null ? mf = new MainFrame() : mf;
}
/**
* Game loop
*/
#Override
public void run() {
isRunning = true;
int frames = 0;
double frameCounter = 0;
double lastTime = System.nanoTime() / 1000000000.0;
double unprocessedTime = 0;
while(isRunning) {
boolean render = false;
double startTime = System.nanoTime() / 1000000000.0;
double passedTime = startTime - lastTime;
lastTime = startTime;
unprocessedTime += passedTime;
frameCounter += passedTime;
while(unprocessedTime > secondsPerFrame) {
render = true;
unprocessedTime -= secondsPerFrame;
// Update the state of the rectangles' brightness
dp.update(secondsPerFrame);
if(frameCounter >= 1.0) {
// Show fps count. Updates every second
dp.setFps(frames);
frames = 0;
frameCounter = 0;
}
}
if(render) {
// Render the rectangles
dp.render();
frames++;
} else {
try {
Thread.sleep(1);
} catch (InterruptedException ie) {}
}
}
}
}
DisplayPanel.java
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
// Create a display within the window
class DisplayPanel extends Canvas implements KeyListener {
private static final long serialVersionUID = 2L;
private Graphics2D g2; // Drawing tool
private BufferStrategy strategy; // Drawing tool
private FadingRectangle[] fadingRectangles; // Appears/disappears based on elapsed time
private FadingRectangle userControlledRectangle; // Appears/disappears upon command by user
private int fps; // Used to display the fps on screen
DisplayPanel() {
setPreferredSize(new Dimension(800, 600));
addKeyListener(this);
setFocusable(true);
fadingRectangles = new FadingRectangle[2];
fadingRectangles[0] = new FadingRectangle(150, 250, 100, 100);
fadingRectangles[1] = new FadingRectangle(550, 250, 100, 100);
userControlledRectangle = new FadingRectangle(350, 100, 100, 100);
}
/**
* Updates the brightness of rectangles
*
* #param elapsedSeconds Seconds elapsed since the last call to this method
*/
void update(double elapsedSeconds) {
fadingRectangles[0].update(elapsedSeconds);
fadingRectangles[1].update(elapsedSeconds);
}
/**
* Draw everything
*/
void render() {
// Prepare drawing tools
if (strategy == null || strategy.contentsLost()) {
createBufferStrategy(2);
strategy = getBufferStrategy();
Graphics g = strategy.getDrawGraphics();
this.g2 = (Graphics2D) g;
}
// Anti-aliasing
this.g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Clear screen by drawing background on top
this.g2.setColor(Color.BLACK);
this.g2.fillRect(0, 0, getWidth(), getHeight());
// Draw the rectangles
fadingRectangles[0].draw(new Color(255, 0, 0));
fadingRectangles[1].draw(new Color(0, 0, 255));
userControlledRectangle.draw(Color.WHITE);
// Draw fps count on the upper left corner
g2.setColor(Color.WHITE);
g2.drawString("FPS: " + Integer.toString(fps), 10, 20);
// Set the drawn lines visible
if(!strategy.contentsLost())
strategy.show();
}
/**
* #param fps The fps to be drawn when render() is called
*/
void setFps(int fps) {
this.fps = fps;
}
/**
* Used to draw rectangles in the display
*/
private class FadingRectangle {
private int x, y, width, height; // Location and size of the rectangle
private double secondsPassed; // Arbitrary number that determines the brightness of blue
private FadingRectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
/**
* Called by render()
*
* #param color The color of the rectangle to be drawn
*/
private void draw(Color color) {
// Determine color
double fade = Math.abs(Math.sin(secondsPassed));
int red = (int) (color.getRed() * fade);
int green = (int) (color.getGreen() * fade);
int blue = (int) (color.getBlue() * fade);
g2.setColor(new Color(red, green, blue));
// Draw the rectangle
g2.drawRect(x, y, width, height);
}
private void update(double elapsedSeconds) {
secondsPassed += elapsedSeconds;
}
}
// A quick and dirty implementation. Should be fixed to make it clearer
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_C) {
userControlledRectangle.update(Math.PI / 2);
}
}
#Override
public void keyPressed(KeyEvent e) {}
#Override
public void keyTyped(KeyEvent e) {}
}
Trying the exercise in lesson2 of the Udacity course. Despite importing the classes (I'm at java.awt.* now, but I also tried java.awt.Color and java.awt.Canvas separately (also need Shape))..
package com.jul.udacity.lesson2;
public class TestRectangle {
public static void main(String[] args) {
// TODO Auto-generated method stub
Rectangle rect1 = new Rectangle(100.0, 100.0, 200.0, 100.0);
rect1.draw();
}
}
And the class is copied from there and java.awt import added. Any help will be great. Thanks!
package com.jul.udacity.lesson2;
//HIDE
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
//import java.awt.Color;
//import java.awt.Shape;
//import java.awt.Canvas;
import java.awt.*;
public class Rectangle implements Shape
{
private Color color = Color.BLACK;
private boolean filled = false;
private double x;
private double y;
private double width;
private double height;
/**
Constructs an empty rectangle.
*/
public Rectangle()
{
x = 0;
y = 0;
width = 0;
height = 0;
}
/**
Constructs a rectangle.
#param x the leftmost x-coordinate
#param y the topmost y-coordinate
#param width the width
#param height the height
*/
public Rectangle(double x, double y, double width, double height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
/**
Gets the leftmost x-position of this rectangle.
#return the leftmost x-position
*/
public int getX()
{
return (int) Math.round(x);
}
/**
Gets the topmost y-position of this rectangle.
#return the topmost y-position
*/
public int getY()
{
return (int) Math.round(y);
}
/**
Gets the width of this rectangle.
#return the width
*/
public int getWidth()
{
return (int) Math.round(width);
}
/**
Gets the height of this rectangle.
#return the height
*/
public int getHeight()
{
return (int) Math.round(height);
}
/**
Moves this rectangle by a given amount.
#param dx the amount by which to move in x-direction
#param dy the amount by which to move in y-direction
*/
public void translate(double dx, double dy)
{
x += dx;
y += dy;
Canvas.getInstance().repaint();
}
/**
Resizes this rectangle both horizontally and vertically.
#param dw the amount by which to resize the width on each side
#param dw the amount by which to resize the height on each side
*/
public void grow(double dw, double dh)
{
width += 2 * dw;
height += 2 * dh;
x -= dw;
y -= dh;
Canvas.getInstance().repaint();
}
/**
Sets the color of this rectangle.
#param newColor the new color
*/
public void setColor(Color newColor)
{
color = newColor;
Canvas.getInstance().repaint();
}
/**
Draws this rectangle.
*/
public void draw()
{
filled = false;
Canvas.getInstance().show(this);
}
/**
Fills this rectangle.
*/
public void fill()
{
filled = true;
Canvas.getInstance().show(this);
}
public String toString()
{
return "Rectangle[x=" + getX() + ",y=" + getY() + ",width=" + getWidth() + ",height=" + getHeight() + "]";
}
public void paintShape(Graphics2D g2)
{
Rectangle2D.Double rect = new Rectangle2D.Double(getX(), getY(),
getWidth(), getHeight());
g2.setColor(new java.awt.Color((int) color.getRed(), (int) color.getGreen(), (int) color.getBlue()));
if (filled)
{
g2.fill(rect);
}
else
{
g2.draw(rect);
}
}
}
You might want to check the lesson directions carefully. java.awt.Canvas has no getInstance() method. You just use new to make a Canvas. So you either didn't read carefully and are using the wrong Canvas, or there's something else going on.
Also the show() methods are deprecated, so I'm leaning towards you are supposed to be using a different Canvas class.
Also, Swing is not thread safe. Read up on how to use Swing objects
markspace is correct: Canvas here is not the class from java.awt - the Udacity instructors are using their own class called Canvas. I suggest grabbing the lesson files and using those. Here's the Canvas class from that Intro To Java Course:
import java.awt.image.BufferedImage;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.RescaleOp;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Canvas
{
private static Canvas canvas = new Canvas();
private ArrayList<Shape> shapes = new ArrayList<Shape>();
private BufferedImage background;
private JFrame frame;
private CanvasComponent component;
private static final int MIN_SIZE = 100;
private static final int MARGIN = 10;
private static final int LOCATION_OFFSET = 120;
class CanvasComponent extends JComponent
{
public void paintComponent(Graphics g)
{
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(java.awt.Color.BLACK);
if (background != null)
{
g.drawImage(background, 0, 0, null);
}
for (Shape s : new ArrayList<Shape>(shapes))
{
Graphics2D g2 = (Graphics2D) g.create();
s.paintShape(g2);
g2.dispose();
}
}
public Dimension getPreferredSize()
{
int maxx = MIN_SIZE;
int maxy = MIN_SIZE;
if (background != null)
{
maxx = Math.max(maxx, background.getWidth());
maxy = Math.max(maxx, background.getHeight());
}
for (Shape s : shapes)
{
maxx = (int) Math.max(maxx, s.getX() + s.getWidth());
maxy = (int) Math.max(maxy, s.getY() + s.getHeight());
}
return new Dimension(maxx + MARGIN, maxy + MARGIN);
}
}
private Canvas()
{
component = new CanvasComponent();
if (System.getProperty("com.horstmann.codecheck") == null)
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(component);
frame.pack();
frame.setLocation(LOCATION_OFFSET, LOCATION_OFFSET);
frame.setVisible(true);
}
else
{
final String SAVEFILE ="canvas.png";
final Thread currentThread = Thread.currentThread();
Thread watcherThread = new Thread()
{
public void run()
{
try
{
final int DELAY = 10;
while (currentThread.getState() != Thread.State.TERMINATED)
{
Thread.sleep(DELAY);
}
saveToDisk(SAVEFILE);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
};
watcherThread.start();
}
}
public static Canvas getInstance()
{
return canvas;
}
public void show(Shape s)
{
if (!shapes.contains(s))
{
shapes.add(s);
}
repaint();
}
public void repaint()
{
if (frame == null) return;
Dimension dim = component.getPreferredSize();
if (dim.getWidth() > component.getWidth()
|| dim.getHeight() > component.getHeight())
{
frame.pack();
}
else
{
frame.repaint();
}
}
/**
* Pauses so that the user can see the picture before it is transformed.
*/
public void pause()
{
if (frame == null) return;
JOptionPane.showMessageDialog(frame, "Click Ok to continue");
}
/**
* Takes a snapshot of the screen, fades it, and sets it as the background.
*/
public static void snapshot()
{
Dimension dim = getInstance().component.getPreferredSize();
java.awt.Rectangle rect = new java.awt.Rectangle(0, 0, dim.width, dim.height);
BufferedImage image = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, rect.width, rect.height);
g.setColor(java.awt.Color.BLACK);
getInstance().component.paintComponent(g);
float factor = 0.8f;
float base = 255f * (1f - factor);
RescaleOp op = new RescaleOp(factor, base, null);
BufferedImage filteredImage
= new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
op.filter(image, filteredImage);
getInstance().background = filteredImage;
getInstance().component.repaint();
}
public void saveToDisk(String fileName)
{
Dimension dim = component.getPreferredSize();
java.awt.Rectangle rect = new java.awt.Rectangle(0, 0, dim.width, dim.height);
BufferedImage image = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(java.awt.Color.WHITE);
g.fill(rect);
g.setColor(java.awt.Color.BLACK);
component.paintComponent(g);
String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
try
{
ImageIO.write(image, extension, new File(fileName));
}
catch(IOException e)
{
System.err.println("Was unable to save the image to " + fileName);
}
g.dispose();
}
}
I have created an applet with a red circle to move from top left to bottom right. That occurs but the whole frame is flickering even though I generate the circle as a buffered image. Here is the code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;
public class MovingCircleApplet extends JApplet implements Runnable {
private int x_pos;
private int y_pos;
private int radius;
private Thread circle;
#Override
public void init() {
x_pos = 20;
y_pos = 40;
radius = 10;
setSize(245,265);
circle = new Thread(this);
circle.start();
}
#Override
public void run()
{
while (true)
{
// redraw
repaint();
try
{
// sleep thread for 20 milliseconds
Thread.sleep(20);
}
catch (InterruptedException e)
{
System.out.println("Terminated prematurely due to interruption");
}
// move circle to lower right corner
if (x_pos < 225)
{
x_pos++;
y_pos++;
}
// restart at top right corner
else
{
x_pos = 20;
y_pos = 40;
}
}
}
#Override
public void paint(Graphics g)
{
super.paint(g);
g.drawImage(getFrame(), 0, 0, null);
}
private Image getFrame()
{
// Buffered image to prevent flickering
BufferedImage img = new BufferedImage(getWidth(),getHeight(),
BufferedImage.TYPE_3BYTE_BGR);
Graphics g = img.getGraphics();
// white background
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
// red circle
g.setColor(Color.RED);
g.fillOval(x_pos-radius, y_pos-radius, 2*radius, 2*radius);
//
g.dispose();
return img;
}
}
I am wanting to make a game that has each level loaded from an image.
I want to draw up the whole level in Photoshop, and then set it as the background and allow the player to walk over it.
I want another invisible image to go over top which will be black in all places that I want to collide with.
The reason I don't want to use tiles, which are much easier with rectangle collision and such, is because there will be complex corners and not everything will be rectangle.
Is this a good idea, and is it possible to do easily?
Would this be a big CPU hog or is there a better way to do this?
Level image
Obstacles shown in red
..there will be complex corners and not everything will be rectangle.
This could be achieved by drawing and dealing with Shape and Area instances. E.G.
Yellow is a little animated 'player'.
The bounds of the image represent walls that contain the path of the player (it bounces off them).
Obstacles are painted green when not in collision, red otherwise.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class ShapeCollision {
private BufferedImage img;
private Area[] obstacles = new Area[4];
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
/** A method to determine if two instances of Area intersect */
public boolean doAreasCollide(Area area1, Area area2) {
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1)) {
collide = true;
}
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2)) {
collide = true;
}
return collide;
}
ShapeCollision() {
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w/2;
y = h/2;
//circle
obstacles[0] = new Area(new Ellipse2D.Double(40, 40, 30, 30));
int[] xTriangle = {330,360,345};
int[] yTriangle = {60,60,40};
//triangle
obstacles[1] = new Area(new Polygon(xTriangle, yTriangle, 3));
int[] xDiamond = {60,80,60,40};
int[] yDiamond = {120,140,160,140};
//diamond
obstacles[2] = new Area(new Polygon(xDiamond, yDiamond, 4));
int[] xOther = {360,340,360,340};
int[] yOther = {130,110,170,150};
// other
obstacles[3] = new Area(new Polygon(xOther, yOther, 4));
walls = new Area(new Rectangle(0,0,w,h));
ActionListener animate = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
animate();
imageLabel.repaint();
}
};
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
}
public void animate() {
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLUE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x+=xDelta;
y+=yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player,walls)) {
if ( x+s>img.getWidth() || x<0 ) {
xDelta *= -1;
}
if(y+s>img.getHeight() || y<0 ) {
yDelta *= -1;
}
}
g.setColor(Color.ORANGE);
for (Area obstacle : obstacles) {
if (doAreasCollide(obstacle, player)) {
g.setColor(Color.RED);
} else {
g.setColor(Color.GREEN);
}
g.fill(obstacle);
}
g.setColor(Color.YELLOW);
g.fill(player);
g.dispose();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new ShapeCollision();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
Edit
make it detect all the red color and set that as the collision bounds
At start-up, use the source seen in the Smoothing a jagged path question to get an outline of the red pixels (see the getOutline(Color target, BufferedImage bi) method). Store that Area as the single obstacle on start-up.
Th code below has few problems :
1) The polygon joins last and first point itself, should not do itself but user should draw it.
2) The polygons lines disappeared after clicking on other shapes.
package Circles;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
//////////////////////////////////////////////////////////////PaintDemo
class PaintDemo2 {
//============================================================= main
public static void main(String[] args) {
PaintWindow2 window = new PaintWindow2();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}//end main
}//endclass PaintDemo
#SuppressWarnings("serial")
////////////////////////////////////////////////////////////PaintWindow
class PaintWindow2 extends JFrame {
PaintPanel2 canvas = new PaintPanel2();
//====================================================== constructor
public PaintWindow2() {
//--- create the buttons
JButton circleButton = new JButton("Circle");
circleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
canvas.setShape(PaintPanel2.CIRCLE);
}});
JButton rectangleButton = new JButton("Rectangle");
rectangleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
canvas.setShape(PaintPanel2.Ellipse);
}});
JButton polyButton = new JButton("Polygon");
polyButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
canvas.setShape(PaintPanel2.POLY);
}});
//--- layout the buttons
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(2, 1));
buttonPanel.add(circleButton);
buttonPanel.add(rectangleButton);
buttonPanel.add(polyButton);
//--- layout the window
Container content = this.getContentPane();
content.setLayout(new BorderLayout());
content.add(buttonPanel, BorderLayout.WEST);
content.add(canvas , BorderLayout.CENTER);
this.setTitle("Paint Demo");
this.pack();
}//end constructor
}//endclass PaintWindow
///////////////////////////////////////////////////////////// PaintPanel2
#SuppressWarnings("serial")
class PaintPanel2 extends JPanel implements MouseListener,
MouseMotionListener {
//--- Public constants used to specify shape being drawn.
public static final int NONE = 0;
public static final int LINE = 1;
public static final int Ellipse = 2;
public static final int CIRCLE = 3;
public static final int POLY = 4;
//--- Variables to store the current figure info
private int _shape = NONE;
public int getShape() {
return _shape;
}
private int _currentStartX = 0; // where mouse first pressed
private int _currentStartY = 0;
private int _currentEndX = 0; // where dragged to or released
private int _currentEndY = 0;
//--- BufferedImage to store the underlying saved painting.
// Will be initialized first time paintComponent is called.
private BufferedImage _bufImage = null;
private boolean polygonIsNowComplete = false;
//--- Private constant for size of paint area.
private static final int SIZE = 600; // size of paint area
private final Point trackPoint = new Point();
private Path2D currentShape;
private ArrayList<Path2D> lstPloys = new ArrayList<Path2D>();;
private Point lastPoint;
private Point currentPoint;
#SuppressWarnings("rawtypes")
private ArrayList points = new ArrayList();
//====================================================== constructor
public PaintPanel2() {
setPreferredSize(new Dimension(SIZE, SIZE));
setBackground(Color.white);
//--- Add the mouse listeners.
this.addMouseListener(this);
this.addMouseMotionListener(this);
}//endconstructor
//========================================================= setShape
public void setShape(int shape) {
//--- Provided so users can set the shape.
_shape = shape;
}//end setShape
//=================================================== paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g; // downcast to Graphics2D
if (_bufImage == null) {
//--- This is the first time, initialize _bufImage
int w = this.getWidth();
int h = this.getHeight();
_bufImage = (BufferedImage)this.createImage(w, h);
Graphics2D gc = _bufImage.createGraphics();
gc.setColor(Color.white);
gc.fillRect(0, 0, w, h); // fill in background
}
g2.drawImage(_bufImage, null, 0, 0); // draw previous shapes
drawCurrentShape(g2);
}//end paintComponent
//================================================= drawCurrentShape
private void drawCurrentShape(Graphics2D g2) {
//--- Draws current shape on a graphics context, either
// on the context passed to paintComponent, or the
// context for the BufferedImage.
switch (_shape) {
case NONE :
break;
case CIRCLE:
g2.drawOval(_currentStartX, _currentStartY,
_currentEndX - _currentStartX,
_currentEndY - _currentStartY);
break;
case Ellipse:
g2.draw(new Ellipse2D.Double(_currentStartX, _currentStartY,
_currentEndX - _currentStartX,
_currentEndY - _currentStartY));
break;
case POLY:
drawPolyGon(g2);
break;
default: // should never happen
g2.drawString("Huh?", 10, 20);
break;
}
}//end paintComponent
private void drawPolyGon(Graphics2D g2) {
g2.create();
if (lastPoint != null) {
g2.setColor(Color.RED);
g2.fillOval(lastPoint.x - 2, lastPoint.y - 2, 4, 4);
}
if (currentShape != null) {
g2.setColor(Color.RED);
g2.draw(currentShape);
if (lastPoint != null && currentPoint != null) {
g2.setColor(new Color(255, 0, 0, 64));
g2.draw(new Line2D.Float(lastPoint, currentPoint));
}
}
g2.setColor(Color.BLACK);
for (Path2D shape : lstPloys) {
g2.draw(shape);
}
g2.dispose();
// TODO Auto-generated method stub
}
//===================================================== mousePressed
public void mousePressed(MouseEvent e) {
_currentStartX = e.getX(); // save x coordinate of the click
_currentStartY = e.getY(); // save y
_currentEndX = _currentStartX; // set end to same pixel
_currentEndY = _currentStartY;
}//end mousePressed
//===================================================== mouseDragged
public void mouseDragged(MouseEvent e) {
_currentEndX = e.getX(); // save new x and y coordinates
_currentEndY = e.getY();
this.repaint();
// show new shape
}//end mouseDragged
//==================================================== mouseReleased
public void mouseReleased(MouseEvent e) {
// This will save the shape that has been dragged by
// drawing it onto the bufferedImage where all shapes
// are written.
_currentEndX = e.getX(); // save ending coordinates
_currentEndY = e.getY();
//--- Draw the current shape onto the buffered image.
Graphics2D grafarea = _bufImage.createGraphics();
drawCurrentShape(grafarea);
this.repaint();
}//end mouseReleased
public void mouseMoved (MouseEvent e) {
if (currentShape != null) {
currentPoint = e.getPoint();
repaint();
} else {
currentPoint = null;
}
}
public void mouseEntered (MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mouseClicked (MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.getClickCount() == 1) {
Point p = e.getPoint();
lastPoint = p;
if (currentShape == null) {
currentShape = new Path2D.Float();
currentShape.moveTo(p.x, p.y);
} else {
currentShape.lineTo(p.x, p.y);
}
repaint();
} else if (e.getClickCount() == 2) {
currentShape.closePath();
lstPloys.add(currentShape);
currentShape = null;
lastPoint = null;
repaint();
}
}
}
}
Paint cycles are stateless, that is, the contents of the graphics are not passed from one cycle to another.
You are required to re-paint the entire contents of component on each paint cycles.
You've attempt to implement a double buffer solution, but instead of passing the graphs context of the buffer, you've passed the graphics contents supplied to you by the paint system. If you passed the graphs context of the buffer to the drawCurrentShape method, it might solve your problem (and eliminate the need to cache all the shapes)
UPDATED
So, in your paintComponent method of your PaintPanel2 component, you are creating a BufferedImage, but you are not painting the components to it...
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g; // downcast to Graphics2D
if (_bufImage == null) {
//--- This is the first time, initialize _bufImage
int w = this.getWidth();
int h = this.getHeight();
_bufImage = (BufferedImage)this.createImage(w, h);
Graphics2D gc = _bufImage.createGraphics();
gc.setColor(Color.white);
gc.fillRect(0, 0, w, h); // fill in background
}
g2.drawImage(_bufImage, null, 0, 0); // draw previous shapes
drawCurrentShape(g2);
}//end paintComponent
Instead, you should use the graphics context from the buffered image to drawCurrentShape
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g; // downcast to Graphics2D
Graphics2D gc = null;
if (_bufImage == null) {
//--- This is the first time, initialize _bufImage
int w = this.getWidth();
int h = this.getHeight();
_bufImage = (BufferedImage)this.createImage(w, h);
gc = _bufImage.createGraphics();
gc.setColor(Color.white);
gc.fillRect(0, 0, w, h); // fill in background
} else {
gc = _bufImage.createGraphics();
}
drawCurrentShape(g2);
gc.dispose();
g2.drawImage(_bufImage, null, 0, 0); // draw previous shapes
}//end paintComponent
It should be noted that this might create some other issues, but the concept is sound.
Personally, I prefer to keep a List of all the shapes and repaint them. This gives you the ability to select, move, delete, re-order and change all the shapes within the program, as well as provide a type of history ;)