I want to click on the screen and have the character move to that destination. Not instantly, but rather "walk" to the given coordinate. Currently I'm using JLabels and they're fine if I only use static images, but everytime I click somewhere on the screen the image shows up at that exact point. Could someone give me some tips?
edit: Should I override the paint class and draw some items that way?
Here's some code:
package mod;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.awt.KeyboardFocusManager;
import javax.imageio.ImageIO;
import javax.swing.*;
public class Board2 extends JPanel {
private Thread animator;
int x, y;
double ix, iy;
double dx, dy;
final int frameCount = 8;
BufferedImage flower;
private int[][] fPos = {{232, 15},{400, 200},{335, 335}}; // flower coordinates
private static int bWIDTH = 800; // width of window
private static int bHEIGHT = 600;// height of window
private Font font;
private FontMetrics metrics;
ImageIcon grassI = new ImageIcon(this.getClass().getResource("grass.png"));
ImageIcon riverI = new ImageIcon(this.getClass().getResource("river.png"));
private Image grass = grassI.getImage();
private Image river = riverI.getImage();
private House house = new House();
private River river1 = new River();
//private Flower flower = new Flower();
private TitleScreenLayer ts = new TitleScreenLayer();
private Player girlP = new Player();
private static int px = 250;
private static int py = 250;
private boolean visTl = false;
private boolean plant = false;
ArrayList<Flower> flowers= new ArrayList<Flower>();
private long period;
private volatile boolean running = false;
private volatile boolean gameOver = false;
private volatile boolean isPaused = false;
// New stuff for Board2 below
private JLayeredPane lpane;
private JLabel grassLabel;
private JLabel riverLabel;
private JLabel houseLabel;
private JLabel pear1Label;
private JLabel pear2Label;
private JLabel pear3Label;
private JLabel drivewayLabel;
private JLabel girlLabel;
private JProgressBar progressBar;
private JLabel toolLabel;
private JTextArea textBubble;
ImageIcon girlImage = new ImageIcon(girlP.getImage());
int mouseClicks = 0;
CountdownTimer cTimer;
private static String message;
public static String setMessage(String newMessage){
return message = newMessage;
}
private static ImageIcon playerTool = new ImageIcon("BradfordPear.png");
public ImageIcon getPlayerTool(){
return playerTool;
}
public static void setPlayerTool(String image){
playerTool = new ImageIcon(image);
}
public JTextArea getTextBubble(){
return textBubble;
}
public Player getPlayer(){
return girlP;
}
public static int getPlayerX(){
return px;
}
public static int getPlayerY(){
return py;
}
public JLayeredPane getLayeredPane(){
return lpane;
}
public Board2(){
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
//create the layered pane
lpane = new JLayeredPane();
lpane.setPreferredSize(new Dimension(800, 600));
//create the "background" image
ImageIcon image = new ImageIcon("grass.png");
grassLabel = new JLabel(image);
grassLabel.setBounds(0, 0, image.getIconWidth(), image.getIconHeight());
//create the house image
ImageIcon houseImage = new ImageIcon("house.png");
houseLabel = new JLabel(houseImage);
houseLabel.setBounds(-330, -150, image.getIconWidth(), image.getIconHeight());
//create the driveway image
ImageIcon drivewayImage = new ImageIcon("driveway.png");
drivewayLabel = new JLabel(drivewayImage);
drivewayLabel.setBounds(-335, 105, image.getIconWidth(), image.getIconHeight());
//create the river image
ImageIcon riverImage = new ImageIcon("river.png");
riverLabel = new JLabel(riverImage);
riverLabel.setBounds(360, 0, image.getIconWidth(), image.getIconHeight());
//create pear1 image
ImageIcon pear1Image = new ImageIcon("BradfordPear.png");
pear1Label = new JLabel(pear1Image);
pear1Label.setBounds(100, 100, image.getIconWidth(), image.getIconHeight());
//create pear2 image
ImageIcon pear2Image = new ImageIcon("BradfordPear.png");
pear2Label = new JLabel(pear2Image);
pear2Label.setBounds(50, -100, image.getIconWidth(), image.getIconHeight());
//create pear3 image
ImageIcon pear3Image = new ImageIcon("BradfordPear.png");
pear3Label = new JLabel(pear3Image);
pear3Label.setBounds(-100, -50, image.getIconWidth(), image.getIconHeight());
//create initial Player(girl) image
//ImageIcon girlImage = new ImageIcon(girlP.getImage());
girlLabel = new JLabel(girlImage);
girlLabel.setBounds((int)girlP.getPositionX(), (int)girlP.getPositionY(), image.getIconWidth(), image.getIconHeight());
//create progress bar
progressBar = new JProgressBar(JProgressBar.VERTICAL, 0, 10);
progressBar.setValue(0);
progressBar.setBounds(720, 50, 100, 500);
//create timer
JTextField timerField = new JTextField();
cTimer = new CountdownTimer(timerField);
timerField.setBounds(400, 0, 50, 50);
//create toolbox
Toolbox toolbox = new Toolbox();
toolbox.setBounds(550, 0, 250, 50);
//create the text bubble
textBubble = new JTextArea("IDPC is the best coding group ever");
textBubble.setLineWrap(true);
//textBubble.setBounds(200, 200, 100, 100);
//add the background & various images
lpane.add(grassLabel, new Integer(1));
lpane.add(houseLabel, new Integer(2));
lpane.add(riverLabel, new Integer(2));
lpane.add(drivewayLabel, new Integer(2));
lpane.add(pear1Label, new Integer(2));
lpane.add(pear2Label, new Integer(2));
lpane.add(pear3Label, new Integer(2));
lpane.add(progressBar, new Integer(3));
lpane.add(girlLabel, new Integer(3));
lpane.add(timerField, new Integer(2));
lpane.add(toolbox, new Integer(3));
add(lpane);
cTimer.start();
// listen for action events
new ActionListener() {
public void actionPerformed(ActionEvent e) {
girlP.move();
//girlLabel.setLocation(px, py);
}
};
// listen for mouse presses
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
//lpane.remove(textBubble);
mouseClicks+= 1;
testPress(e.getX(), e.getY());
//textBubble.setBounds(e.getX(), e.getY(), 40, 40);
updateProgressBar();
}
});
//listen for player action
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 2){
ImageIcon flowerImage = playerTool;
JLabel flowerPanel = new JLabel(flowerImage);
flowerPanel.setBounds((px -((int)girlP.getPositionX() / 2)),
(py - ((int)girlP.getPositionY() / 2)),
flowerImage.getIconWidth(),
flowerImage.getIconHeight());
lpane.add(flowerPanel, new Integer(3));
textBubble.setBounds(e.getX(), e.getY(), 200, 40);
textBubble.replaceSelection(message);
lpane.add(textBubble, new Integer(3));
//lpane.remove(textBubble);
}
}
});
x = 15;
y = 150;
ix = 0;
iy = 0;
dx = .05;
dy = .05;
girlP.setDestination(px, py);
}
public void testPress(int x, int y){
px = x;
py = y;
if (px < (ix + house.getImage().getWidth(this))
&& (py < (iy + house.getImage().getHeight(this)))) {
px = px + (house.getImage().getWidth(this)/3);
py = py + (house.getImage().getHeight(this)/3);
}
if (px > (bWIDTH - river1.getImage().getWidth(this))) {
px = px - 80 - (river1.getImage().getWidth(this)/2);
}
girlLabel.setBounds((px -((int)(girlP.getPositionX()*2.5))),
(py - ((int)(girlP.getPositionY()*2.5))),
girlImage.getIconWidth(), girlImage.getIconHeight());
girlP.setDestination((px-(girlP.getImage().getWidth(this)/2)),
(py-(girlP.getImage().getHeight(this)/2)));
girlP.pinned(x, y);
}
public void updateProgressBar(){
if(progressBar.getValue() == 3){
//progressBar.setBackground(Color.red);
//UIManager.put("progressBar.foreground", Color.RED);
UIDefaults defaults = new UIDefaults();
defaults.put("progressBar[Enabled].foregroundPainter", Color.RED);
progressBar.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
progressBar.putClientProperty("Nimbus.Overrides", defaults);
}
progressBar.setValue(mouseClicks);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new TitleScreenLayer();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Here's the player class:
package mod;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.ImageObserver;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class Player {
int tile;
double positionX;
double positionY;
int destinationX;//Used when moving from place to place
int destinationY;
Tool currentTool;
int direction; //Position the image is facing
double dx;
double dy;
int [] pin = new int[10];
private String girl = "girl.png";
ImageIcon ii = new ImageIcon(this.getClass().getResource(girl)); // load girl image
private Image image = ii.getImage();
private boolean visible = true;
public boolean plant = false;
Image playerImage;
public double getPositionX() {
return positionX;
}
public void setPositionX(double positionX) {
this.positionX = positionX;
}
public double getPositionY() {
return positionY;
}
public void setPositionY(double positionY) {
this.positionY = positionY;
}
public Player(){
positionX=30;
positionY=20;
dx = 0.2;
dy = 0.2;
destinationX=(int)positionX;
destinationY=(int)positionY;
//this.playerImage=playerImage;
}
public void doAction() {
//currentTool.getNum();
plant = true;
}
public void pinned(int x, int y) {
if (plant == true) {
pin[0] = x;
pin[1] = y;
}
//plant = false;
}
public void plant(Graphics g, ImageObserver io) {
int x = pin[0];
int y = pin[1];
if (plant == true) {
// g.drawImage(flower.getImage(), x, y, io);
}
}
public void ActionPerformed(ActionEvent e) {
positionX += dx;
positionY += dy;
}
public boolean isVisible() {
return visible;
}
public void setVisible(Boolean visible) {
this.visible = visible;
}
public Image getImage() {
return image;
}
public void move(){
//MOVE LEFT AND RIGHT
if(destinationX<positionX){
positionX-=dx;
}
if(destinationX>positionX){
positionX+=dx;
}
//MOVE UP AND DOWN
if(destinationY<positionY){
positionY-=dy;
}
if(destinationY>positionY){
positionY+=dy;
}
}
public double setDx(double speed) {
dx = speed;
return dx;
}
public double setDy(double speed) {
dy = speed;
return dy;
}
public void TileIn(int px, int py)
{
px=destinationX;
py=destinationY;
int tileX=1;
int tileY = 1;
int bWIDTH=800;
int bHEIGHT=600;
if(px >= 0 && px <= 800*.1)
{
tileX=2;
}
else if(px> bWIDTH*.1 && px <= bWIDTH*.2)
{
tileX=3;
}
else if(px > bWIDTH*.2 && px <= bWIDTH*.3)
{
tileX=4;
}
else if(px > bWIDTH*.3 && px <= bWIDTH*.4)
{
tileX=5;
}
else if(px > bWIDTH*.4 && px <= bWIDTH*.5)
{
tileX=6;
}
else if(px > bWIDTH*.5 && px <= bWIDTH*.6)
{
tileX=7;
}
else if(px > bWIDTH*.6 && px <= bWIDTH*.7)
{
tileX=8;
}
else if(px > bWIDTH*.7 && px <= bWIDTH*.8)
{
tileX=9;
}
else if(px > bWIDTH*.8 && px <= bWIDTH*.9)
{
tileX=10;
}
else if(px > bWIDTH*.9 && px <= bWIDTH)
{
tileX=11;
}
if(py >= 0 && py <= bHEIGHT*.1)
{
tileY=2;
}
else if(py> bHEIGHT*.1 && py <= bHEIGHT*.2)
{
tileY=3;
}
else if(py > bHEIGHT*.2 && py <= bHEIGHT*.3)
{
tileY=4;
}
else if(py > bHEIGHT*.3 && py <= bHEIGHT*.4)
{
tileY=5;
}
else if(py > bHEIGHT*.4 && py <= bHEIGHT*.5)
{
tileY=6;
}
else if(py > bHEIGHT*.5 && py <= bHEIGHT*.6)
{
tileY=7;
}
else if(py > bHEIGHT*.6 && py <= bHEIGHT*.7)
{
tileY=8;
}
else if(py > bHEIGHT*.7 && py <= bHEIGHT*.8)
{
tileY=9;
}
else if(py > bHEIGHT*.8 && py <= bHEIGHT*.9)
{
tileY=10;
}
else if(py > bHEIGHT*.9 && py <= bHEIGHT)
{
tileY=11;
}
System.out.println("Grid X: " + tileX + " Grid Y: " + tileY);
}
public void setDestination(int x, int y){
destinationX=x;
destinationY=y;
System.out.println(x + "," + y);
TileIn(x,y);
}
// public void tileIn(int a)
// {
//
// b=destinationY;
// return TileIn(x,y)
// }
public void draw(Graphics g,ImageObserver io){
g.drawImage(image, (int)positionX,(int) positionY,io);
}
}
Here is the main class:
package mod;
import java.awt.Container;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
public class Skeleton2 extends JFrame /*implements WindowListener*/{
private static int DEFAULT_FPS = 80;
private Board2 bd;
public Skeleton2(long period) {
super("Skeleton");
makeGUI(period);
//addWindowListener(this);
pack();
setResizable(false);
setVisible(true);
}
public void makeGUI(long period) {
Container c = getContentPane();
bd = new Board2();
c.add(bd, "Center");
} // end of makeGUI()
//==================================================================================
// Window Events
//==================================================================================
/*
public void windowActivated(WindowEvent e) {
bd.resumeGame();
}
public void windowDeactivated(WindowEvent e) {
bd.pauseGame();
}
public void windowDeiconified(WindowEvent e) {
bd.resumeGame();
}
public void windowIconified(WindowEvent e) {
bd.pauseGame();
}
public void windowClosing(WindowEvent e) {
bd.stopGame();
}
*/
public void windowClosed(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
//==================================================================================
public static void main(String[] args) {
int fps = DEFAULT_FPS;
long period = (long) 1000.0/fps;
new Skeleton2(period);
System.out.println("Period: " + period);
}
}
Not instantly, but rather "walk" to the given coordinate.
Then you need to use a Swing Timer. The Timer is used to schedule the animation. So you would need to calculate a path between the two points. Every time the Timer fires you would move the label a few pixels until it reaches it's destination.
There is no need to do custom painting for this. A JLabel will work fine. The hard part is calculating the path you want the character to take. Also make sure you use a null layout on the panel you add the JLabel to, which means you will also need to set the size of the label equal to the preferred size of the label.
You should rather use g.drawImage in your component paintComponent method.
Related
The game runs at about 2000 to 3100 fps in normal window mode. If i set the JFrame component to fullscreen and scale up my JPanel to also the same resolution, the fps drops to 20-70.
(This is a prototype, hardcoded resolutions will be later swapped out)
This is my relevant code (if this is not enough, I can provide more):
Game.java
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame window = new JFrame("Platformer Test");
window.setContentPane(new GamePanel(window));
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(true);
//window.setUndecorated(true);
window.pack();
window.setVisible(true);
}
}
GamePanel.java
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
// custom imports
import GameState.GameStateManager;
#SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, KeyListener{
// dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// Graphic Device (used for fullscreen)
static GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
private JFrame frame;
// game Thread
private Thread thread;
private boolean running;
private double GameTicks = 60;
// image
private BufferedImage image;
private Graphics2D g;
boolean renderFPS = false;
int frames = 0;
// game state manager
private GameStateManager gsm;
public GamePanel(JFrame frame) {
super();
this.frame = frame;
// set Window Size
setFocusable(true);
setFullscreen(true);
}
private void setFullscreen(boolean t) {
if(t) {
setPreferredSize(new Dimension(1920, 1080));
device.setFullScreenWindow(frame);
requestFocus();
}else {
setSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
requestFocus();
}
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
// create image --> Game is drawn on here
image = new BufferedImage(
WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB
);
// get graphics component of game image
g = (Graphics2D) image.getGraphics();
// starts game clock
running = true;
// adds new GameStateManager
gsm = new GameStateManager();
}
#Override
public void run() {
init();
//game loop setup
double ns = 1000000000 / GameTicks;
double delta = 0;
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
int ticks = 0;
// game loop
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
update();
ticks++;
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames + ", ticks: " + ticks);
renderFPS = true;
frames = 0;
ticks = 0;
}
}
}
private void update() {
gsm.update();
}
private void render() {
gsm.render(g);
int fps = 0;
// Draw To Screen
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
//g2.drawImage(image, 0, 0, 1920, 1080, null);
if(renderFPS) {
fps = frames;
}
g2.setColor(Color.red);
g2.drawString("FPS: " + fps, 100,100);
g2.dispose();
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
gsm.keyPressed(e.getKeyCode());
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
gsm.keyReleased(e.getKeyCode());
}
}
Swing is not thread safe, you shouldn't be updating the UI, or the state the UI relies on, from outside the context of the Event Dispatching Thread. This means you shouldn't be using Thread as your "game loop".
See Concurrency in Swing for more details and How to Use Swing Timers for the most common solution.
Don't use JPanel#getGraphics, this is not how painting in Swing is done. Instead, override paintComponent. See Painting in AWT and Swing
and Performing Custom Painting for more details.
Don't use KeyListener, seriously, it's just not worth all the hacking around to make it work. Instead, use the key bindings API
The following, simple, example runs at roughly 171 updates a second (it separates the "timer ticks" and "paint ticks", as in Swing, it's not really possible to know when something is actually rendered to the screen) in both windowed and full screen
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
JFrame frame = new JFrame();
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// device.setFullScreenWindow(frame);
}
});
}
public class GamePanel extends JPanel {
private Timer timer;
private int ticksPerSecond = 0;
private int paintsPerSecond = 0;
private int xDelta = 1;
private Rectangle boxy = new Rectangle(0, 0, 50, 50);
// Graphic Device (used for fullscreen)
public GamePanel() {
timer = new Timer(5, new ActionListener() {
private Instant lastTick;
private int ticks = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick == null) {
lastTick = Instant.now();
}
if (Duration.between(lastTick, Instant.now()).toMillis() >= 1000) {
ticksPerSecond = ticks;
lastTick = Instant.now();
ticks = 0;
}
ticks++;
boxy.x += xDelta;
if (boxy.x + boxy.width > getWidth()) {
boxy.x = getWidth() - boxy.width;
xDelta *= -1;
} else if (boxy.x < 0) {
boxy.x = 0;
xDelta *= -1;
}
boxy.y = (getHeight() - boxy.height) / 2;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
timer.stop();
super.removeNotify();
}
private Instant lastPaint;
private int paints = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lastPaint == null) {
lastPaint = Instant.now();
}
if (Duration.between(lastPaint, Instant.now()).toMillis() >= 1000) {
paintsPerSecond = paints;
lastPaint = Instant.now();
paints = 0;
}
paints++;
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString("Ticks p/s " + ticksPerSecond, 10, 10 + fm.getAscent());
g2d.drawString("Paints p/s " + paintsPerSecond, 10, 10 + fm.getAscent() + fm.getHeight());
g2d.fill(boxy);
g2d.dispose();
}
}
}
If you "really" need absolute control over the painting process, then you should be using a BufferStrategy, see BufferStrategy and BufferCapabilities and the JavaDocs which has an excellent example of how it should be used.
Lots of things might effect the performance of the paint process, for example
A crazy experiment...
So, this example allows you to change the number of entities been rendered on the screen. Each entity is moving in it's own direction and is rotating (no collision detection).
I can get this to run up to roughly 20-25, 0000 entities before I start seeing a (significant) change in the number of updates per second. I rolled it to 100, 000 and it dropped to roughly 42 updates per second.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
GamePanel gamePanel = new GamePanel();
JSlider slider = new JSlider(1, 100000);
slider.setValue(100);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (slider.getValueIsAdjusting()) {
return;
}
gamePanel.setBoxCount(slider.getValue());
}
});
JFrame frame = new JFrame();
frame.add(gamePanel);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
device.setFullScreenWindow(frame);
}
});
}
public class GamePanel extends JPanel {
private Timer timer;
private int ticksPerSecond = 0;
private int paintsPerSecond = 0;
private List<Box> boxes = new ArrayList<>(100);
// Graphic Device (used for fullscreen)
public GamePanel() {
for (int index = 0; index < 100; index++) {
boxes.add(new Box());
}
timer = new Timer(5, new ActionListener() {
private Instant lastTick;
private int ticks = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (lastTick == null) {
lastTick = Instant.now();
}
if (Duration.between(lastTick, Instant.now()).toMillis() >= 1000) {
ticksPerSecond = ticks;
lastTick = Instant.now();
ticks = 0;
}
ticks++;
for (Box box : boxes) {
box.update(getSize());
}
repaint();
}
});
}
public void setBoxCount(int count) {
if (count < boxes.size()) {
boxes = boxes.subList(0, count);
return;
}
while (boxes.size() < count) {
boxes.add(new Box());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
timer.stop();
super.removeNotify();
}
private Instant lastPaint;
private int paints = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lastPaint == null) {
lastPaint = Instant.now();
}
if (Duration.between(lastPaint, Instant.now()).toMillis() >= 1000) {
paintsPerSecond = paints;
lastPaint = Instant.now();
paints = 0;
}
paints++;
Graphics2D g2d = (Graphics2D) g.create();
for (Box box : boxes) {
box.paint(g2d);
}
FontMetrics fm = g2d.getFontMetrics();
int yPos = 10 + fm.getAscent();
g2d.drawString("Ticks p/s " + ticksPerSecond, 10, yPos + fm.getAscent());
yPos += fm.getHeight();
g2d.drawString("Paints p/s " + paintsPerSecond, 10, yPos + fm.getAscent());
yPos += fm.getHeight();
g2d.drawString("Count " + boxes.size(), 10, yPos + fm.getAscent());
g2d.dispose();
}
}
private List<Color> colors = new ArrayList<>(Arrays.asList(new Color[]{
Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN,
Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE,
Color.YELLOW
}));
public class Box {
private Color fill;
private Rectangle shape;
private int x;
private int y;
private int xDelta;
private int yDelta;
private int rotationDelta;
private int angle = 0;
public Box() {
Random rnd = new Random();
int width = rnd.nextInt(45) + 5;
int height = rnd.nextInt(45) + 5;
x = rnd.nextInt(400) - width;
y = rnd.nextInt(400) - height;
xDelta = rnd.nextInt(2) + 1;
yDelta = rnd.nextInt(2) + 1;
if (rnd.nextBoolean()) {
xDelta *= -1;
}
if (rnd.nextBoolean()) {
yDelta *= -1;
}
rotationDelta = rnd.nextInt(5);
if (rnd.nextBoolean()) {
rotationDelta *= -1;
}
shape = new Rectangle(x, y, width, height);
Collections.shuffle(colors);
fill = colors.get(0);
shape = new Rectangle(0, 0, width, height);
}
public void update(Dimension bounds) {
x += xDelta;
y += yDelta;
angle += rotationDelta;
if (x + getWidth() > bounds.width) {
x = bounds.width - getWidth();
xDelta *= -1;
} else if (x < 0) {
x = 0;
xDelta *= -1;
}
if (y + getWidth() > bounds.height) {
y = bounds.height - getWidth();
yDelta *= -1;
} else if (y < 0) {
y = 0;
yDelta *= -1;
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return shape.width;
}
public int getHeight() {
return shape.height;
}
public void paint(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
g2d.translate(x, y);
g2d.rotate(Math.toRadians(angle), getWidth() / 2, getHeight() / 2);
g2d.setColor(fill);
g2d.fill(shape);
g2d.dispose();
}
}
}
So I'm new at java and need some help with my breakout game. My JFrame is just blank and i don't know how to fix it?
So I have a ball class, paddle class, canvas class and a brick class as well as a main class. In my canvas class I set all functions the ball, paddle and bricks has etc. In brick class I draw the bricks. And in my main I do the JFrame but it's blank
Main class :
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame();
Canvas c = new Canvas();
frame.add(c);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I expect the JFrame to show the game instead of just blank window
package breakout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.KeyEvent;
import breakout.Bricks.Type;
public class Canvas extends JPanel implements ActionListener, MouseMotionListener, MouseListener, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int HEIGHT = 600;
public static final int WIDTH = 720;
private int horizontalCount;
private BufferedImage image;
private Graphics2D bufferedGraphics;
private Timer time;
private static final Font endFont = new Font(Font.SANS_SERIF, Font.BOLD, 20);
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private Paddle player;
private Ball ball;
ArrayList<ArrayList<Bricks>> bricks;
public Canvas() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
bufferedGraphics = image.createGraphics();
time = new Timer (15, this);
player = new Paddle((WIDTH/2)-(Paddle.PADDLE_WIDTH/2));
ball = new Ball (((player.getX() + (Paddle.PADDLE_WIDTH / 2 )) - (Ball.DIAMETER / 2)), (Paddle.Y_POS - (Ball.DIAMETER + 10 )), -5, -5);
bricks = new ArrayList<ArrayList<Bricks>>();
horizontalCount = WIDTH / Bricks.BRICK_WIDTH;
for(int i = 0; i < 8; ++i) {
ArrayList<Bricks> temp = new ArrayList<Bricks>();
#SuppressWarnings("unused")
Type rowColor = null;
switch(i) {
case 0 :
case 2:
rowColor = Type.LOW;
break;
case 1 :
case 3 :
case 5 :
rowColor = Type.MEDIUM;
break;
case 4 :
case 6 :
rowColor = Type.HIGH;
break;
case 7 :
default :
rowColor = Type.ULTRA;
break;
}
for(int j = 0; j < horizontalCount; ++j) {
Bricks tempBrick = new Bricks();
temp.add(tempBrick);
}
bricks.add(temp);
addMouseMotionListener(this);
addMouseListener(this);
addKeyListener(this);
requestFocus();
}
}
public void actionPerformed(ActionEvent e) {
checkCollisions();
ball.Move();
for(int i = 0; i < bricks.size(); ++i) {
ArrayList<Bricks> al = bricks.get(i);
for(int j = 0; j < al.size(); ++j) {
Bricks b = al.get(j);
if(b.dead()) {
al.remove(b);
}
}
}
repaint();
}
private void checkCollisions() {
if(player.hitPaddle(ball)) {
ball.setDY(ball.getDY() * -1);
return;
}
if(ball.getX() >= (WIDTH - Ball.DIAMETER) || ball.getX() <= 0) {
ball.setDX(ball.getDX() * -1);
}
if(ball.getY() > (Paddle.Y_POS + Paddle.PADDLE_HEIGHT + 10)) {
resetBall();
}
if(ball.getY() <= 0) {
ball.setDY(ball.getDY() * -1);
}
int brickRowActive = 0;
for(ArrayList<Bricks> alb : bricks) {
if(alb.size() == horizontalCount) {
++brickRowActive;
}
}
for(int i = (brickRowActive==0) ? 0 : (brickRowActive - 1); i < bricks.size(); ++i) {
for(Bricks b : bricks.get(i)) {
if(b.hitBy(ball)) {
player.setScore(player.getScore() + b.getBrickType().getPoints());
b.decrementType();
}
}
}
}
private void resetBall() {
if(gameOver()) {
time.stop();
return;
}
ball.setX(WIDTH/2);
ball.setDY((HEIGHT/2) + 80);
player.setLives(player.getLives() -1);
player.setScore(player.getScore() <= 1);
}
private boolean gameOver() {
if(player.getLives() <= 1) {
return true;
}
return false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
bufferedGraphics.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(bufferedGraphics);
player.drawBall(bufferedGraphics);
for(ArrayList<Bricks> row : bricks) {
for(Bricks b : row) {
b.drawBrick(bufferedGraphics);
}
}
bufferedGraphics.setFont(scoreFont);
bufferedGraphics.drawString("Score: " + player.getScore(), 10, 25);
if(gameOver() && ball.getY() >= HEIGHT) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("Game Over Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT/2));
}
if(empty()) {
bufferedGraphics.setColor(Color.black);
bufferedGraphics.setFont(endFont);
bufferedGraphics.drawString("You won. Score: " + player.getScore(), (WIDTH /2) -85, (HEIGHT /2));
time.stop();
}
g.drawImage(image, 0, 0, this);
Toolkit.getDefaultToolkit().sync();
}
private boolean empty() {
for(ArrayList<Bricks> al : bricks) {
if(al.size() != 0) {
return false;
}
}
return true;
}
#Override
public void mouseMoved(MouseEvent e) {
player.setX(e.getX() - (Paddle.PADDLE_WIDTH / 2));
}
#Override
public void mouseClicked(MouseEvent e) {
if(time.isRunning()) {
return;
}
time.start();
}
#Override
public void mouseDragged(MouseEvent e) { }
#Override
public void mouseEntered(MouseEvent arg0) {}
#Override
public void mouseExited(MouseEvent arg0) {}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
#Override
public void keyPressed(KeyEvent arg0) {}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
}
Preparing an MCVE, as required in SO, not only it makes helping much easier.
In many case, while preparing one, you are likely to find the problem, so it is a good debugging tool.
To answer "why is my JFrame blank ?" you could create the minimal code example like the following (copy-paste the entire code into GameBoard.java and run):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameBoard extends JPanel {
static final int HEIGHT = 600, WIDTH = 720, BRICK_ROWS = 8;
private final int horizontalCount;
private static final Font scoreFont = new Font(Font.SANS_SERIF, Font.BOLD, 15);
private final Paddle player;
private final Ball ball;
ArrayList<ArrayList<Brick>> bricks;
public GameBoard() {
super();
setPreferredSize( new Dimension(WIDTH, HEIGHT));
player = new Paddle(WIDTH/2-Paddle.PADDLE_WIDTH/2);
ball = new Ball (player.getX() + Paddle.PADDLE_WIDTH / 2 - Ball.DIAMETER / 2,
Paddle.Y_POS - (Ball.DIAMETER + 10 ));
bricks = new ArrayList<>();
horizontalCount = WIDTH / Brick.BRICK_WIDTH;
for(int i = 0; i < BRICK_ROWS; ++i) {
ArrayList<Brick> temp = new ArrayList<>();
for(int j = 0; j < horizontalCount; ++j) {
Brick tempBrick = new Brick(j*Brick.BRICK_WIDTH , Brick.BRICK_YPOS + i*Brick.BRICK_HEIGHT);
temp.add(tempBrick);
}
bricks.add(temp);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.clearRect(0, 0, WIDTH, HEIGHT);
player.drawPaddle(g2D);
ball.drawBall(g2D);
for(ArrayList<Brick> row : bricks) {
for(Brick b : row) {
b.drawBrick(g2D);
}
}
g2D.setFont(scoreFont);
g2D.drawString("Score: " + player.getScore(), 10, 25);
}
}
class Paddle{
public final static int PADDLE_WIDTH = 100, PADDLE_HEIGHT= 30, Y_POS = GameBoard.HEIGHT - 2* PADDLE_HEIGHT;
private int xPos, score;
Paddle(int xPos) {
this.xPos = xPos;
}
void setX(int xPos) {this.xPos = xPos;}
int getX() {return xPos;}
String getScore() {
return String.valueOf(score);
}
void drawPaddle(Graphics2D g2D) {
g2D.setColor(Color.GREEN);
g2D.fillRect(xPos, Y_POS, PADDLE_WIDTH, PADDLE_HEIGHT);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(400,250);
frame.add(new GameBoard());
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
class Brick{
final static int BRICK_WIDTH = 80, BRICK_HEIGHT = 15, BRICK_YPOS = 50;
int xPos, yPos;
Brick(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBrick(Graphics2D g2D) {
g2D.setColor(Color.RED);
g2D.fillRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
g2D.setColor(Color.BLACK);
g2D.drawRect(xPos, yPos, BRICK_WIDTH, BRICK_HEIGHT);
}
}
class Ball{
final static int DIAMETER = 40;
int xPos, yPos;
Ball(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
void drawBall(Graphics2D g2D) {
g2D.setColor(Color.BLUE);
g2D.fillOval(xPos, yPos, DIAMETER, DIAMETER);
}
}
This produces the following result, which I believe can serve as the basis of what you wanted to achieve:
Now start adding the missing functionality and see what breaks it.
I want to know how to manage the rgb values to change the background colour of a JFrame dynamically, by now i can only change from green to blue and versa vice; this are the colours that i need:
The rgb of these colours can be found here
Here's my sample code of how to change from green to blue dynamically:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ChangeColor {
public ChangeColor() {
JFrame frame = new JFrame();
frame.add(new ColorPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class ColorPanel extends JPanel {
private static final int DELAY = 30;
private static final int INCREMENT = 15;
private Color currentColor = Color.BLUE;
boolean isBlue = true;
boolean isGreen = false;
private int r,g,b;
private Timer timer = null;
private JButton greenButton = null;
private JButton blueButton = null;
public ColorPanel() {
r = 0; g = 0; b = 255;
greenButton = createGreenButton();
blueButton = createBlueButton();
timer = new Timer(DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isBlue) {
if (b == 0) {
stopTimer();
enableButtons();
} else {
blueToGreen();
setColor(new Color(r, b, g));
}
}
if (isGreen) {
if (g == 0) {
stopTimer();
enableButtons();
} else {
greenToBlue();
setColor(new Color(r, b, g));
}
}
repaint();
}
});
add(blueButton);
add(greenButton);
}
public JButton createBlueButton() {
JButton button = new JButton("BLUE");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if (currentColor != new Color(0, 255, 0)) {
System.out.println("turn blue");
isBlue = true;
isGreen = false;
diableButtons();
startTimer();
}
}
});
return button;
}
public void diableButtons() {
blueButton.setEnabled(false);
greenButton.setEnabled(false);
}
public void enableButtons() {
blueButton.setEnabled(true);
greenButton.setEnabled(true);
}
public JButton createGreenButton() {
JButton button = new JButton("GREEN");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if (currentColor != new Color(0, 0, 255)) {
System.out.println("turn green");
isGreen = true;
isBlue = false;
diableButtons();
startTimer();
}
}
});
return button;
}
private void blueToGreen() {
b -= INCREMENT;
g += INCREMENT;
}
private void greenToBlue() {
g -= INCREMENT;
b += INCREMENT;
}
public void setColor(Color color) {
this.currentColor = color;
}
public void startTimer() {
timer.start();
}
public void stopTimer() {
timer.stop();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(currentColor);
g.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new ChangeColor();
}
});
}
}
The code has some bugs, and it's not the most robust code, so that if you can improve it, i'll be thanked
Here's the output of the sample code:
Your current approach is indeed not easily generalizable. The hard-coded parts of the buttons for "blue" and "green", and especially the special methods like blueToGreen make it impossible to extend the number of colors with reasonable effort. (You don't want to create methods blueToYellow, blueToRed, blueToTheThirdColorFromThisListForWhichIDontKnowAName ...).
There are many possible ways of generalizing this. You did not say much about the intended structure and responsibilities. But you should at least create a method that can interpolate between two arbitrary colors with a given number of steps. In the code snippet below, this is done in the ´createColorsArrayArgb` method, which creates an array of ARGB colors from an arbitrary sequence of colors (I needed this recently). But you can probably boil it down to 2 colors, if you want to.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ChangeColor
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new ChangeColor();
}
});
}
public ChangeColor()
{
JFrame frame = new JFrame();
ColorPanel colorPanel = new ColorPanel(Color.BLUE);
ColorInterpolator ci = new ColorInterpolator(colorPanel, Color.BLUE);
colorPanel.addColorButton(createButton("Blue", Color.BLUE, ci));
colorPanel.addColorButton(createButton("Green", Color.GREEN, ci));
colorPanel.addColorButton(createButton("Red", Color.RED, ci));
colorPanel.addColorButton(createButton("Cyan", Color.CYAN, ci));
colorPanel.addColorButton(createButton("Yellow", Color.YELLOW, ci));
colorPanel.addColorButton(createButton("Magenta", Color.MAGENTA, ci));
frame.add(colorPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static JButton createButton(String name, final Color color,
final ColorInterpolator colorInterpolator)
{
JButton button = new JButton(name);
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
colorInterpolator.interpolateTo(color);
}
});
return button;
}
/**
* Creates an array with the given number of elements, that contains the
* ARGB representations of colors that are linearly interpolated between the
* given colors
*
* #param steps The number of steps (the size of the resulting array)
* #param colors The colors to interpolate between
* #return The array with ARGB colors
*/
static int[] createColorsArrayArgb(int steps, Color... colors)
{
int result[] = new int[steps];
double normalizing = 1.0 / (steps - 1);
int numSegments = colors.length - 1;
double segmentSize = 1.0 / (colors.length - 1);
for (int i = 0; i < steps; i++)
{
double relative = i * normalizing;
int i0 = Math.min(numSegments, (int) (relative * numSegments));
int i1 = Math.min(numSegments, i0 + 1);
double local = (relative - i0 * segmentSize) * numSegments;
Color c0 = colors[i0];
int r0 = c0.getRed();
int g0 = c0.getGreen();
int b0 = c0.getBlue();
Color c1 = colors[i1];
int r1 = c1.getRed();
int g1 = c1.getGreen();
int b1 = c1.getBlue();
int dr = r1 - r0;
int dg = g1 - g0;
int db = b1 - b0;
int r = (int) (r0 + local * dr);
int g = (int) (g0 + local * dg);
int b = (int) (b0 + local * db);
int argb = (0xFF << 24) | (r << 16) | (g << 8) | (b << 0);
result[i] = argb;
}
return result;
}
}
class ColorInterpolator
{
private static final int DELAY = 20;
private Color currentColor;
private int currentIndex = 0;
private int currentColorsArgb[];
private final Timer timer;
private final ColorPanel colorPanel;
ColorInterpolator(final ColorPanel colorPanel, Color initialColor)
{
this.colorPanel = colorPanel;
currentColor = initialColor;
currentColorsArgb = new int[]{ initialColor.getRGB() };
timer = new Timer(DELAY, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
currentIndex++;
if (currentIndex >= currentColorsArgb.length-1)
{
timer.stop();
colorPanel.enableButtons();
}
else
{
int argb = currentColorsArgb[currentIndex];
currentColor = new Color(argb);
colorPanel.setColor(currentColor);
}
}
});
}
void interpolateTo(Color targetColor)
{
colorPanel.diableButtons();
currentColorsArgb = ChangeColor.createColorsArrayArgb(
40, currentColor, targetColor);
currentIndex = 0;
timer.start();
}
}
class ColorPanel extends JPanel
{
private Color currentColor;
private List<JButton> buttons;
public ColorPanel(Color initialColor)
{
currentColor = initialColor;
buttons = new ArrayList<JButton>();
}
void addColorButton(JButton button)
{
buttons.add(button);
add(button);
}
public void diableButtons()
{
for (JButton button : buttons)
{
button.setEnabled(false);
}
}
public void enableButtons()
{
for (JButton button : buttons)
{
button.setEnabled(true);
}
}
public void setColor(Color color)
{
currentColor = color;
repaint();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(currentColor);
g.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(600, 300);
}
}
For simplicity, you might look at doing the fade calculation in HSB color coordinates. The example below interpolates between the two hues, green and blue, but you can pass saturation and brightness, too. The color lookup table, clut, is precomputed and used in the timer's listener. Some related examples are seen here.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
/**
* #see https://stackoverflow.com/a/24718561/230513
*/
public class HSBTest {
private static final float greenHue = 2 / 6f;
private static final float blueHue = 4 / 6f;
private void display() {
JFrame f = new JFrame("HSBTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Fader fader = new Fader(greenHue, blueHue);
f.add(fader);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
fader.start();
}
private static class Fader extends JPanel implements ActionListener {
private final float N = 32;
private final Queue<Color> clut = new LinkedList<Color>();
Timer t = new Timer(50, this);
public Fader(float h1, float h2) {
float d = (h2 - h1) / N;
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(h1 + d * i, 1, 1));
}
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(h2 - d * i, 1, 1));
}
}
private void start() {
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
this.setBackground(clut.peek());
clut.add(clut.remove());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(256, 128);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new HSBTest().display();
}
});
}
}
public void actionPerformed(ActionEvent e) {
List<Color> colors = new ArrayList<Color>();
colors.add(new Color(0,0,255);
colors.add(new Color(255,0,0);
colors.add(new Color(0,255,0);
// add your other colors
for(Color c : colors){
setColor(c)
Thread.sleep(1000); //wait 1 second to change the color
repaint();
}
}
I'm trying to make Boy1 move but in the second class I'm getting redlines under setX. Anyone know what's wrong?
First Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class MyGame extends JPanel implements ActionListener, KeyListener {
Timer t = new Timer(5, this);
int x = 0, y = 0, velx =0, vely =0;
public MyGame() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x,y,50,30);
}
public void actionPerformed(ActionEvent e) {
if(x < 0)
{
velx=0;
x = 0;
}
if(x > 530)
{
velx=0;
x = 530;
}
if(y < 0)
{
vely=0;
y = 0;
}
if(y > 330)
{
vely=0;
y = 330;
}
x += velx;
y += vely;
repaint();
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_DOWN){
vely = 1;
velx = 0;
}
if (code == KeyEvent.VK_UP){
vely = -1;
velx = 0;
}
if (code == KeyEvent.VK_LEFT){
vely = 0;
velx = -1;
}
if (code == KeyEvent.VK_RIGHT){
vely = 0;
velx = 1;
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
velx=0;
vely=0;
}
public static void main (String arge[]){
JFrame f = new JFrame();
MyGame s = new MyGame();
f.add(s);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(600,400);
f.setVisible(true);
}
}
Problem is with this last class. Perhaps it needs to be KeyAdapter? But I tried that and seems like that totally doesn't work.
The compiler is flagging you for a reason -- you're trying to call methods on a variable as if it were a variable that the class contains when it isn't -- it's held by a different class. And this isn't Kosher in Java.
What you should do is give the class that holds the variable public methods that outside classes can call and that will allow outside classes to be able to move the label. Then give your control object (the listener) an instance of the class that has these methods.
As an aside, you're usually better off using Key Bindings and not using a KeyListener.
I can give you an example of code that uses Key Bindings to move a JLabel around a JPanel:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.EnumMap;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
public static final String DUKE_IMG_PATH = // https://duke.kenai.com/iconSized/duke.gif
"https://duke.kenai.com/iconSized/duke4.gif";
private static final int PREF_W = 800;
private static final int PREF_H = 800;
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
private static final String KEY_RELEASE = "key release";
public static final int TRANSLATE_SCALE = 3;
private static final String BACKGROUND_STRING = "Use Arrow Keys to Move Image";
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int imgX = 0;
private int imgY = 0;
private int bgStringX;
private int bgStringY;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
try {
URL imgUrl = new URL(DUKE_IMG_PATH);
image = ImageIO.read(imgUrl);
Icon icon = new ImageIcon(image);
JOptionPane.showMessageDialog(null, icon);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
new Timer(TIMER_DELAY, new TimerListener()).start();
// here we set up our key bindings
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
// for the key down key stroke
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0,
false);
inputMap.put(keyStroke, dir.name() + KEY_DOWN);
actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
// for the key release key stroke
keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyStroke, dir.name() + KEY_RELEASE);
actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, false);
}
});
}
FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT);
int w = fontMetrics.stringWidth(BACKGROUND_STRING);
int h = fontMetrics.getHeight();
bgStringX = (PREF_W - w) / 2;
bgStringY = (PREF_H - h) / 2;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString(BACKGROUND_STRING, bgStringX, bgStringY);
if (image != null) {
g.drawImage(image, imgX, imgY, this);
}
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
imgX += dir.getX() * TRANSLATE_SCALE;
imgY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
};
}
enum Direction {
Up(KeyEvent.VK_UP, 0, -1), Down(KeyEvent.VK_DOWN, 0, 1), Left(
KeyEvent.VK_LEFT, -1, 0), Right(KeyEvent.VK_RIGHT, 1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getKeyCode() {
return keyCode;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_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();
}
});
}
}
I am working on a Java project to simulate the flight of a helicopter in a frame. The helicopter moves on the screen using the arrow keys. I want the helicopter to be able to move infinitely, that is, when the helicopter reaches the edge of the frame, the background should move in the opposite direction to have the effect of endless terrain.
Here is the code I have so far:
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MainFrame extends JFrame
{
private static int FRAME_WIDTH = 800;
private static int FRAME_HEIGHT = 500;
public MainFrame()
{
add(new AnotherBackground(FRAME_WIDTH, FRAME_HEIGHT));
setTitle("Helicopter Background Test");
setSize(FRAME_WIDTH,FRAME_HEIGHT);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
class AnotherBackground extends JPanel
{
private BufferedImage heliImage = null;
private BufferedImage backImage = null;
private int heliX = 0;
private int heliY = 0;
private int backX = 0;
private int backY = 0;
private int frameWidth = 0;
private int frameHeight = 0;
private int backWidth = 0;
private int backHeight = 0;
public AnotherBackground(int fWidth, int fHeight)
{
frameWidth = fWidth;
frameHeight = fHeight;
this.setFocusable(true);
this.addKeyListener(new HeliListener());
try
{
heliImage = ImageIO.read(new URL("http://imageshack.us/a/img7/2133/helicopter2f.png"));
// 2.7 Meg Crap that is a humungous image! Substitute dummy.
backImage = new BufferedImage(1918,1200,BufferedImage.TYPE_INT_RGB);
}
catch(IOException ex)
{
System.out.println("Problem durinng loading heli image");
}
backWidth = backImage.getWidth();
backHeight = backImage.getHeight();
HeliPainter l = new HeliPainter();
new Thread(l).start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(backImage, backX, backY, null);
g.drawImage(heliImage, heliX, heliY, null);
}
class HeliListener extends KeyAdapter
{
#Override
public void keyPressed(KeyEvent e)
{
System.out.println(heliX + " " + heliY + " " + backX + " " + backY);
if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
if(heliX > 0)
{
heliX -= 5;
}
else
{
backX += 5;
}
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
{
if(heliX < frameWidth)
{
heliX += 5;
}
else
{
backX -= 5;
}
}
else if (e.getKeyCode() == KeyEvent.VK_UP)
{
if(heliY > 0)
{
heliY -= 5;
}
else
{
backY += 5;
}
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
if(heliY < frameHeight)
{
heliY += 5;
}
else
{
backY -= 5;
}
}
}
}
class HeliPainter implements Runnable
{
#Override
public void run()
{
try
{
while(true)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
repaint();
}
});
Thread.sleep(1);
}
}
catch(InterruptedException ex)
{
System.out.println("Problem putting thread to sleep");
}
}
}
}
Now there's two images in the code. One is that of a small helicopter, and the other is a large (2.7 meg) background. They are here:
background
helicopter http://imageshack.us/a/img7/2133/helicopter2f.png
How to show the BG continuously?
Have a look through this source which behaves in a more predictable manner, and also includes a nice tweak to the chopper image (animated). ;)
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MainFrame
{
public MainFrame()
{
JFrame f = new JFrame("Helicopter Background Test");
f.add(new AnotherBackground());
//setTitle("Helicopter Background Test"); Redundant..
// Set a preferred size for the content area and pack() the frame instead!
// setSize(FRAME_WIDTH,FRAME_HEIGHT);
// setLocationRelativeTo(null); Better to..
f.setLocationByPlatform(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack(); // Size the GUI - VERY MPORTANT!
f.setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
class AnotherBackground extends JPanel
{
private static int PREFERRED_WIDTH = 400;
private static int PREFERRED_HEIGHT = 200;
private BufferedImage heliImage = null;
private BufferedImage heliLeftImage = null;
private BufferedImage heliRightImage = null;
private BufferedImage backImage = null; //getFlippedImage(
private int heliX = 0;
private int heliY = 0;
private int backX = 0;
private int backY = 0;
private int frameWidth = 0;
private int frameHeight = 0;
private int backWidth = 0;
private int backHeight = 0;
public AnotherBackground()
{
frameWidth = PREFERRED_WIDTH;
frameHeight = PREFERRED_HEIGHT;
this.setFocusable(true);
this.addKeyListener(new HeliListener());
try
{
heliLeftImage = ImageIO.read(
new URL("http://imageshack.us/a/img7/2133/helicopter2f.png"));
heliRightImage = getFlippedImage(heliLeftImage);
heliImage = heliLeftImage;
// 2.7 Meg Crap that is an humungous image! Substitute dummy.
backImage = getTileImage(250);
//ImageIO.read(
// new URL("http://i.stack.imgur.com/T5uTa.png"));
backWidth = backImage.getWidth();
backHeight = backImage.getHeight();
//HeliPainter l = new HeliPainter(); // see mention of repaint()
//new Thread(l).start();
} catch(IOException ex) {
// THERE IS NO POINT CONTINUING AFTER THIS POINT!
// unless it is to pop an option pane error message..
System.err.println("Problem during loading heli image");
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int normalizeX = (heliRealX-heliX)%backImage.getWidth();
int normalizeY = (heliRealY-heliY)%backImage.getHeight();
int timesRepeatX = (getWidth()/backImage.getWidth())+2;
int timesRepeatY = (getHeight()/backImage.getHeight())+2;
for (int xx=-1; xx<timesRepeatX; xx++) {
for (int yy=-1; yy<timesRepeatY; yy++) {
g.drawImage(
backImage,
(xx*backImage.getWidth())-normalizeX,
(yy*backImage.getHeight())-normalizeY,
this); // A JPanel IS AN ImageObserver!
g.drawImage(heliImage, heliX, heliY, this);
}
}
g.setColor(Color.BLACK);
}
private int heliRealX = 0;
private int heliRealY = 0;
class HeliListener extends KeyAdapter
{
#Override
public void keyPressed(KeyEvent e)
{
int pad = 5;
if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
if(heliX > 0)
{
heliX -= 5;
}
else
{
backX += 5;
}
heliRealX-=5;
heliImage = heliLeftImage;
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
{
// correct for image size + padding
if(heliX+heliImage.getWidth()+pad < getWidth())
{
heliX += 5;
}
else
{
backX -= 5;
}
heliRealX+=5;
heliImage = heliRightImage;
}
else if (e.getKeyCode() == KeyEvent.VK_UP)
{
if(heliY > 0)
{
heliY -= 5;
}
else
{
backY += 5;
}
heliRealY-=5;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
// correct for image size + padding
if(heliY+heliImage.getHeight()+pad < getHeight())
{
heliY += 5;
}
else
{
backY -= 5;
}
heliRealY+=5;
}
repaint(); // Replaces need for threads for this simple demo!
}
}
public BufferedImage getFlippedImage(BufferedImage original) {
BufferedImage bi = new BufferedImage(
original.getWidth(),
original.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
AffineTransform at = AffineTransform.getTranslateInstance(bi.getWidth(),1d);
at.concatenate(AffineTransform.getScaleInstance(-1d,1d));
g.setTransform(at);
g.drawImage(original,0,0,this);
g.dispose();
return bi;
}
public BufferedImage getTileImage(int s) {
BufferedImage bi = new BufferedImage(s,s,BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
GradientPaint gp1 = new GradientPaint(
(float)0,(float)s/4, Color.YELLOW,
(float)s/4,0f, Color.GREEN,
true);
g.setPaint(gp1);
g.fillRect(0,0,s,s);
int trans = 165;
GradientPaint gp2 = new GradientPaint(
(float)s/2,(float)s/2, new Color(255,0,0,trans),
0f,(float)s/2, new Color(255,255,255,trans),
true);
g.setPaint(gp2);
g.fillRect(0,0,s,s);
g.dispose();
return bi;
}
}
This is a really simple example (you can only move in a single direction). The basic idea is that there is a prepareView method that is responsible for generating a view of the world based on the available viewable area. If the view is trying to view an area off the map, the map is titled to make up for it.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class InfiniteBackground {
public static void main(String[] args) {
new InfiniteBackground();
}
public InfiniteBackground() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static final int DELTA = 5;
private BufferedImage terrian;
private BufferedImage heli;
private Point pov;
private Point heliPoint;
private BufferedImage view;
public TestPane() {
pov = new Point();
heliPoint = new Point();
try {
terrian = ImageIO.read(getClass().getResource("/terrain_map.jpg"));
heli = ImageIO.read(getClass().getResource("/helicopter2f.png"));
pov.x = terrian.getWidth() - getPreferredSize().width;
pov.y = ((terrian.getHeight() - getPreferredSize().height) / 2);
heliPoint.x = getPreferredSize().width / 2;
heliPoint.y = getPreferredSize().height / 2;
prepareView();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "goLeft");
am.put("goLeft", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
heliPoint.x -= DELTA;
if (heliPoint.x - (heli.getWidth() / 2) < 0) {
heliPoint.x = (heli.getWidth() / 2);
prepareView();
pov.x -= DELTA;
}
repaint();
}
});
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
protected void prepareView() {
if (getWidth() > 0 && getHeight() > 0) {
view = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = view.createGraphics();
if (pov.x < 0) {
pov.x = terrian.getWidth();
}
g2d.drawImage(terrian, -pov.x, -pov.y, this);
if (pov.x + getWidth() > terrian.getWidth()) {
g2d.drawImage(terrian, -pov.x + terrian.getWidth(), -pov.y, this);
}
g2d.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (terrian != null) {
Graphics2D g2d = (Graphics2D) g.create();
if (view == null) {
prepareView();
}
g2d.drawImage(view, 0, 0, this);
g2d.drawImage(heli, heliPoint.x - (heli.getWidth() / 2), heliPoint.y - (heli.getHeight() / 2), this);
g2d.dispose();
}
}
}
}