How can I quickly and efficiently set all pixels of a BufferedImage to transparent so that I can simply redraw what sprite graphics I want for each frame?
I am designing a simple game engine in java that updates a background and foreground BufferedImage and draws them to a composite VolatileImage for efficient scaling, to be drawn to a JPanel. This scalable model allows me to add more layers and iterate over each drawing layer.
I simplified my application into one class given below that demonstrates my issue. Use the arrow keys to move a red square over the image. The challenge is I want to decouple updating the game graphics from drawing the composite graphics to the game engine. I have studied seemingly thorough answers to this question but cannot figure out how to apply them to my application:
Java: Filling a BufferedImage with transparent pixels
Here is the critical section that does not clear the pixels correctly. The commented out section is from stack-overflow answers I have read already, but they either draw the background as a non-transparent black or white. I know the foregroundImage begins with transparent pixels in my implementation as you can see the random pixel noise of the backgroundImage behind the red sprite when the application begins. Right now, the image is not cleared, so the previous drawn images remain.
/** Update the foregroundGraphics. */
private void updateGraphics(){
Graphics2D fgGraphics = (Graphics2D) foregroundImage.getGraphics();
// set image pixels to transparent
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
//fgGraphics.setColor(new Color(0,0,0,0));
//fgGraphics.clearRect(0, 0, width, height);
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw again.
fgGraphics.setColor(Color.RED);
fgGraphics.fillRect(sx, sy, spriteSize, spriteSize);
fgGraphics.dispose();
}
Here is my entire example code:
/**
* The goal is to draw two BufferedImages quickly onto a scalable JPanel, using
* a VolatileImage as a composite.
*/
public class Example extends JPanel implements Runnable, KeyListener
{
private static final long serialVersionUID = 1L;
private int width;
private int height;
private Object imageLock;
private Random random;
private JFrame frame;
private Container contentPane;
private BufferedImage backgroundImage;
private BufferedImage foregroundImage;
private VolatileImage compositeImage;
private Graphics2D compositeGraphics;
private int[] backgroundPixels;
private int[] foregroundPixels;
// throttle the framerate.
private long prevUpdate;
private int frameRate;
private int maximumWait;
// movement values.
private int speed;
private int sx;
private int sy;
private int dx;
private int dy;
private int spriteSize;
/** Setup required fields. */
public Example(){
width = 512;
height = 288;
super.setPreferredSize(new Dimension(width, height));
imageLock = new Object();
random = new Random();
frame = new JFrame("BufferedImage Example");
frame.addKeyListener(this);
contentPane = frame.getContentPane();
contentPane.add(this, BorderLayout.CENTER);
// used to create hardware-accelerated images.
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
backgroundImage = gc.createCompatibleImage(width, height,Transparency.TRANSLUCENT);
foregroundImage = gc.createCompatibleImage(width, height,Transparency.TRANSLUCENT);
compositeImage = gc.createCompatibleVolatileImage(width, height,Transparency.TRANSLUCENT);
compositeGraphics = compositeImage.createGraphics();
compositeGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
compositeGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
backgroundPixels = ((DataBufferInt) backgroundImage.getRaster().getDataBuffer()).getData();
foregroundPixels = ((DataBufferInt) foregroundImage.getRaster().getDataBuffer()).getData();
//initialize the background image.
for(int i = 0; i < backgroundPixels.length; i++){
backgroundPixels[i] = random.nextInt();
}
// used to throttle frames per second
frameRate = 180;
maximumWait = 1000 / frameRate;
prevUpdate = System.currentTimeMillis();
// used to update sprite state.
speed = 1;
dx = 0;
dy = 0;
sx = 0;
sy = 0;
spriteSize = 32;
}
/** Renders the compositeImage to the Example, scaling to fit. */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// draw the composite, scaled to the JPanel.
synchronized (imageLock) {
((Graphics2D) g).drawImage(compositeImage, 0, 0, super.getWidth(), super.getHeight(), 0, 0, width, height, null);
}
// force repaint.
repaint();
}
/** Update the BufferedImage states. */
#Override
public void run() {
while(true){
updateSprite();
updateGraphics();
updateComposite();
throttleUpdateSpeed();
}
}
/** Update the Sprite's position. */
private void updateSprite(){
// update the sprite state from the inputs.
dx = 0;
dy = 0;
if (Command.UP.isPressed()) dy -= speed;
if (Command.DOWN.isPressed()) dy += speed;
if (Command.LEFT.isPressed()) dx -= speed;
if (Command.RIGHT.isPressed()) dx += speed;
sx += dx;
sy += dy;
// adjust to keep in bounds.
sx = sx < 0 ? 0 : sx + spriteSize >= width ? width - spriteSize : sx;
sy = sy < 0 ? 0 : sy + spriteSize >= height ? height - spriteSize : sy;
}
/** Update the foregroundGraphics. */
private void updateGraphics(){
Graphics2D fgGraphics = (Graphics2D) foregroundImage.getGraphics();
// set image pixels to transparent
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
//fgGraphics.setColor(new Color(255, 255, 255, 255));
//fgGraphics.clearRect(0, 0, width, height);
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw again.
fgGraphics.setColor(Color.RED);
fgGraphics.fillRect(sx, sy, spriteSize, spriteSize);
fgGraphics.dispose();
}
/** Draw the background and foreground images to the volatile composite. */
private void updateComposite(){
synchronized (imageLock) {
compositeGraphics.drawImage(backgroundImage, 0, 0, null);
compositeGraphics.drawImage(foregroundImage, 0, 0, null);
}
}
/** Keep the update rate around 60 FPS. */
public void throttleUpdateSpeed(){
try {
Thread.sleep(Math.max(0, maximumWait - (System.currentTimeMillis() - prevUpdate)));
prevUpdate = System.currentTimeMillis();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
/** Ignore key typed events. */
#Override
public void keyTyped(KeyEvent e) {}
/** Handle key presses. */
#Override
public void keyPressed(KeyEvent e) {
setCommandPressedFrom(e.getKeyCode(), true);
}
/** Handle key releases. */
#Override
public void keyReleased(KeyEvent e) {
setCommandPressedFrom(e.getKeyCode(), false);
}
/** Switch over key codes and set the associated Command's pressed value. */
private void setCommandPressedFrom(int keycode, boolean pressed){
switch (keycode) {
case KeyEvent.VK_UP:
Command.UP.setPressed(pressed);
break;
case KeyEvent.VK_DOWN:
Command.DOWN.setPressed(pressed);
break;
case KeyEvent.VK_LEFT:
Command.LEFT.setPressed(pressed);
break;
case KeyEvent.VK_RIGHT:
Command.RIGHT.setPressed(pressed);
break;
}
}
/** Commands are used to interface with key press values. */
public enum Command{
UP, DOWN, LEFT, RIGHT;
private boolean pressed;
/** Press the Command. */
public void press() {
if (!pressed) pressed = true;
}
/** Release the Command. */
public void release() {
if (pressed) pressed = false;
}
/** Check if the Command is pressed. */
public boolean isPressed() {
return pressed;
}
/** Set if the Command is pressed. */
public void setPressed(boolean pressed) {
if (pressed) press();
else release();
}
}
/** Begin the Example. */
public void start(){
try {
// create and display the frame.
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Example e = new Example();
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
// start updating from key inputs.
Thread t = new Thread(this);
t.start();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
/** Start the application. */
public static void main(String[] args){
Example e = new Example();
e.start();
}
}
Edits:
- Fixed a typo in the for-loop initializing the backgroundPixels to random.
Turns out I goofed in my method selection. I noticed I was clearing a one-pixel wide box that was the outline of my graphics. This is because I accidentally used drawRect() instead of fillRect(). Upon changing my code it works now. Here are examples I was able to get to work.
Example using AlphaComposite.CLEAR (draw with any opaque color):
// clear pixels
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
fgGraphics.setColor(new Color(255,255,255,255));
fgGraphics.fillRect(0, 0, width, height);
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw new graphics
Example using AlphaComposite.SRC_OUT (draw with any color with alpha zero):
// clear pixels
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
fgGraphics.setColor(new Color(255,255,255,0));
fgGraphics.fillRect(0, 0, width, height);
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw new graphics
Related
I'm using double-buffered graphics in my JLayer subclass to implement a simple swipe animation in a Java Swing application. It works fine on the older displays, but when I run it on a Retina display, the screen loses the doubled-resolution when the animation starts, and gets it back when it ends. I'm not sure how to maintain the higher resolution during the animation.
My animate method originally looked like this:
private void animate() {
Timer timer = new Timer(frameMillis, null);
final ActionListener actionListener = (evt) -> { /* omitted for brevity */ };
timer.addActionListener(actionListener);
int imageType = BufferedImage.TYPE_INT_ARGB;
upcomingScreen = new BufferedImage(liveComponent.getWidth(), liveComponent.getHeight(), imageType);
Graphics2D graphics2D = (Graphics2D) upcomingScreen.getGraphics();
liveComponent.paint(graphics2D); // liveComponent is a JComponent
graphics2D.dispose();
timer.start();
}
I tried doubling the image size, but that didn't help.
upcomingScreen = new BufferedImage(liveComponent.getWidth()*2, liveComponent.getHeight()*2, imageType);
To reflect these changes, I changed my drawing code in LayerUI by doubling xLimit, width, height:
public void paint(final Graphics g, final JComponent c) {
if (isAnimating) {
int xLimit = (c.getWidth()*2 * frame) / maxFrames;
int width = c.getWidth()*2;
int height = c.getHeight()*2;
g.drawImage(uScreen, 0, 0, xLimit, height, 0, 0, xLimit, height, c);
g.drawImage(pScreen, xLimit, 0, width, height, xLimit, 0, width, height, c);
} else {
super.paint(g, c);
}
}
This doesn't help. It draws the same with or without this last change, which makes no sense.
Here is a class that illustrates the problem:
/**
* <p>Created by IntelliJ IDEA.
* <p>Date: 5/2/20
* <p>Time: 10:25 AM
*
* #author Miguel Mu\u00f1oz
*/
#SuppressWarnings({"HardcodedLineSeparator", "StringConcatenation", "HardCodedStringLiteral", "DuplicatedCode"})
public final class SwipeViewTest extends JPanel {
public static final String text1 = "Demo of Swipe View.\n\nThe swipe button will toggle between two pages of text. It has a built-in " +
"special effect, which is a swipe. When you hit the swipe button, it should flip between two pages of text. This worked fine on " +
"the older displays, but for some reason, on a Retina display, the text briefly switches to low resolution as the swipe proceeds, " +
"then switches back once it has finished. This code is written for retina displays. I don't know if it will work for the older, " +
"low resolution displays.\n\nYou can watch it swipe by hitting the space bar or by clicking the swipe button.";
public static final String text2 = "Demo of Swipe View.\n\nThis is the second page of the swipe-text demo. The change in resolution is " +
"most easily noticed when watching the line at the top, which doesn't change as the swipe is performed.";
private final SwipeView<TestView> swipeView;
private final TestView testView;
public static void main(String[] args) {
JFrame frame = new JFrame("SwipeView demo");
SwipeViewTest comp = new SwipeViewTest();
comp.install();
frame.add(comp);
frame.setLocationByPlatform(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
private boolean page1 = true;
private SwipeViewTest() {
super(new BorderLayout());
testView = new TestView();
swipeView = SwipeView.wrap(testView, 1000);
add(BorderLayout.CENTER, swipeView.getLayer());
}
private void install() {
JButton jButton = new JButton("Swipe");
jButton.addActionListener(this::doSwipe);
add(jButton, BorderLayout.PAGE_END);
AncestorListener ancestorListener = new AncestorListener() {
#Override
public void ancestorAdded(final AncestorEvent event) {
JComponent button = event.getComponent();
button.requestFocus();
button.removeAncestorListener(this); // execute only once.
}
#Override public void ancestorRemoved(final AncestorEvent event) { }
#Override public void ancestorMoved(final AncestorEvent event) { }
};
jButton.addAncestorListener(ancestorListener);
}
private void doSwipe(ActionEvent ignored) {
swipeView.swipeLeft(this::flipPage);
}
private void flipPage() {
page1 = !page1;
if (page1) {
testView.setText(text1);
} else {
testView.setText(text2);
}
}
private static class TestView extends JPanel {
private final JTextArea textArea;
TestView() {
super(new BorderLayout());
textArea = new JTextArea(20, 40);
JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
textArea.setText(text1);
add(scrollPane, BorderLayout.CENTER);
}
private void setText(String text) {
textArea.setText(text);
}
}
/**
* SwipeView adds a swipe special effect to a Component. This draws a swipe-right or swipe-left effect on a chosen
* action. It also optionally supports a repeated action when the mouse is held down.
* <p>
* This class is very specific right now, but I hope to generalize it for other special effects later.
* <p>Created by IntelliJ IDEA.
* <p>Date: 4/4/18
* <p>Time: 12:38 AM
*
* #author Miguel Mu\u00f1oz
*/
#SuppressWarnings("MagicNumber")
public static final class SwipeView<C extends JComponent> extends LayerUI<C> {
public static <J extends JComponent> SwipeView<J> wrap(J view, int durationMillis) {
JLayer<J> jLayer = new JLayer<>(view);
final SwipeView<J> ui = new SwipeView<>(view, jLayer, durationMillis);
jLayer.setUI(ui);
return ui;
}
private final C liveComponent;
private Image priorScreen = null;
private Image upcomingScreen = null;
private final JLayer<C> layer;
private boolean isAnimating = false;
private SwipeDirection swipeDirection = SwipeDirection.SWIPE_RIGHT;
private final int maxFrames;
// Calculated:
#SuppressWarnings("FieldCanBeLocal")
private final int frameMillis;
private int frame = 0;
private final long startTime = System.currentTimeMillis();
private SwipeView(C view, JLayer<C> theLayer, int animationDurationMillis) {
super();
liveComponent = view;
layer = theLayer;
maxFrames = (30 * animationDurationMillis) / 1000;
frameMillis = animationDurationMillis / maxFrames;
}
public JLayer<C> getLayer() { return layer; }
/**
* Perform the specified operation with a swipe-right special effect. This is often used in an ActionListener:
* <pre>
* first.addActionListener((e) -> swipeView.swipeRight(recordModel::goFirst));
* </pre>
* Here, the Action listener will perform a Swipe-right after executing the goFirst() method of recordModel.
*
* #param operation The operation
*/
#SuppressWarnings("WeakerAccess")
public void swipeRight(Runnable operation) {
swipe(operation, SwipeDirection.SWIPE_RIGHT);
}
/**
* Perform the specified operation with a swipe-left special effect. This is often used in an ActionListener:
* <pre>
* first.addActionListener((e) -> swipeView.swipeLeft(recordModel::goFirst));
* </pre>
* Here, the Action listener will perform a Swipe-Left after executing the goFirst() method of recordModel.
*
* #param operation The operation
*/
#SuppressWarnings("WeakerAccess")
public void swipeLeft(Runnable operation) {
swipe(operation, SwipeDirection.SWIPE_LEFT);
}
private void swipe(Runnable operation, SwipeDirection swipeDirection) {
prepareToAnimate(swipeDirection);
operation.run();
animate();
}
// #SuppressWarnings({"HardCodedStringLiteral", "HardcodedFileSeparator"})
#Override
public void paint(final Graphics g, final JComponent c) {
if (isAnimating) {
int xLimit = (c.getWidth() * 2 * frame) / maxFrames;
if (swipeDirection == SwipeDirection.SWIPE_LEFT) {
xLimit = (c.getWidth() * 2) - xLimit;
}
int width = c.getWidth() * 2;
int height = c.getHeight() * 2;
// //noinspection UseOfSystemOutOrSystemErr
// System.out.printf("Dimensions: Frame: %d/%d (at %d) xLimit: %4d (%4d x %4d) (from %4d x %4d) Animating: %b%n",
// frame, maxFrames, System.currentTimeMillis() - startTime, xLimit, width, height, c.getWidth(), c.getHeight(), isAnimating);
assert upcomingScreen != null;
assert priorScreen != null;
Image pScreen = Objects.requireNonNull(priorScreen);
Image uScreen = Objects.requireNonNull(upcomingScreen);
if (swipeDirection == SwipeDirection.SWIPE_RIGHT) {
g.drawImage(uScreen, 0, 0, xLimit, height, 0, 0, xLimit, height, c);
g.drawImage(pScreen, xLimit, 0, width, height, xLimit, 0, width, height, c);
} else {
g.drawImage(uScreen, xLimit, 0, width, height, xLimit, 0, width, height, c);
g.drawImage(pScreen, 0, 0, xLimit, height, 0, 0, xLimit, height, c);
}
} else {
super.paint(g, c);
}
}
private void prepareToAnimate(SwipeDirection swipeDirection) {
this.swipeDirection = swipeDirection;
isAnimating = true;
frame = 0;
// Save current state
priorScreen = new BufferedImage(liveComponent.getWidth() * 2, liveComponent.getHeight() * 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = (Graphics2D) priorScreen.getGraphics();
liveComponent.paint(graphics2D);
graphics2D.dispose();
}
private void animate() {
Timer timer = new Timer(frameMillis, null);
final ActionListener actionListener = (evt) -> {
frame++;
layer.repaint();
if (frame == maxFrames) {
frame = 0;
isAnimating = false;
timer.stop(); // Investigate: Am I leaking timers?
}
};
timer.addActionListener(actionListener);
upcomingScreen = new BufferedImage(liveComponent.getWidth() * 2, liveComponent.getHeight() * 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = (Graphics2D) upcomingScreen.getGraphics();
liveComponent.paint(graphics2D);
graphics2D.dispose();
timer.start();
}
}
public static enum SwipeDirection {
#SuppressWarnings("JavaDoc") SWIPE_RIGHT,
#SuppressWarnings("JavaDoc") SWIPE_LEFT
}
}
I don't use a retina display, but I did notice a slight painting difference when the animation started.
I changed both of your BufferedImage to get rid of the alpha value and I no longer notice the painting difference:
//priorScreen = new BufferedImage(liveComponent.getWidth() * 2, liveComponent.getHeight() * 2, BufferedImage.TYPE_INT_ARGB);
priorScreen = new BufferedImage(liveComponent.getWidth() * 2, liveComponent.getHeight() * 2, BufferedImage.TYPE_INT_RGB);
It turns out I needed to change the way I animated the frame to account for the doubling of the scale.
First, I needed to detect the scale. I added this code, which requires Java 9 or greater to work correctly. (It compiles under java 8, but fails to execute correctly, always returning 1 for any screen.)
private static final int SCALE = calculateScaleForDefaultScreen();
private static int calculateScaleForDefaultScreen() {
// scale will be 2.0 for a Retina screen, and 1.0 for an older screen
double scale = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration()
.getDefaultTransform()
.getScaleX(); // Requires Java 9+ to work. Compiles under Java 8 but always returns 1.0.
//noinspection NumericCastThatLosesPrecision
return (int) Math.round(scale);
}
When I prepared my two off-screen graphics, I needed to do so at twice the scale:
Graphics2D graphics2D = (Graphics2D) priorScreen.getGraphics();
graphics2D.scale(SCALE, SCALE);
liveComponent.paint(graphics2D); // paint the current state of liveComponent into the image
graphics2D.dispose();
And…
Graphics2D graphics2D = (Graphics2D) upcomingScreen.getGraphics();
graphics2D.scale(SCALE, SCALE);
liveComponent.paint(graphics2D); // paint the upcoming state of liveComponent into the image
graphics2D.dispose();
Then, when I did my animation, I needed to include the SCALE in the drawing.
if (swipeDirection == SwipeDirection.SWIPE_RIGHT) {
g.drawImage(uScreen, 0, 0, xLimit, height, 0, 0, xLimit*SCALE, height*SCALE, c);
g.drawImage(pScreen, xLimit, 0, width, height, xLimit*SCALE, 0, width*SCALE, height*SCALE, c);
} else {
g.drawImage(uScreen, xLimit, 0, width, height, xLimit*SCALE, 0, width*SCALE, height*SCALE, c);
g.drawImage(pScreen, 0, 0, xLimit, height, 0, 0, xLimit*SCALE, height*SCALE, c);
}
There are several other places where I multiplied widths and heights by 2. I changed those to SCALE as well.
I wish there were a more elegant solution, but this works.
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) {}
}
i'm working on a Pong game now and I my ball animation is going too fast. I want to add a timer to my animation but I really don't know how to do it. I tried it with some code i found on the internet but it don't work. please help me :(
here is my code :
private static final long serialVersionUID = 1L;
private int posX = SCREEN_WIDTH / 2;
private int posY;
int x;
int y;
private int scoreCountPlayer1 = 0;
private int scoreCountComputer = 0;
private int delay = 10;
// Create a timer with delay 1000 ms
private Timer timer = new Timer(delay, new TimerListener());
private Rectangle ballRect;
private Rectangle padRect;
private int upLimit;
private int downLimit;
private int padPosition;
public boolean backX = false;
public boolean backY = false;
public boolean move = true;
public Point posMouse = new Point();
private int playPanelWidth;
private int playPanelHeight;
private int padPanelWidth;
private int padPanelHeight;
private int panPanelWidth;
private int panPanelHeight;
private JLabel player1Score = new JLabel("1");
private JLabel ComputerScore = new JLabel("0");
private JPanel panPlayer1;
public JPanel panComputer;
public JPanel padPlayer1;
public JPanel padComputer;
public ScorePanel scorePanel;
private JButton but_Escape = new JButton("Press Space to continue !");
/*
* Constructeur de classe : PlayPanel.java
*/
// ==============================================
public PlayPanel() {
super(new BorderLayout());
setBackground(PANPLAY_COLOR);
scorePanel = new ScorePanel();
panPlayer1 = new JPanel();
panComputer = new JPanel();
padPlayer1 = new JPanel();
padComputer = new JPanel();
padPlayer1.setBackground(Color.DARK_GRAY);
padComputer.setBackground(Color.DARK_GRAY);
padPlayer1.setPreferredSize(PADPANEL_SIZE);
padComputer.setPreferredSize(PADPANEL_SIZE);
panPlayer1.setBackground(PANPLAY_COLOR);
panComputer.setBackground(PANPLAY_COLOR);
panPlayer1.add(padPlayer1);
panComputer.add(padComputer);
add(panPlayer1, BorderLayout.WEST);
add(panComputer, BorderLayout.EAST);
add(scorePanel, BorderLayout.SOUTH);
player1Score.setFont(FONT_SCORE);
ComputerScore.setFont(FONT_SCORE);
addMouseMotionListener(this);
timer.start();
}
/*
* Add the ball
*/
// ==============================================
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.BLACK);
initBall(g2);
// trait épais
g2.setColor(Color.DARK_GRAY);
g2.setStroke(new BasicStroke(10));
g2.drawLine((getPlayPanelWidth() / 2) - 5, getPlayPanelHeight(),
(getPlayPanelWidth() / 2) - 5, 0);
}
/*
* Init ball
*/
// ==============================================
private void initBall(Graphics2D graphics2d) {
Graphics2D g2 = graphics2d;
x = getPosX();
y = getPosY();
ballRect = new Rectangle(posX, posY, BALL_WIDTH, BALL_HEIGHT);
padRect = new Rectangle(playPanelWidth
- (getPanComputer().getWidth() + 2), (int) getPosMouse().getY()
- getPadPanelHeight() / 2, padPanelWidth, padPanelHeight);
g2.fillOval(posX, posY, BALL_WIDTH, BALL_HEIGHT);
if (posX == 763) {
if (ballRect.intersects(padRect)) {
System.out.println("collision");
Move();
} else {
int posMouseY = getPosMouse().y;
System.out.println("pas collision");
stopBall(g2, posMouseY);
}
} else {
Move();
}
}
private void Move() {
if (x < 1 + 15) {
backX = false;
scorePanel.getLab_Player1().setText("" + scoreCountPlayer1);
}
if (x > getWidth() - 52) {
backX = true;
}
if (y < 1) {
backY = false;
}
if (y > getHeight() - (SCOREPANEL_SIZE.getHeight() + BALL_HEIGHT)) {
backY = true;
}
if (!backX) {
setPosX(++x);
}
else {
setPosX(--x);
}
if (!backY) {
setPosY(++y);
} else {
setPosY(--y);
}
repaint();
}
private void stopBall(final Graphics2D g2, int posBallY) {
move = false;
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent arg0) {
// Définit les limite de le souris, la position
// des pads et de la souris.
upLimit = getPadPanelHeight() / 2;
downLimit = playPanelHeight - scorePanel.getScorePanHeight()
- (getPadPanelHeight() / 2);
padPosition = playPanelHeight - scorePanel.getScorePanHeight()
- (getPadPanelHeight());
posMouse.setLocation(arg0.getX(), arg0.getY());
setPosMouse(posMouse);
if (arg0.getY() >= downLimit) {
padPlayer1.setLocation(getPanPanelWidth() - 10, padPosition);
padComputer.setLocation(0, padPosition);
} else if (arg0.getY() <= upLimit) {
padPlayer1.setLocation(getPanPanelWidth() - 10, 0);
padComputer.setLocation(0, 0);
} else {
padPlayer1.setLocation(getPanPanelWidth() - 10,
(int) posMouse.getY());
padComputer.setLocation(0, (int) posMouse.getY()
- (getPadPanelHeight() / 2));
}
}
private class TimerListener implements ActionListener {
/** Handle the action event */
public void actionPerformed(ActionEvent e) {
repaint();
}
}
}
I am not too familiar with animations, but what I think you need to be doing is use the timer to move the ball a certain distance at fixed time intervals.
Your current code schedules (emphasis on schedule, and not perform) a repaint in almost every paint call (through the call to the move method). This will make it very hard to control the speed of the ball. You do not know how many repaints will actually be performed, hence you do not know how fast the ball moves (due to the fact that multiple scheduled repaints can be grouped into one repaint). Adding a Timer in the mix to perform some extra repaints will not help (and certainly not a timer which schedules repaints every second ... this would result in a 1FPS framerate, which looks so 1950 ).
If you would use your timer to change the coordinates of the ball at fixed time intervals (50ms, 100ms, ... experiment a bit) and schedule a repaint you have full control over the speed of the ball. Even if two repaint calls of the timer are grouped, the ball will skip a position but the speed will be consistent. And when you go up a level, just increase the number of times the Timer code get triggered by decreasing the delay.
You might want to read the freely available Animation chapter of the Filthy Rich Clients book, which features more free excerpts and code examples. You might also like to examine the examples seen here.
Use a Timer from java.util package:
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
repaint();
}
}, 20, 20); // replace 20 with how often you want it to be called (in milliseconds)
The problem is that You are moving the ball by one pixel for every loop, I think that you should change the x and y coordinatea from int to double, introduce a new variable named speed with the value 0.1 and adding this speed to the coordinates.
Then the ball should move one pixel for every 10 game loops, maybe a smaller value should be needed, but you'll have to tweak it
Hope it helps
I've been searching for a way to draw a black-and-white array on screen. It's a simple array, just 20x20. What I plan to do is to draw on an array with the mouse so that each pixel "toggles" from black to white and back when clicked, then pass the array as a set of booleans (or integers) to another function. Currently I'm using Swing. I do remember to have used Swing for drawing on a canvas, but I still can't find the actual usage. Should I use a canvas, or instead rely on JToggleButtons?
You can simply use a JFrame (or other Swing component) and override the paint(Graphics) method to draw a representation of the boolean matrix (note that in the case of a lightweight component such as JPanel you should override paintComponent(Graphics). This will give you the click-and-drag capability you require (which is very difficult to achieve using a grid of individual Swing components).
As other people have commented, AWT Canvas doesn't give you anything not provided by Swing components and you'll see in the example below that I've used the createBufferStrategy method also present on JFrame to ensure a non-flicker display.
Note that my example is fairly simple in that it toggles every pixel you drag across rather than the click operation establishing whether you're in "paint" mode or "erase" mode and then exclusively applying black or white pixels for the duration of the drag.
public class Grid extends JFrame {
private static final int SCALE = 10; // 1 boolean value == 10 x 10 pixels.
private static final int SIZE = 20;
private boolean[][] matrix = new boolean[SIZE][SIZE];
private boolean painting;
private int lastX = -1;
private int lastY = -1;
public Grid() throws HeadlessException {
setPreferredSize(new Dimension(SIZE * SCALE, SIZE * SCALE));
setResizable(false);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
painting = true;
tryAdjustValue(e.getPoint());
}
public void mouseReleased(MouseEvent e) {
painting = false;
lastX = -1;
lastY = -1;
}
});
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
tryAdjustValue(e.getPoint());
}
public void mouseMoved(MouseEvent e) {
tryAdjustValue(e.getPoint());
}
});
}
private void tryAdjustValue(Point pt) {
int newX = pt.x / SCALE;
int newY = pt.y / SCALE;
if (painting && isInRange(newX) && isInRange(newY) && (newX != lastX || newY != lastY)) {
// Only invert "pixel" if we're currently in painting mode, both array indices are valid
// and we're not attempting to adjust the same "pixel" as before (important for drag operations).
matrix[newX][newY] = !matrix[newX][newY];
lastX = newX;
lastY = newY;
repaint();
}
}
private boolean isInRange(int val) {
return val >= 0 && val < SIZE;
}
public void paint(Graphics g) {
super.paint(g);
for (int x=0; x<SIZE; ++x) {
for (int y=0; y<SIZE; ++y) {
if (matrix[x][y]) {
g.fillRect(x * SCALE, y * SCALE, SCALE, SCALE);
}
}
}
}
public static void main(String[] args) {
Grid grid = new Grid();
grid.pack();
grid.setLocationRelativeTo(null);
grid.createBufferStrategy(2);
grid.setVisible(true);
}
}
Why not a simple 20 x 20 grid of JPanel held in a GridLayout(20, 20), and flip the panel's background color if clicked via a MouseListener's mousePressed method. You could hold the panels in a 2D array and query their background color whenever the need arises.
You could also use JLabels for this, but you'd have to remember to turn their opaque properties to true. A JButton would work as well or a JToggleButton, ... the options are almost limitless. I do not recommend though that you use AWT (Canvas) as their's no need to step backwards in functionality since Swing handles this so well.
If you get stuck on this, why not come back and show us your code and we'll better be able to give you more specific help.
Another way to solve this is to use a single JPanel and override its paintComponent method. You could give it an int[][] array to serve as its model, and then in the paintComponent method draw rectangles of whatever color desired based on the state of the model. Then give it a MouseListener that changes the state of the model and calls repaint.
e.g.,
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class BlackWhiteGridPanel extends JPanel {
// can have multiple colors if desired
// public static final Color[] COLORS = {Color.black, Color.red, Color.blue, Color.white};
public static final Color[] COLORS = {Color.black, Color.white};
public static final int SIDE = 20;
private static final int BWG_WIDTH = 400;
private static final int BWG_HEIGHT = BWG_WIDTH;
private int[][] model = new int[SIDE][SIDE]; // filled with 0's.
public BlackWhiteGridPanel() {
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
myMousePressed(e);
}
});
}
private void myMousePressed(MouseEvent e) {
// find relative position of mouse press on grid.
int i = (e.getX() * SIDE) / getWidth();
int j = (e.getY() * SIDE) / getHeight();
int value = model[i][j];
// the model can only hold states allowed by the COLORS array.
// So if only two colors, then value can only be 0 or 1.
value = (value + 1) % COLORS.length;
model[i][j] = value;
repaint();
}
public int[][] getModel() {
// return a copy of model so as not to risk corruption from outside classes
int[][] copy = new int[model.length][model[0].length];
for (int i = 0; i < copy.length; i++) {
System.arraycopy(model[i], 0, copy[i], 0, model[i].length);
}
return copy;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int ht = getHeight();
for (int i = 0; i < model.length; i++) {
for (int j = 0; j < model[i].length; j++) {
Color c = COLORS[model[i][j]];
g.setColor(c);
int x = (i * width) / SIDE;
int y = (j * ht) / SIDE;
int w = ((i + 1) * width) / SIDE - x;
int h = ((j + 1) * ht) / SIDE - y;
g.fillRect(x, y, w, h);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(BWG_WIDTH, BWG_HEIGHT);
}
private static void createAndShowGui() {
BlackWhiteGridPanel mainPanel = new BlackWhiteGridPanel();
JFrame frame = new JFrame("BlackWhiteGrid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
so im building brick breaker on Java and so far I have most of the UI done but Im having an issue with showing my bricks on my UI. I have written the code for it in my paint method which builds my panel and then my panel is added to a JFrame in another class. Everything shows except for my bricks and I cant seem to figure out why..
// AnimationPanel.java
//
// Informatics 45 Spring 2010
// Code Example: Our Ball Animation Becomes a Game
//
// This is relatively similar to our AnimationPanel in the previous version
// of our ball animation, with two changes:
//
// * The paddle is now drawn, in addition to just the ball. For fun, I've
// drawn the paddle in a different color than the ball.
//
// * This panel has a MouseMotionListener attached to it. The job of a
// MouseMotionListener is to listen for mouse movement within a component.
// In this case, any mouse movement within our AnimationPanel will turn
// into events, which we'll handle by adjusting the center x-coordinate
// of the paddle accordingly.
//
// * Because we need to calculate a new position for the paddle as the mouse
// moves, we'll need to be able to convert coordinates in both directions
// (i.e., fractional coordinates to pixel coordinates and pixel coordinates
// to fractional coordinates).
package inf45.spring2010.examples.animation2.gui;
import inf45.spring2009.examples.animation2.model.Animation;
import inf45.spring2009.examples.animation2.model.AnimationState;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnimationPanel extends JPanel
{
private Animation animation;
private inf45.spring2009.examples.animation2.model.AnimationState currentState;
boolean brickVisible[][];
int bricksInRow = 4;
int bricksInColumn = 8;
int brickWidth;
int brickHeight;
int bricksLeft;
public AnimationPanel(Animation animation)
{
this.animation = animation;
currentState = null;
setBackground(Color.WHITE);
addMouseMotionListener(
new MouseMotionAdapter()
{
public void mouseMoved(MouseEvent e)
{
updatePaddlePosition(e.getX());
}
});
}
public void updateState(AnimationState newState)
{
currentState = newState;
}
private void updatePaddlePosition(int pixelX)
{
double paddleCenterX = convertPixelXToX(pixelX);
animation.updatePaddleCenterX(paddleCenterX);
}
public void paint(Graphics g)
{
super.paint(g);
if (currentState == null)
{
return;
}
int centerPixelX = convertXToPixelX(currentState.getBallCenterX());
int centerPixelY = convertYToPixelY(currentState.getBallCenterY());
int radiusX = convertXToPixelX(currentState.getBallRadius());
int radiusY = convertYToPixelY(currentState.getBallRadius());
int topLeftPixelX = centerPixelX - radiusX;
int topLeftPixelY = centerPixelY - radiusY;
int paddleCenterPixelX = convertXToPixelX(currentState.getPaddleCenterX());
int paddleCenterPixelY = convertYToPixelY(currentState.getPaddleCenterY());
int paddleWidthPixels = convertXToPixelX(currentState.getPaddleWidth());
int paddleHeightPixels = convertYToPixelY(currentState.getPaddleHeight());
int paddleTopLeftPixelX = paddleCenterPixelX - (paddleWidthPixels / 2);
int paddleTopLeftPixelY = paddleCenterPixelY - (paddleHeightPixels / 2);
Graphics2D g2d = (Graphics2D) g;
/* for (int x = 0;x<bricksInRow;x++){
for (int y = 0;y<bricksInColumn;y++){
boolean bricks[][] = null;
brickVisible[x][y] = bricks[x][y] ;
{
g2d.setColor(Color.black);
g2d.fillRect(x*brickWidth,y*brickHeight,brickWidth-1,brickHeight-1);
}
}
}
*/
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLUE);
g2d.fillOval(topLeftPixelX, topLeftPixelY, radiusX * 2, radiusY * 2);
g2d.setColor(Color.RED);
g2d.fillRect(
paddleTopLeftPixelX, paddleTopLeftPixelY,
paddleWidthPixels, paddleHeightPixels);
}
private int convertXToPixelX(double x)
{
return (int) (x * getWidth());
}
private int convertYToPixelY(double y)
{
return (int) (y * getHeight());
}
private double convertPixelXToX(int pixelX)
{
return (double) pixelX / getWidth();
}
}
It seems like you don't assign any value to brickHeight and brickWidth in your code. This might be the problem.
What MByD said, although as these fields are package-local you could possibly be setting these elsewhere. Also, there is also a NPE problem here:
boolean bricks[][] = null;
brickVisible[x][y] = bricks[x][y] ;
I'm not sure if you added this in before or after you found things weren't working, but this is a sure-fire NullPointerException which will cause the rest of your paint code to not execute when thrown.
EDIT: I'm assuming you commented out the code that wasn't working, but this is the bit you want to make work.