I'm currently coding a game and I'm trying to have it where if you click on the first button on the front page, it shows a transition screen for 5 seconds, and then shows the game. Here is my code:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ForgottenMain extends JPanel implements KeyListener,MouseListener{
/**
*
*/
private static final int TIMER_DELAY = 35;
private static final long serialVersionUID = -4926251405849574401L;
public static BufferedImage attic,flashlight,player,killer,frontpage,transition;
public static boolean onFrontPage,up,down,left,right,inAttic,onTransition;
public static int px,py,kx,ky;
public static Thread th1,th2;
public static JFrame frame = new JFrame("Forgotten");
public static void main(String[] args){
onFrontPage = true;
px = 600;
py = 400;
ForgottenMain fm = new ForgottenMain();
frame.add(fm);
frame.setSize(1200,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
frame.add(new ForgottenMain());
fm.repaint();
}
public ForgottenMain(){
init();
}
public void init(){
setSize(1200,800);
setVisible(true);
frame.addKeyListener(this);
frame.addMouseListener(this);
try{
player = ImageIO.read(new File("char.png"));
flashlight = ImageIO.read(new File("flashlightimage.png"));
attic = ImageIO.read(new File("attic.png"));
killer = ImageIO.read(new File("killer.png"));
frontpage = ImageIO.read(new File("frontpageoutline.png"));
transition = ImageIO.read(new File("transitionoutline.png"));
} catch (Exception e){
e.printStackTrace();
}
// Gameloop
new javax.swing.Timer(TIMER_DELAY, new ActionListener() {
public void actionPerformed(ActionEvent e) {
gameLoop();
}
}).start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int fx = px - 1033;
int fy = py - 635;
kx = 500;
ky = 500;
// Removes the flickering of the images
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Resets the screen to make sure that it only shows the character once
g2.clearRect(0, 0, 1200, 800);
// Draws the background attic
if(inAttic == true){
System.out.println("Drawing attic");
g2.drawImage(attic,0,0,this);
}
if(onFrontPage == true){
g2.drawImage(frontpage, 0, 0, this);
}
// Draws the player
if(onFrontPage == false && onTransition == false){
g2.drawImage(player, px, py, this);
// Draws the Serial Killer
g2.drawImage(killer, kx, ky, this);
// Draws the flashlight
g2.drawImage(flashlight, fx, fy, this);
}
if(onTransition == true){
g2.drawImage(transition, 0, 0, this);
System.out.println("Drawing Transition");
try {
System.out.println("Sleeping for 5 Seconds");
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Done sleeping.");
onTransition = false;
inAttic = true;
}
System.out.println(px + " " + py);
}
public void gameLoop(){
}
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println("MouseLocation: " + arg0.getX() + ", " + arg0.getY());
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent e) {
if(e.getX() > 499 && e.getY() > 343 && e.getX() < 748 && e.getY() < 391){
onFrontPage = false;
onTransition = true;
repaint();
}
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
My problem is, in the paint method, in the if statement testing if onTransition is equal to true, it's supposed to draw the image transition, wait 5 seconds, and then draw the game.
However, right now it's waiting 5 seconds, then quickly drawing the transition screen and then the game. For some reason they are out of order.
How can I fix this? I have tried alternate methods of waiting 5 seconds, for example using currentTimeMillis();, but have the same outcome.
You have some serious problems:
you add panels twice
you have some circuitous use of frame etc
Beyond that the following will work: you have to realize that you don't have control of the painting process. Therefore you should launch a new thread to count the sleep and let the paint do its work uninhibited.
(Also I have removed static - if you really want them you can try to put them back in - if it doesnt work you just throw them out)
class F extends JPanel implements MouseListener{
/**
*
*/
private static final int TIMER_DELAY = 35;
private static final long serialVersionUID = -4926251405849574401L;
public BufferedImage attic,flashlight,player,killer,frontpage,transition;
public boolean onFrontPage,up,down,left,right,inAttic,onTransition;
public int px,py,kx,ky;
public static Thread th1,th2;
public JFrame frame = new JFrame("Forgotten");
public F(){
init();
}
public void init(){
setSize(1200,800);
setVisible(true);
onFrontPage = true;
px = 600;
py = 400;
frame.add(this);
frame.setSize(1200,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
frame.addMouseListener(this);
try{
// player = ImageIO.read(new File("char.png"));
// flashlight = ImageIO.read(new File("flashlightimage.png"));
// attic = ImageIO.read(new File("attic.png"));
// killer = ImageIO.read(new File("killer.png"));
attic = ImageIO.read(new File(...));
frontpage = ImageIO.read(new File(...));
transition = ImageIO.read(new File(...));
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int fx = px - 1033;
int fy = py - 635;
kx = 500;
ky = 500;
// Removes the flickering of the images
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Resets the screen to make sure that it only shows the character once
g2.clearRect(0, 0, 1200, 800);
// Draws the background attic
if(inAttic == true){
System.out.println("Drawing attic"+System.currentTimeMillis());
g2.drawImage(attic,0,0,this);
}
if(onFrontPage == true){
g2.drawImage(frontpage, 0, 0, this);
}
// Draws the player
if(onFrontPage == false && onTransition == false){
g2.drawImage(player, px, py, this);
// Draws the Serial Killer
g2.drawImage(killer, kx, ky, this);
// Draws the flashlight
g2.drawImage(flashlight, fx, fy, this);
}
if(onTransition == true){
Graphics gt=transition.getGraphics();
gt.setColor(Color.red);
gt.drawString("xxx"+System.currentTimeMillis(), 10, 100);
g2.drawImage(transition, 0, 0, this);
onTransition = false;
inAttic = true;
}
System.out.println(px + " " + py);
}
public void gameLoop(){
}
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println("MouseLocation: " + arg0.getX() + ", " + arg0.getY());
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent e) {
onFrontPage = false;
onTransition = true;
Thread th=new Thread() {
public void run() {
repaint();
System.out.println("Drawing Transition"+System.currentTimeMillis());
try {
System.out.println("Sleeping for 5 Seconds"+System.currentTimeMillis());
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Done sleeping."+System.currentTimeMillis());
repaint();
}
};
th.start();
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
Related
I am learning Java currently and have been given the assignmnet of finidhing off a program to create the game Conways's life (we started with some code provided to us and must add features etc to this).
I am currently stuck on a menu option for the game. I want it to start off at the menu screen, wherein buttons appear at the top for "Start", "Random", "Load", Save". I have written code so that the program displays these buttons, through a fillRect option in my paint method.
My question is, how do I use the mousePressed method to recognise the cells selected so that I can get an action to occur when they are selected. I been looking at this for a while but can't seem to get this working.
Any suggestion would be a massive help. I have shared my code below. It's a work in progress but I would really like to get this working before continuing on with the other functionality.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
public class ConwaysLife extends JFrame implements Runnable, MouseListener {
// member data
private BufferStrategy strategy;
private Graphics offscreenBuffer;
private boolean gameState[][] = new boolean[40][40];
private boolean isGameInProgress = false;
// constructor
public ConwaysLife () {
//Display the window, centred on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - 400;
int y = screensize.height/2 - 400;
setBounds(x, y, 800, 800);
setVisible(true);
this.setTitle("Conway's game of life");
// initialise double-buffering
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenBuffer = strategy.getDrawGraphics();
// register the Jframe itself to receive mouse events
addMouseListener(this);
// initialise the game state
for (x=0;x<40;x++) {
for (y=0;y<40;y++) {
gameState[x][y]=false;
}
}
// create and start our animation thread
Thread t = new Thread(this);
t.start();
}
// thread's entry point
public void run() {
while ( 1==1 ) {
// 1: sleep for 1/5 sec
try {
Thread.sleep(200);
} catch (InterruptedException e) { }
// 2: animate game objects [nothing yet!]
/*if (isGameInProgress == false) {
this.repaint();
}*/
// 3: force an application repaint
this.repaint();
}
}
// mouse events which must be implemented for MouseListener
public void mousePressed(MouseEvent e) {
while (!isGameInProgress) {
int x = e.getX()/20;
int y = e.getY()/20;
if(x >= 10 && x <= 80 && y >= 40 && y <= 65) {
isGameInProgress = !isGameInProgress;
this.repaint();
}
}
// determine which cell of the gameState array was clicked on
int x = e.getX()/20;
int y = e.getY()/20;
// toggle the state of the cell
gameState[x][y] = !gameState[x][y];
// request an extra repaint, to get immediate visual feedback
this.repaint();
}
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
//
// application's paint method
public void paint(Graphics g) {
Font font = new Font("Veranda", Font.BOLD, 20);
g = offscreenBuffer; // draw to off screen buffer
// clear the canvas with a big black rectangle
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
/*look to add a while game in progress loop here!!!*/
// draw menu options
if(!isGameInProgress) {
g.setColor(Color.green);
g.fillRect(10, 40, 70, 25);
g.fillRect(100, 40, 100, 25);
g.fillRect(300, 40, 170, 25);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString("Start", 15, 60);
g.drawString("Random", 105, 60);
g.drawString("Load", 305, 60);
g.drawString("Save", 395, 60);
}
// redraw all game objects
g.setColor(Color.WHITE);
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (gameState[x][y]) {
g.fillRect(x*20, y*20, 20, 20);
}
}
}
// flip the buffers
strategy.show();
}
// application entry point
public static void main(String[] args) {
ConwaysLife w = new ConwaysLife();
}
}
You're not going to like the answer, but it's the "correct" way to approach the problem.
What you need to understand is, Swing/AWT is using a "passive" rendering workflow and BufferStrategy is using a "active" rendering workflow, these are incompatible with each other.
As a general rule, you should not be overriding paint of top level containers like JFrame, this is going to end in no end of issues. Instead, you should be starting with something like JPanel and overriding it's paintComponent method instead.
Having said that, there's a "simpler" solution available to you. CardLayout. This will allow you to seperate the workflows of the menu from the game and resolve the issue between Swing/AWT and BufferStrategy
For example...
import java.awt.Canvas;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
enum View {
MENU, GAME;
}
private CardLayout cardLayout = new CardLayout();
private GamePane gamePane;
public MainPane() {
setLayout(cardLayout);
gamePane = new GamePane();
add(new MenuPane(new MenuPane.Observer() {
#Override
public void startNewGame() {
showGame();
}
#Override
public void randomGame() {
}
#Override
public void loadGame() {
}
#Override
public void saveGame() {
}
}), View.MENU);
add(gamePane, View.GAME);
}
protected void add(Component compent, View view) {
add(compent, view.name());
}
protected void showGame() {
show(View.GAME);
gamePane.start();
}
protected void showMenu() {
gamePane.stop();
show(View.MENU);
}
protected void show(View view) {
cardLayout.show(this, view.name());
}
}
public class MenuPane extends JPanel {
public interface Observer {
public void startNewGame();
public void randomGame();
public void loadGame();
public void saveGame();
}
private Observer observer;
public MenuPane(Observer observer) {
this.observer = observer;
JButton startButton = new JButton("Start");
JButton randomButton = new JButton("Random");
JButton loadButton = new JButton("Load");
JButton saveButton = new JButton("Save");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.ipadx = 10;
gbc.ipady = 10;
gbc.insets = new Insets(8, 8, 8, 8);
gbc.weightx = GridBagConstraints.REMAINDER;
add(startButton, gbc);
add(randomButton, gbc);
add(loadButton, gbc);
add(saveButton, gbc);
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.startNewGame();
}
});
randomButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.randomGame();
}
});
loadButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.loadGame();
}
});
saveButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.saveGame();
}
});
}
}
public class GamePane extends Canvas {
private Thread thread;
private volatile boolean isRunning = false;
public GamePane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void start() {
if (isRunning) {
return;
}
createBufferStrategy(3);
isRunning = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
mainLoop();
}
});
thread.start();
}
protected void stop() {
if (!isRunning || thread == null) {
return;
}
isRunning = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
protected void mainLoop() {
try {
while (isRunning) {
render();
Thread.sleep(16);
}
} catch (InterruptedException ex) {
}
}
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics graphics = strategy.getDrawGraphics();
FontMetrics fm = graphics.getFontMetrics();
String text = "All your game are belong to us";
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = (getHeight() - fm.getHeight()) / 2;
graphics.setColor(Color.WHITE);
graphics.drawString(text, x, y + fm.getAscent());
// Render to graphics
// ...
// Dispose the graphics
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
}
I would strongly recommend that you take the time to read through:
Creating a GUI With Swing
A Visual Guide to Layout Managers
Performing Custom Painting
Painting in AWT and Swing
BufferStrategy and BufferCapabilities
JavaDocs for BufferStrategy which demonstrate how the API should be used.
A "fully" BufferStrategy based approach...
Now, if you can't use Swing, "for reasons", you can still achieve a simular concept using "delegation".
Basically this means "delegating" responsibility for performing some workflow to another. In this case, we want to delegate the rendering and the handling of the mouse events.
This allows you to have a dedicated workflow for the menu and a dedicated workflow for the game, without having to try and mix a lot of state.
Why do I keep on insisting on separating these two workflows? Simply, because it makes it MUCH easier to manage and reason about, but also because it supports the Single Responsibility Principle.
The follow example makes use of Renderable interface to define the core functionality that end "render" delegate will need to implement, in this case, it's pretty simple, we want to tell the renderer to "render" it's content on each paint pass and we want to (optionally) delegate mouse clicked events (this could be done via a second interface or even just the MouseListener interface directly, but I've made it a requirement of the Renderable interface for demonstration purposes.
The "basic" solution to your actual question is found through the use of Rectangle#contains(Point).
This basically inspects each "button" Rectangle to determine if the supplied MouseEvent occurs within it's bounds, if it does, we take action.
It is, however, a little more complicated then that, as we need to have the Rectangles built ahead of time, not difficult, but it's state which is actually reliant on the parent, as we need to know the area in which the renderer is been displayed, run the example, you'll see what I mean 😉
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MainPane mainPane = new MainPane();
JFrame frame = new JFrame();
frame.add(mainPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
mainPane.start();
}
});
}
public interface Renderable {
public void render(Graphics2D g2d, Dimension size);
// We could just extend from MouseListener
// but I don't need all those event handlers
public void mouseClicked(MouseEvent e);
}
public class MainPane extends Canvas {
private Thread thread;
private volatile boolean isRunning = false;
private Renderable currentRenderer;
private MenuRenderer menuRenderer;
private GameRenderer gameRenderer;
public MainPane() {
setBackground(Color.BLACK);
gameRenderer = new GameRenderer();
menuRenderer = new MenuRenderer(new MenuRenderer.Observer() {
#Override
public void startNewGame() {
showGame();
}
#Override
public void randomGame() {
}
#Override
public void loadGame() {
}
#Override
public void saveGame() {
}
});
showMenu();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (currentRenderer == null) {
return;
}
currentRenderer.mouseClicked(e);
}
});
}
protected void showMenu() {
// This may need to tell the game renderer to stop
// or pause
currentRenderer = menuRenderer;
}
protected void showGame() {
currentRenderer = gameRenderer;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void start() {
if (isRunning) {
return;
}
createBufferStrategy(3);
isRunning = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
mainLoop();
}
});
thread.start();
}
protected void stop() {
if (!isRunning || thread == null) {
return;
}
isRunning = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
protected void mainLoop() {
try {
while (isRunning) {
render();
Thread.sleep(16);
}
} catch (InterruptedException ex) {
}
}
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null && currentRenderer != null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
g2d.setBackground(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
);
g2d.setRenderingHints(hints);
// Render to graphics
currentRenderer.render(g2d, getSize());
// Dispose the graphics
g2d.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
public class GameRenderer implements Renderable {
#Override
public void render(Graphics2D g2d, Dimension size) {
FontMetrics fm = g2d.getFontMetrics();
String text = "All your game are belong to us";
int x = (size.width - fm.stringWidth(text)) / 2;
int y = (size.height - fm.getHeight()) / 2;
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y + fm.getAscent());
}
#Override
public void mouseClicked(MouseEvent e) {
}
}
public class MenuRenderer implements Renderable {
public interface Observer {
public void startNewGame();
public void randomGame();
public void loadGame();
public void saveGame();
}
private Observer observer;
private String[] menuOptions = new String[]{
"New Game",
"Random",
"Load Game",
"Save Game"
};
private Rectangle[] menuBounds;
private int internalPadding = 20;
private int horizontalGap = 16;
public MenuRenderer(Observer observer) {
this.observer = observer;
}
#Override
public void render(Graphics2D g2d, Dimension size) {
if (menuBounds == null) {
createMenus(g2d, size);
}
renderMenus(g2d);
}
protected void createMenus(Graphics2D g2d, Dimension size) {
FontMetrics fm = g2d.getFontMetrics();
int totalHeight = (((fm.getHeight() + internalPadding) + horizontalGap) * menuOptions.length) - horizontalGap;
int buttonHeight = fm.getHeight() + internalPadding;
menuBounds = new Rectangle[menuOptions.length];
int buttonWidth = 0;
for (int index = 0; index < menuOptions.length; index++) {
int width = fm.stringWidth(menuOptions[index]) + internalPadding;
buttonWidth = Math.max(width, buttonWidth);
}
int yPos = (size.height - totalHeight) / 2;
for (int index = 0; index < menuOptions.length; index++) {
int xPos = (size.width - buttonWidth) / 2;
Rectangle menuRectangle = new Rectangle(xPos, yPos, buttonWidth, buttonHeight);
menuBounds[index] = menuRectangle;
yPos += buttonHeight + (horizontalGap / 2);
}
}
protected void renderMenus(Graphics2D g2d) {
for (int index = 0; index < menuOptions.length; index++) {
String text = menuOptions[index];
Rectangle bounds = menuBounds[index];
renderMenu(g2d, text, bounds);
}
}
protected void renderMenu(Graphics2D g2d, String text, Rectangle bounds) {
FontMetrics fm = g2d.getFontMetrics();
int textWidth = fm.stringWidth(text);
int textXPos = (bounds.x + (internalPadding / 2)) + ((bounds.width - internalPadding - textWidth) / 2);
int textYPos = bounds.y + (internalPadding / 2);
RoundRectangle2D buttonBackground = new RoundRectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height, 20, 20);
g2d.setColor(Color.BLUE.darker());
g2d.fill(buttonBackground);
g2d.setColor(Color.WHITE);
g2d.drawString(text, textXPos, textYPos + fm.getAscent());
}
#Override
public void mouseClicked(MouseEvent e) {
if (menuBounds == null) {
return;
}
for (int index = 0; index < menuOptions.length; index++) {
if (menuBounds[index].contains(e.getPoint())) {
switch (index) {
case 0:
observer.startNewGame();
break;
case 2:
observer.randomGame();
break;
case 3:
observer.loadGame();
break;
case 4:
observer.saveGame();
break;
}
}
}
}
}
}
For some reason, my KeyListener works just fine and fires off the Booleans to make down and up true and false and the y value changes according to those Booleans exactly how I want it to. My problem is that for some reason, the red rectangle appears to grow in size rather than move, and I'm pretty sure that it's because the previous frame is not cleared. I tried to use super.paintComponent(g); to clear the frame but this accomplishes nothing. Here's the code:
JFrame:
import java.awt.*;
import javax.swing.*;
public class H extends JFrame
{
public H()
{
super("Atlas Blade");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
pack();
P p = new P();
Insets frameInsets = getInsets();
int frameWidth = p.getWidth() +
(frameInsets.left + frameInsets.right);
int frameHeight = p.getHeight() + (
frameInsets.top + frameInsets.bottom);
setPreferredSize(new Dimension(frameWidth, frameHeight));
setLayout(null);
add(p);
pack();
setVisible(true);
}
}
JPanel:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.image.*;
public class P extends JPanel implements KeyListener, Runnable
{
private long updateCount=0;
private long paintCount=0;
private int updatesPerSecond = 50;
private boolean aLeft,aRight,aDown,aUp=false;
private boolean up,down,left,right=false;
int x = 20;
int y=20;
Hb box = new Hb(x,y);
Rectangle rect = new Rectangle(0,300,300,50);
BufferedImage buffer;
public P()
{
super();
setSize(600,350);
//setSize(50,50);
buffer = new BufferedImage (600,350,BufferedImage.TYPE_4BYTE_ABGR);
addKeyListener(this);
Thread jim = new Thread(this);
jim.start();
}
public void run()
{
int waitToUpdate = 1000/updatesPerSecond;
long startTime = System.nanoTime();
while(true)
{
boolean shouldRepaint = false;
long currentTime = System.nanoTime();
long updatesNeeded = (((currentTime-startTime) / 1000000))/ waitToUpdate;
for(long x = updateCount; x< updatesNeeded; x++)
{
updateGame();
shouldRepaint=true;
updateCount++;
}
if(shouldRepaint)
{
paintCount++;
repaint();
}
try
{
Thread.sleep(5);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics bg = buffer.getGraphics();
bg.setColor(Color.BLACK);
bg.drawRect(0,300,300,50);
bg.setColor(Color.RED);
bg.fillRect(x,y,35,35);
g.drawImage(buffer,0,0,null);
}
public void updateGame()
{
box.updateHitbox(x,y);
if(down)
{
if(!box.center.intersects(rect))
{
y++;
//y=y+40;
}
}
else if(up)
{
if(!box.center.intersects(rect))
{
y--;
}
}
}
public void keyPressed(KeyEvent e)
{
int code = e.getKeyCode();
if(code==KeyEvent.VK_A)
{
left=true;
right=false;
aLeft=true;
aRight=false;
aDown=false;
aUp=false;
}
if(code==KeyEvent.VK_D)
{
left=false;
right=true;
aLeft=false;
aRight=true;
aDown=false;
aUp=false;
}
if(code==KeyEvent.VK_S)
{
System.out.println(y);
down=true;
up=false;
aLeft=false;
aRight=false;
aDown=true;
aUp=false;
}
if(code==KeyEvent.VK_W)
{
down=false;
up=true;
aLeft=false;
aRight=false;
aDown=false;
aUp=true;
}
repaint();
}
public void keyTyped(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
int code = e.getKeyCode();
if(code==e.VK_A)
{
left=false;
aLeft=false;
}
if(code==e.VK_D)
{
right=false;
aRight=false;
}
if(code==e.VK_S)
{
down=false;
aDown=false;
}
if(code==e.VK_W)
{
up=false;
aUp=false;
}
}
public void addNotify()
{
// call super so the method still does what it was built to do
super.addNotify();
// requests focus so that key inputs are sent to this screen
requestFocus();
}
}
And the Hb class:
import java.awt.Rectangle;
public class Hb
{
public Rectangle center,left,right,up,down;
public Hb(int x, int y)
{
center = new Rectangle(x,y,50,50);
left = new Rectangle(x-1,y+1,1,48);
right = new Rectangle(x+50,y+1,1,48);
up = new Rectangle(x+1,y-1,48,1);
down = new Rectangle(x+1,y+50,48,1);
}
public void updateHitbox(int x, int y)
{
center = new Rectangle(x,y,50,50);
left = new Rectangle(x-1,y+1,1,48);
right = new Rectangle(x+50,y+1,1,48);
up = new Rectangle(x+1,y-1,48,1);
down = new Rectangle(x+1,y+50,48,1);
}
}
Your problem is that you're doing all your drawing in the BufferedImage, and that doesn't allow erasure of "dirty" pixels. Instead, only draw in the BufferedImage that which should be a static and unchanging part of the image, usually the background. The foreground image that moves should be painted directly in paintComponent using the Graphcis object given to the method by the JVM.
public P() {
super();
setSize(600, 350); // not recommended
buffer = new BufferedImage(600, 350, BufferedImage.TYPE_4BYTE_ABGR);
Graphics bg = buffer.getGraphics();
bg.setColor(Color.BLACK);
bg.drawRect(0, 300, 300, 50);
bg.dispose();
// ....
}
and
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(buffer, 0, 0, null);
g.setColor(Color.RED);
g.fillRect(x, y, 35, 35);
}
public class Screen extends Canvas implements Runnable {
private static final int MAX_FPS = 60;
private static final int FPS_SAMPLE_SIZE = 6;
private boolean[] keysPressed = new boolean[256];
private ArrayList<Character> characters = new ArrayList<>();
private ArrayList<Character> ai = new ArrayList<>();
private Thread thread;
private BufferedImage bg;
//for testing putposes
private Player slime = new Player("Slime.png", 100, 100);
private Player bird = new Player("Bird.png", 250, 250);
private Player giant = new Player("Giant.png", 250, 500);
private Player swordsman = new Player("Swordsman.png", 500, 250);
//screen dimensions
private int height = ((Toolkit.getDefaultToolkit().getScreenSize().height-32)/5*4);
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private long prevTick = -1;
private LinkedList<Long> frames = new LinkedList<>();
private int averageFPS;
private boolean running;
public Screen() {
setSize(width, height);
try {bg = ImageIO.read(new File("GUI Images/success.jpg"));}
catch (Exception e) {Utilities.showErrorMessage(this, e);}
characters.add(slime);
// ai.add(bird);
//ai.add(giant);
// ai.add(swordsman);
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {keysPressed[e.getKeyCode()] = true;}
public void keyReleased(KeyEvent e) {keysPressed[e.getKeyCode()] = false;}
public void keyTyped(KeyEvent e) {}
});
addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
});
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {}
});
addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {}
});
setVisible(true);
start();
}
public void paint(Graphics g){
BufferStrategy bs = getBufferStrategy();
if(getBufferStrategy() == null){
createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
g.drawImage(bg,0,0,width,height, null);
g.drawString(String.valueOf(averageFPS), 0, 0);
for (Character character : ai){
character.setY(character.getY() + (int)(Math.random() * 10 - 5));
character.setX(character.getX() + (int)(Math.random() * 10 - 5));
}
for (Character character : ai) {g.drawImage(character.getImage(), character.getX(), character.getY(), null);}
for (Character character : characters) {g.drawImage(character.getImage(), character.getX(), character.getY(), null);}
g.dispose();
bs.show();
}
public void run() {
while (running) {
tick();
repaint();
}
}
public synchronized void start() {
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
try {thread.join();}
catch (InterruptedException e) {Utilities.showErrorMessage(this, e);}
}
public void tick() {
if (keysPressed[KeyEvent.VK_W] && (slime.getY() > 0)) {slime.setY(slime.getY() - 1);}
if (keysPressed[KeyEvent.VK_S] && (slime.getY() < height)) {slime.setY(slime.getY() + 1);}
if (keysPressed[KeyEvent.VK_A] && (slime.getY() > 0)) {slime.setX(slime.getX() - 1);}
if (keysPressed[KeyEvent.VK_D] && (slime.getY() < width)) {slime.setX(slime.getX() + 1);}
// System.out.println(slime.getX() + ", " + slime.getY());
long pastTime = System.currentTimeMillis() - prevTick;
prevTick = System.currentTimeMillis();
if (frames.size() == FPS_SAMPLE_SIZE) {
frames.remove();
}
frames.add(pastTime);
// Calculate average FPS
long sum = 0;
for (long frame : frames) {
sum += frame;
}
long averageFrame = sum / FPS_SAMPLE_SIZE;
averageFPS = (int)(1000 / averageFrame);
// Only if the time passed since the previous tick is less than one
// second divided by the number of maximum FPS allowed do we delay
// ourselves to give Time time to catch up to our rendering.
if (pastTime < 1000.0 / MAX_FPS) {
try {
Thread.sleep((1000 / MAX_FPS) - pastTime);
System.out.println(averageFPS);
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
}
There's my code, essentially this is just test code for the screen to see if I can properly paint objects to the screen, but there's erratic white frames that keep getting painted. They don't seem to be caused by any sort of input in particular, they just happen seemingly randomly during frame refreshes. Any ideas as to what it might be?
Don't override paint and use a BufferStrategy within it. Painting is usually done on a need to do bases (AKA "passive" rendering). BufferStrategy is a "active" rendering approach, where you take control of the paint process and update the buffer directly, the two don't tend to play well together
Take the logic from the current paint method and place it within your game loop.
This will require you to remove your call to start within the constructor, as the component won't have been added to a valid native peer (a component which is displayed on the screen).
Instead, create an instance of Screen, add it to your frame and then call start, for example...
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestScreen {
public static void main(String[] args) {
new TestScreen();
}
public TestScreen() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Frame frame = new Frame("Testing");
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Screen screen = new Screen();
frame.add(screen);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
screen.start();
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class Screen extends Canvas implements Runnable {
private static final int MAX_FPS = 60;
private static final int FPS_SAMPLE_SIZE = 6;
private boolean[] keysPressed = new boolean[256];
private ArrayList<Character> characters = new ArrayList<>();
private ArrayList<Character> ai = new ArrayList<>();
private Thread thread;
private BufferedImage bg;
private long prevTick = -1;
private LinkedList<Long> frames = new LinkedList<>();
private int averageFPS;
private boolean running;
public Screen() {
try {
bg = ImageIO.read(new File("GUI Images/success.jpg"));
} catch (Exception e) {
e.printStackTrace();
}
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
keysPressed[e.getKeyCode()] = true;
}
public void keyReleased(KeyEvent e) {
keysPressed[e.getKeyCode()] = false;
}
public void keyTyped(KeyEvent e) {
}
});
addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
});
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
}
});
addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
}
});
}
#Override
public Dimension getPreferredSize() {
int height = ((Toolkit.getDefaultToolkit().getScreenSize().height - 32) / 5 * 4);
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
return new Dimension(width, height);
}
public void run() {
createBufferStrategy(3);
BufferStrategy bs = null;
while (running) {
bs = getBufferStrategy();
Graphics g = bs.getDrawGraphics();
g.drawImage(bg, 0, 0, getWidth(), getHeight(), null);
FontMetrics fm = g.getFontMetrics();
g.setColor(Color.RED);
g.drawString(String.valueOf(averageFPS), 0, fm.getAscent());
g.dispose();
bs.show();
tick();
}
}
public synchronized void start() {
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void tick() {
// if (keysPressed[KeyEvent.VK_W] && (slime.getY() > 0)) {
// slime.setY(slime.getY() - 1);
// }
// if (keysPressed[KeyEvent.VK_S] && (slime.getY() < height)) {
// slime.setY(slime.getY() + 1);
// }
// if (keysPressed[KeyEvent.VK_A] && (slime.getY() > 0)) {
// slime.setX(slime.getX() - 1);
// }
// if (keysPressed[KeyEvent.VK_D] && (slime.getY() < width)) {
// slime.setX(slime.getX() + 1);
// }
// // System.out.println(slime.getX() + ", " + slime.getY());
long pastTime = System.currentTimeMillis() - prevTick;
prevTick = System.currentTimeMillis();
if (frames.size() == FPS_SAMPLE_SIZE) {
frames.remove();
}
frames.add(pastTime);
// Calculate average FPS
long sum = 0;
for (long frame : frames) {
sum += frame;
}
long averageFrame = sum / FPS_SAMPLE_SIZE;
averageFPS = (int) (1000 / averageFrame);
// Only if the time passed since the previous tick is less than one
// second divided by the number of maximum FPS allowed do we delay
// ourselves to give Time time to catch up to our rendering.
if (pastTime < 1000.0 / MAX_FPS) {
try {
Thread.sleep((1000 / MAX_FPS) - pastTime);
System.out.println(averageFPS);
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
}
}
So I've pretty much thrown together a basic game in java by following a bunch of different tutorials - the problem is i cant manage to figure out how to get my sprite to move in different directions. Here is the code for my main
package com.game.src.main;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 850;
public static final int HEIGHT = 650;
public static final int SCALE = 1;
public final String TITLE = "Racing Game!";
static ServerSocket serverSocket;
static Socket socket;
static DataOutputStream out;
private boolean running = false;
private Thread thread;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
private BufferedImage spriteSheet = null;
private BufferedImage spriteSheet2 = null;
private BufferedImage background = null;
private BufferedImage MenuBackground = null;
private Player p;
private Player2 p2;
private Menu menu;
public static enum STATE {
MENU,
GAME
};
public static STATE State = STATE.MENU;
public void init() {
BufferedImageLoader loader = new BufferedImageLoader();
try {
spriteSheet = loader.loadImage("/Sprite_Sheet.png");
background = loader.loadImage("/Track.png");
MenuBackground = loader.loadImage("/MenuBG.fw.png");
}
catch (IOException e) {
e.printStackTrace();
}
menu = new Menu();
addKeyListener(new KeyInput(this));
this.addMouseListener(new MouseInput());
p = new Player(365, 500, this);
p2 = new Player2(365, 550, this);
}
private synchronized void start() {
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
private synchronized void stop() {
if(!running)
return;
running = false;
try {
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(1);
}
public void run() {
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if(delta >= 1) {
tick();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(updates + " FPS, TICKS " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
private void tick() {
if(State == STATE.GAME){
p.tick();
p2.tick();
}
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if(bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.drawImage(MenuBackground, 0, 0, null);
if(State == STATE.GAME){
//Drawing the main games background
g.drawImage(background, 0, 0, null);
p.render(g);
p2.render(g);
}
else if(State == STATE.MENU){
menu.render(g);
}
g.dispose();
bs.show();
}
public void keyPressed(KeyEvent e){
int key = e.getKeyCode();
if(State == STATE.GAME){
if(key == KeyEvent.VK_RIGHT){
p.setVelX(5);
}
if(key == KeyEvent.VK_D){
p2.setVelX2(5);
}
else if(key == KeyEvent.VK_LEFT) {
p.setVelX(-5);
}
else if(key == KeyEvent.VK_A) {
p2.setVelX2(-5);
}
else if(key == KeyEvent.VK_DOWN) {
p.setVelY(5);
}
else if(key == KeyEvent.VK_S) {
p2.setVelY2(5);
}
else if(key == KeyEvent.VK_UP) {
p.setVelY(-5);
}
else if(key == KeyEvent.VK_W) {
p2.setVelY2(-5);
}
}
}
public void keyReleased(KeyEvent e){
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT){
p.setVelX(0);
}
if(key == KeyEvent.VK_D){
p2.setVelX2(0);
}
else if(key == KeyEvent.VK_LEFT) {
p.setVelX(0);
}
else if(key == KeyEvent.VK_A) {
p2.setVelX2(0);
}
else if(key == KeyEvent.VK_DOWN) {
p.setVelY(0);
}
else if(key == KeyEvent.VK_S) {
p2.setVelY2(0);
}
else if(key == KeyEvent.VK_UP) {
p.setVelY(0);
}
else if(key == KeyEvent.VK_W) {
p2.setVelY2(0);
}
}
public static void main(String args[]) throws Exception {
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame(game.TITLE);
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
System.out.println("Starting server....");
serverSocket = new ServerSocket(7777);
System.out.println("Server started");
socket = serverSocket.accept();
System.out.println("Connecting from: " + socket.getInetAddress());
out = new DataOutputStream(socket.getOutputStream());
out.writeUTF("This is a test of Java Sockets");
System.out.println("Data has been sent");
}
public BufferedImage getSpriteSheet() {
return spriteSheet;
}
public BufferedImage getSpriteSheet2() {
return spriteSheet2;
}
}
This is my player class
package com.game.src.main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class Player {
private double x;
private double y;
private double velX = 0;
private double velY = 0;
private BufferedImage player;
BufferedImageLoader loader = new BufferedImageLoader();
BufferedImage SpriteSheet = null;
public Player(double x, double y, Game game) {
this.x = x;
this.y = y;
//New instance of Sprite sheet - reading from buffered image loader
SpriteSheet ss = new SpriteSheet(game.getSpriteSheet());
player = ss.grabImage(1, 1, 50, 50);
try {
SpriteSheet = loader.loadImage("/Sprite_Sheet.png");
}
catch(Exception e) {
e.printStackTrace();
}
}
public void tick() {
x+=velX;
y+=velY;
//Adding basic collision
if(x < 0 + 50) {
x = 0 + 50;
}
if(x >= 850 - 100) {
x = 850 - 100;
}
if(y < 0 + 100) {
y = 0 + 100;
}
if(y >= 650 - 100){
y = 650 - 100;
}
}
public void render(Graphics g){
//Draw Track
Color c1 = Color.green;
g.setColor( c1 );
g.fillRect( 150, 200, 550, 300 ); //grass
Color c2 = Color.black;
g.setColor( c2 );
g.drawRect(50, 100, 750, 500); // outer edge
g.drawRect(150, 200, 550, 300); // inner edge
Color c3 = Color.yellow;
g.setColor( c3 );
g.drawRect( 100, 150, 650, 400 ); // mid-lane marker
Color c4 = Color.white;
g.setColor( c4 );
g.drawLine( 425, 500, 425, 600 ); // start line
g.drawImage(player, (int)x, (int)y, null);
}
public double getX(Graphics g){
return x;
}
public double getY(){
return y;
}
public void setX(double x){
this.x = x;
}
public void setY(double y){
this.y = y;
}
public void setVelX(double velX){
this.velX = velX;
}
public void setVelY(double velY){
this.velY = velY;
}
}
I have two players in this game but i'm really stuck on how i can change the sprites direction by 22.5% in a desired direction so if i pressed the up key for player 1 it would rotate my car 22.5% north etc. I have a sprite sheet with 16 sprites for each player for every change in angle by 22.5% This is really confusing me and i'm not sure how i can implement this,
Thanks for taking the time to look
This is a basic example of spinning a sprite
What this is maintain's a virtual state which the Player object inspects in order to determine how it should be changed accordingly. This separates the action from the result, meaning that it would be possible to substitute the action (arrow up key) with some other action, but still obtain the same result.
This example also uses the key bindings API, which doesn't suffer from the same focus related issues that KeyListener does, but this is a pure Swing API and won't be compatiable with Canvas, but is a nice demonstration ;)
The real magic occurs in the characters paint method...
public void paint(Graphics2D g2d) {
Graphics2D g = (Graphics2D) g2d.create();
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.rotate(Math.toRadians(angle), character.getWidth() / 2, character.getHeight() / 2);
g.transform(at);
g.drawImage(character, 0, 0, null);
}
Basically, this creates a AffineTransformation which is then compounded to produce the result we need. That is, first it's anchor position is translated to the characters x/y position and then rotated about the characters center point. Because it's been translated, we can simply paint the character at 0x0. This much easier then try to calculate the characters rotation anchor somewhere else in virtual space - IMHO
The character is rotated by pressing either the Up or Down arrow keys. While pressed, the character will continue to rotate, this is a feature of the example for demonstration purpose.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
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.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateCharater {
public static void main(String[] args) {
new RotateCharater();
}
public RotateCharater() {
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 class TestPane extends JPanel {
private DefaultState state;
private Player player;
public TestPane() {
player = new Player();
state = new DefaultState();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upKeyPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upKeyReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downKeyPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downKeyReleased");
ActionMap am = getActionMap();
am.put("upKeyPressed", new UpKeyAction(state, true));
am.put("upKeyReleased", new UpKeyAction(state, false));
am.put("downKeyPressed", new DownKeyAction(state, true));
am.put("downKeyReleased", new DownKeyAction(state, false));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
player.update(state);
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
player.paint(g2d);
g2d.dispose();
}
public class UpKeyAction extends AbstractAction {
private DefaultState state;
private boolean pressed;
public UpKeyAction(DefaultState state, boolean pressed) {
this.state = state;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
state.setUpKeyPressed(pressed);
}
}
public class DownKeyAction extends AbstractAction {
private DefaultState state;
private boolean pressed;
public DownKeyAction(DefaultState state, boolean pressed) {
this.state = state;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
state.setDownKeyPressed(pressed);
}
}
}
public interface State {
public boolean isUpKeyPressed();
public boolean isDownKeyPressed();
}
public class DefaultState implements State {
private boolean upKeyPressed;
private boolean downKeyPressed;
public boolean isDownKeyPressed() {
return downKeyPressed;
}
public boolean isUpKeyPressed() {
return upKeyPressed;
}
public void setDownKeyPressed(boolean downKeyPressed) {
this.downKeyPressed = downKeyPressed;
upKeyPressed = false;
}
public void setUpKeyPressed(boolean upKeyPressed) {
this.upKeyPressed = upKeyPressed;
downKeyPressed = false;
}
}
public class Player {
private BufferedImage character;
private int x = 100 - 32, y = 100 - 32;
private double angle;
public Player() {
try {
character = ImageIO.read(getClass().getResource("/Character.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void paint(Graphics2D g2d) {
Graphics2D g = (Graphics2D) g2d.create();
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.rotate(Math.toRadians(angle), character.getWidth() / 2, character.getHeight() / 2);
g.transform(at);
g.drawImage(character, 0, 0, null);
}
public void update(State state) {
if (state.isUpKeyPressed()) {
angle -= 22.5;
} else if (state.isDownKeyPressed()) {
angle += 22.5;
}
}
}
}
Remember, this is just an example used to present the concept ;)
Let me start off by saying I know I've violated some basic Java principles in this messy code, but I'm desperately trying to finish a program by Tuesday for a social science experiment, and I don't know Java, so I'm basically just fumbling through it for now.
With that disclaimer out of the way, I have a separate program working where a circle is moving around the screen and the user must click on it. It works fine when its in its own separate class file, but when I add the code to my main program, it's no longer working. I don't even really understand why repaint() calls my paint() function — as far as I'm concerned, it's magic, but I've noticed that repaint() calls paint() in my test program, but not in the more complicated actual program, and I assume that's why the circle is no longer painting on my program.
Entire code is below:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.io.FileReader;
import java.io.IOException;
import java.util.Calendar;
public class Reflexology1 extends JFrame{
private static final long serialVersionUID = -1295261024563143679L;
private Ellipse2D ball = new Ellipse2D.Double(0, 0, 25, 25);
private Timer moveBallTimer;
int _ballXpos, _ballYpos;
JButton button1, button2;
JButton movingButton;
JTextArea textArea1;
int buttonAClicked, buttonDClicked;
private long _openTime = 0;
private long _closeTime = 0;
JPanel thePanel = new JPanel();
JPanel thePlacebo = new JPanel();
final JFrame frame = new JFrame("Reflexology");
final JFrame frame2 = new JFrame("The Test");
JLabel label1 = new JLabel("Press X and then click the moving dot as fast as you can.");
public static void main(String[] args){
new Reflexology1();
}
public Reflexology1(){
frame.setSize(600, 475);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Reflexology 1.0");
frame.setResizable(false);
frame2.setSize(600, 475);
frame2.setLocationRelativeTo(null);
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.setTitle("Reflexology 1.0");
frame2.setResizable(false);
button1 = new JButton("Accept");
button2 = new JButton("Decline");
//movingButton = new JButton("Click Me");
ListenForAcceptButton lForAButton = new ListenForAcceptButton();
ListenForDeclineButton lForDButton = new ListenForDeclineButton();
button1.addActionListener(lForAButton);
button2.addActionListener(lForDButton);
//movingButton.addActionListener(lForMButton);
JTextArea textArea1 = new JTextArea(24, 50);
textArea1.setText("Tracking Events\n");
textArea1.setLineWrap(true);
textArea1.setWrapStyleWord(true);
textArea1.setSize(15, 50);
textArea1.setEditable(false);
FileReader reader = null;
try {
reader = new FileReader("EULA.txt");
textArea1.read(reader, "EULA.txt");
} catch (IOException exception) {
System.err.println("Problem loading file");
exception.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException exception) {
System.err.println("Error closing reader");
exception.printStackTrace();
}
}
}
JScrollPane scrollBar1 = new JScrollPane(textArea1, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
AdjustmentListener listener = new MyAdjustmentListener();
thePanel.add(scrollBar1);
thePanel.add(button1);
thePanel.add(button2);
frame.add(thePanel);
ListenForMouse lForMouse = new ListenForMouse();
thePlacebo.addMouseListener(lForMouse);
thePlacebo.add(label1);
frame2.add(thePlacebo);
ListenForWindow lForWindow = new ListenForWindow();
frame.addWindowListener(lForWindow);
frame2.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e){
if(e.getKeyChar() == 'X' || e.getKeyChar() == 'x') {moveBallTimer.start();}
}
});
frame.setVisible(true);
moveBallTimer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
moveBall();
System.out.println("Timer started!");
repaint();
}
});
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if(frame2.isVisible()){ moveBallTimer.start(); }
}
});
}
private class ListenForAcceptButton implements ActionListener{
public void actionPerformed(ActionEvent e){
if (e.getSource() == button1){
Calendar ClCDateTime = Calendar.getInstance();
System.out.println(ClCDateTime.getTimeInMillis() - _openTime);
_closeTime = ClCDateTime.getTimeInMillis() - _openTime;
//frame.getContentPane().remove(thePanel);
//thePlacebo.addKeyListener(lForKeys);
//frame.getContentPane().add(thePlacebo);
//frame.repaint();
//moveBallTimer.start();
frame.setVisible(false);
frame2.setVisible(true);
frame2.revalidate();
frame2.repaint();
}
}
}
private class ListenForDeclineButton implements ActionListener{
public void actionPerformed(ActionEvent e){
if (e.getSource() == button2){
JOptionPane.showMessageDialog(Reflexology1.this, "You've declined the license agreement. DO NOT RESTART the program. Please go inform a researcher that you have declined the agreement.", "WARNING", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
}
private class ListenForWindow implements WindowListener{
public void windowActivated(WindowEvent e) {
//textArea1.append("Window is active");
}
// if this.dispose() is called, this is called:
public void windowClosed(WindowEvent arg0) {
}
// When a window is closed from a menu, this is called:
public void windowClosing(WindowEvent arg0) {
}
// Called when the window is no longer the active window:
public void windowDeactivated(WindowEvent arg0) {
//textArea1.append("Window is NOT active");
}
// Window gone from minimized to normal state
public void windowDeiconified(WindowEvent arg0) {
//textArea1.append("Window is in normal state");
}
// Window has been minimized
public void windowIconified(WindowEvent arg0) {
//textArea1.append("Window is minimized");
}
// Called when the Window is originally created
public void windowOpened(WindowEvent arg0) {
//textArea1.append("Let there be Window!");
Calendar OlCDateTime = Calendar.getInstance();
_openTime = OlCDateTime.getTimeInMillis();
//System.out.println(_openTime);
}
}
private class MyAdjustmentListener implements AdjustmentListener {
public void adjustmentValueChanged(AdjustmentEvent arg0) {
AdjustmentEvent scrollBar1;
//System.out.println(scrollBar1.getValue()));
}
}
public void paint(Graphics g) {
//super.paint(g);
frame2.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fill(ball);
System.out.println("Calling fill()");
}
protected void moveBall() {
//System.out.println("I'm in the moveBall() function!");
int width = getWidth();
int height = getHeight();
int min, max, randomX, randomY;
min =200;
max = -200;
randomX = min + (int)(Math.random() * ((max - min)+1));
randomY = min + (int)(Math.random() * ((max - min)+1));
//System.out.println(randomX + ", " + randomY);
Rectangle ballBounds = ball.getBounds();
//System.out.println(ballBounds.x + ", " + ballBounds.y);
if (ballBounds.x + randomX < 0) {
randomX = 200;
} else if (ballBounds.x + ballBounds.width + randomX > width) {
randomX = -200;
}
if (ballBounds.y + randomY < 0) {
randomY = 200;
} else if (ballBounds.y + ballBounds.height + randomY > height) {
randomY = -200;
}
ballBounds.x += randomX;
ballBounds.y += randomY;
_ballXpos = ballBounds.x;
_ballYpos = ballBounds.y;
ball.setFrame(ballBounds);
}
public void start() {
moveBallTimer.start();
}
public void stop() {
moveBallTimer.stop();
}
private class ListenForMouse implements MouseListener{
// Called when the mouse is clicked
public void mouseClicked(MouseEvent e) {
//System.out.println("Mouse Panel pos: " + e.getX() + " " + e.getY() + "\n");
if (e.getX() >=_ballXpos && e.getX() <= _ballXpos + 25 && e.getY() <=_ballYpos && e.getY() >= _ballYpos - 25 ) {
System.out.println("TRUE");
}
System.out.println("{e.getX(): " + e.getX() + " / " + "_ballXpos: " + _ballXpos + " | " + "{e.getY(): " + e.getY() + " / " + "_ballYpos: " + _ballYpos);
}
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
// System.out.println("e.getX(): " + e.getX() + " / " + "_ballXpos: " + _ballXpos);
// Mouse over
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
// Mouse left the mouseover area:
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
Could anyone tell me what I need to do to get repaint() to call the paint() method in the above program? I'm assuming the multiple frames is causing the problem, but that's just a guess. Thanks.
You should not paint directly on JFrame. JPanel or extension of JComponent should be used. For painting override paintComponent() rather than paint(). Don't forget to call super.paintComponent(g);.
Also, keep in mind that repaint() only schedules component update, it does not trigger immediate paint().
For more details see Performing Custom Painting and Painting in AWT and Swing.
For example you can apply the following changes. TestPanel will paint the ball.
class TestPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fill(ball);
}
}
Then, update thePlacebo panel:
JPanel thePlacebo = new TestPanel();
Add repaint in moveBall():
thePlacebo.repaint();
Here is an example based on original code:
package so;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.Random;
import javax.swing.*;
public class TestBall {
private static void createAndShowUI() {
final TestPanel panel = new TestPanel();
panel.validate();
JFrame frame = new JFrame("TestBall");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(300, 300));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ActionListener timerAction = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
panel.moveBall();
}
};
Timer timer = new Timer(1000, timerAction);
timer.setRepeats(true);
timer.start();
}
static class TestPanel extends JPanel {
public static int BALL_SIZE = 25;
private Ellipse2D ball = new Ellipse2D.Double(0, 0, BALL_SIZE,
BALL_SIZE);
Random rand = new Random();
public TestPanel() {
this.addMouseListener(new MouseAdapter() {
// Called when the mouse is clicked
public void mouseClicked(MouseEvent e) {
if (ball.contains(e.getPoint())) {
System.out.println("hit the ball");
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fill(ball);
}
public void moveBall() {
Rectangle ballBounds = ball.getBounds();
ball.setFrame(ballBounds);
ballBounds.x = rand.nextInt(getWidth() - BALL_SIZE + 1) + 1;
ballBounds.y = rand.nextInt(getHeight() - BALL_SIZE + 1) + 1;
ball.setFrame(ballBounds);
repaint();
}
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}