Mouse positon updating incorrectly - java

I'm trying to get the mouse position and light up a button when the curser is over it. However the mouse position isn't updating right. I think the y position is messed up because currently if i am far above the button it lights up.
Heres my code that is incorrect:
public void mouseMoved(MouseEvent e) {
Window.mse = new Point((e.getX()) + ((Frame.size.width - Window.myWidth)/2), (e.getY()) + (Frame.size.height - (Window.myHeight)/2));
}
This is the Window file:
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
public class Window extends JPanel implements Runnable {
public Thread thread = new Thread(this);
public static Image[] tileset_ground = new Image[100];
public static Image[] tileset_air = new Image[100];
public static Image[] tileset_resources = new Image[100];
public static int myWidth, myHeight;
public static boolean isFirst = true;
public static Point mse = new Point(0, 0);
public static Room room;
public static Levels levels;
public static Shop shop;
public Window(Frame frame) {
frame.addMouseListener(new KeyHandler());
frame.addMouseMotionListener(new KeyHandler());
thread.start();
}
public void define() {
room = new Room();
levels = new Levels();
shop = new Shop();
tileset_resources[0] = new ImageIcon("resources/cell.png").getImage();
levels.loadLevels(new File("levels/level1.level"));
for(int i=0;i<tileset_ground.length; i++) {
tileset_ground[i] = new ImageIcon("resources/tileset_ground.png").getImage();
tileset_ground[i] = createImage(new FilteredImageSource(tileset_ground[i].getSource(), new CropImageFilter(0, 32 * i, 32, 32)));
}
for(int i=0;i<tileset_air.length; i++) {
tileset_air[i] = new ImageIcon("resources/tileset_air.png").getImage();
tileset_air[i] = createImage(new FilteredImageSource(tileset_air[i].getSource(), new CropImageFilter(0, 32 * i, 32, 32)));
}
}
public void paintComponent(Graphics g) {
if(isFirst) {
myWidth = getWidth();
myHeight = getHeight();
define();
isFirst = false;
}
g.setColor(new Color(70, 70, 70));
g.fillRect(0, 0, myWidth, myHeight);
g.setColor(new Color(0, 0, 0));
g.drawLine(room.block[0][0].x, room.block[room.worldHeight - 1][0].y + room.blockSize, room.block[0][room.worldWidth - 1].x + room.blockSize, room.block[room.worldHeight - 1][0].y + room.blockSize);
g.drawLine(room.block[0][0].x, room.block[room.worldHeight - 1][0].y + 1 + room.blockSize, room.block[0][room.worldWidth - 1].x + room.blockSize, room.block[room.worldHeight - 1][0].y + 1 + room.blockSize);
room.draw(g); //Draws room
shop.draw(g); //Draws shop
}
public void run() {
while(true) {
if(!isFirst) {
room.physic();
}
repaint();
try {
Thread.sleep(1);
} catch(Exception e) {
e.printStackTrace();
}
}
}
}

Simply add a MouseListener to the JButton. Or better still add a ChangeListener to the button's model, and in it, call isRollover() on the model.

Related

Making a character land on a platform

Im trying to code a simple 2D game. I have a Square that you can move with a/w/d. I got jumping working with a sort of gravity but now I cant get myself to land on a platform. I know how to detect collision between two things but even still idk what to do. My gravity always pulls me down until I reach groundLevel which is part of the problem. Here is my code so far. There's a lot of experimenting happing so its pretty messy.
import javax.swing.Timer;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
/**
* Custom Graphics Example: Using key/button to move a line left or right.
*/
public class PlatformingGame extends JFrame implements ActionListener, KeyListener{
// Name-constants for the various dimensions
public static final int CANVAS_WIDTH = 800;
public static final int CANVAS_HEIGHT = 400;
public static final Color LINE_COLOR = Color.BLACK;
public static final Color CANVAS_BACKGROUND = Color.WHITE;
public int GRAVITY = 10;
public int TERMINAL_VELOCITY = 300;
public int vertical_speed = 0;
public int jumpSpeed;
public int groundLevel = CANVAS_HEIGHT;
int Shapes;
private DrawCanvas canvas; // the custom drawing canvas (extends JPanel)
public enum STATE {
PLAYING,
PAUSED,
ONGROUND,
INAIR
};
JButton btnStartRestat, btnExit;
Timer timer;
Rectangle2D.Double guy, platform, platform2;
Shape[] shapeArr = new Shape[10];
int dx, dy;
/** Constructor to set up the GUI */
ArrayList myKeys = new ArrayList<Character>();
public static STATE gameState = STATE.PAUSED;
//public static STATE playerState = STATE.ONGROUND;
public PlatformingGame() {
dx = 3;
dy = 3;
guy = new Rectangle2D.Double( CANVAS_WIDTH/2 - 20, CANVAS_HEIGHT, 30, 20);
platform2 = new Rectangle2D.Double( CANVAS_WIDTH/4, CANVAS_HEIGHT/2+130, 50, 10);
platform = new Rectangle2D.Double( CANVAS_WIDTH/3, CANVAS_HEIGHT/2+50, 50, 10);
timer = new Timer(10, this);
// Set up a panel for the buttons
JPanel btnPanel = new JPanel();
// btnPanel.setPreferredSize(new Dimension(CANVAS_WIDTH/2, CANVAS_HEIGHT));
btnPanel.setLayout(new FlowLayout());
btnStartRestat = new JButton("Start/Restart");
btnExit = new JButton("Exit");
btnPanel.add(btnStartRestat);
btnPanel.add(btnExit);
btnStartRestat.addActionListener(this);
btnExit.addActionListener(this);
// Set up a custom drawing JPanel
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
// Add both panels to this JFrame
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(canvas, BorderLayout.CENTER);
cp.add(btnPanel, BorderLayout.SOUTH);
// "this" JFrame fires KeyEvent
addKeyListener(this);
requestFocus(); // set the focus to JFrame to receive KeyEvent
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Handle the CLOSE button
pack(); // pack all the components in the JFrame
setVisible(true); // show it
}
public void generateSpikes(){
Rectangle2D.Double spikes = null;
for (int i = 0; i < 10; i++) {
spikes = new Rectangle2D.Double (CANVAS_WIDTH - 300 + i*20 , CANVAS_HEIGHT - 30 , 20, 30);
shapeArr[i] = spikes;
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==btnStartRestat)
{
generateSpikes();
dx = 3;
dy = 3;
guy = new Rectangle2D.Double( 100, CANVAS_HEIGHT - guy.height, 15, 30);
platform = new Rectangle2D.Double( CANVAS_WIDTH/3, CANVAS_HEIGHT/2+50, 50, 10);
platform2 = new Rectangle2D.Double( CANVAS_WIDTH/4, CANVAS_HEIGHT/2+130, 50, 10);
gameState = STATE.PLAYING;
}
else if (e.getSource()==btnExit)
{
// requestFocus(); // change the focus to JFrame to receive KeyEvent
}
else if (e.getSource()== timer){
if (isHitDetected(platform2, guy)){
// playerState = STATE.ONGROUND;
guy.y = platform2.y;
}
if (myKeys.contains('a')
&& (guy.x > 0)){
guy.x = guy.x - 5; }
if (myKeys.contains('d')
&& (guy.x < CANVAS_WIDTH - guy.width)){
guy.x = guy.x + 5; }
{
updateGuyPosition();
}
requestFocus();
canvas.repaint();
}
}
public void updateGuyPosition(){
double guyHeight = guy.height;
if (guy.x >= CANVAS_WIDTH - guy.width){
}
if(gameState == STATE.PLAYING) {
guy.y += jumpSpeed;
if (guy.y < groundLevel - guyHeight) {
// if(playerState == STATE.INAIR) {
jumpSpeed += 1;
}
// }
else {
// if(playerState == STATE.INAIR) {
//playerState = STATE.ONGROUND;
jumpSpeed = 0;
guy.y = groundLevel - guyHeight;
}
// }
if (myKeys.contains('w') == true && guy.y == groundLevel - guyHeight) {
jumpSpeed = -15;
// playerState = STATE.INAIR;
}
}
}
public static boolean isHitDetected(Shape shapeA, Shape shapeB) {
Area areaA = new Area(shapeA);
areaA.intersect(new Area(shapeB));
return !areaA.isEmpty();
}
public void keyPressed(KeyEvent evt) {
if (!myKeys.contains(evt.getKeyChar())){
myKeys.add(evt.getKeyChar());
}
}
public void keyReleased(KeyEvent evt) {
myKeys.remove(myKeys.indexOf(evt.getKeyChar()));
}
public void keyTyped(KeyEvent evt) {
}
/** The entry main() method */
public static void main(String[] args) {
PlatformingGame myProg = new PlatformingGame(); // Let the constructor do the job
myProg.timer.start();
}
/**
* DrawCanvas (inner class) is a JPanel used for custom drawing
*/
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(CANVAS_BACKGROUND);
g.setColor(LINE_COLOR);
Graphics2D g2d = (Graphics2D)g;
if(gameState == STATE.PLAYING) {
g2d.fill(guy);
g.setColor(Color.lightGray);
g2d.fill(platform);
g2d.fill(platform2);
for (int i = 0; i < 10; i++) {
g2d.fill(shapeArr[i]);
}
}
if(gameState == STATE.PAUSED) {
g.drawString("Game Paused", CANVAS_WIDTH/2, CANVAS_HEIGHT/2);
}
}
}
}

How to dissapear rectangle like point in Packman

My game is similar to Packman. My problem is, that if Packman will eat the point, no one disappear respectively every point except the first one will change colour like background. I know a I have it in method, but It did what I want when I draw just one point. I just want to clear the point which Packman ate. I created the window in WindowBuilder (I just wanted to try it), I hope it won't be a problem.
public class Hra extends JFrame {
private JPanel contentPane;
PackMan packman ;
Points point ;
boolean check;
ArrayList<Body> points = new ArrayList<Body>();
static int x =900;
static int y=600;
Color packCol = Color.BLACK;
Color pointCol = Color.WHITE;
/**
* Launch the application.
*/
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);
}
public void inicialization() {
for (int i = 0; i < 4; i++) {
Point point = new Point(x, y, 20, pointCol);
x +=100;
points.add(point);
}
}
public GAME() {
inicialization();
packman = new PackMan(0, 900, 900,packCol);
point = new point(900,800,20,pointCol);
check = false;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
getContentPane().setBackground(Color.YELLOW);
JLabel lblNewLabel = new JLabel("Score:" + packman.getScore());
GroupLayout gl_contentPane = new GroupLayout(contentPane);
gl_contentPane.setHorizontalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addContainerGap()
.addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 107, GroupLayout.PREFERRED_SIZE)
.addContainerGap(309, Short.MAX_VALUE))
);
gl_contentPane.setVerticalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE)
.addContainerGap(229, Short.MAX_VALUE))
);
contentPane.setLayout(gl_contentPane);
this.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()) {
case 37: //left
packman.setCoordinatesX(packman.getCoordinatesX()-10);
repaint();
chechCollision();
break;
case 38: //up
packman.setCoordinatesY(packman.getCoordinatesY()-10);
repaint();
chechCollision();
break;
case 39://right
packman.setCoordinatesX(packman.getCoordinatesX()+10);
repaint();
chechCollision();
break;
case 40://down
packman.setCoordinatesY(packman.getCoordinatesY()+10);
chechCollision();
repaint();
break;
}
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
});
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.fillRect(packman.getCoordinatesX(), packman.getCoordinatesY(), 50, 50);
g.setColor(point.getColor());
g.fillRect(point.getPointX(), body.getPointY(), 50, 50);
g.setColor(Color.BLACK);
for(int i =0;i<points.size();i++){
if (kontrola) {
g.clearRect(points.get(i).getPointX(), points.get(i).getPointY(), 50, 50);
}
}
for (int i = 0; i < bodiky.size(); i++) {
g.fillRect(points.get(i).getPointX(), points.get(i).getPointY(), 50, 50);
g.setColor(points.get(i).getColor());
}
}
}
public void checkCollision() {
if (packman.getCoordinatesX() == point.getPointX() && packman.getCoordinatesY() == point.getPointY()) {
packman.setScore(packman.getScore() + point.getValueOfPoint());
lblNewLabel.setText("Score:" + packman.getScore() );
check = true;
point.setColor(Color.YELLOW);
point.setValueOfPoint(0);
repaint();
}
}
}
}
public class Point{
private Color color;
private int pointX;
private int pointY;
private double valueofPoint;
public int getCoordinatesPointX() {
return pointX;
}
public Point(int pointX, int pointY, double valueofPoint,Color color) {
super();
this.pointX = pointX;
this.pointY = pointY;
this.valueofPoint= valueofPoint;
this.color = color;
}
public void sePointX(int pointX) {
this.pointX = pointX;
}
public int getPointY() {
return pointY;
}
public void setPointY(int pointY) {
this.pointY = pointY;
}
public double getValueofPoint() {
return valueofPoint;
}
public void setValueofPoint(double valueofPoint) {
this.valueofPoint = valueofPoint;
}
public void setColor(Color color){
this.color = color;
}
public Color getColor(){
return color;
}
}
public class PackMan {
private double score;
private int coordinatesX;
private int coordinatesY;
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public int getCoordinatesX() {
return coordinatesX;
}
public void setCoordinatesX(int coordinatesX) {
this.coordinatesX = coordinatesX;
}
public int getCoordinatesY() {
return coordinatesY;
}
public void setCoordinatesY(int coordinatesY) {
this.coordinatesY = coordinatesY;
}
public PackMan(double score, int coordinatesX, int coordinatesY) {
super();
this.score = score;
this.coordinatesX = coordinatesX;
this.coordinatesY = coordinatesY;
}
}
Packman(for now just black Rectangle) is trying to eat the the first point(this point is firs in List too),but ,,dissapear"(Point has just changed the colour like background) the second one
[2 picture] result and problem which I described in 1
Pakcman has moved to another point with same result offcourse
I just want to eat the point just like in Packman
public void checkCollision() {
if (packman.getCoordinatesX() == point.getPointX() && packman.getCoordinatesY() == point.getPointY()) {
packman.setScore(packman.getScore() + point.getValueOfPoint());
lblNewLabel.setText("Score:" + packman.getScore() );
check = true;
point.setColor(Color.YELLOW);
point.setValueOfPoint(0);
repaint();
points.remove(i);
}
pacman after what changed direction
public class Hra extends JFrame {
Timer timer;
private JPanel contentPane;
PackMan packman ;
Points point ;
boolean check;
ArrayList<Point> points = new ArrayList<Point>();
static int x =900;
static int y=600;
Color packCol = Color.BLACK;
Color pointCol = Color.WHITE;
static Direction vysledek = new Direction(null);
static int newX = 900;
static int newY =200;
static int xVel = 1;
static int yVel = 1;
static int count = 1;
static JLabel gifLabel = new JLabel(new ImageIcon(//path of Image));
/**
* Launch the application.
*/
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);
frame.getContentPane().add(gifLabel);
gifLabel.setLocation(packman.getCoordinatesX(),packman.getCoordinatesY()-38);
}
public void inicialization() {
for (int i = 0; i < 4; i++) {
Point point = new Point(x, y, 20, pointCol);
y -=100;
points.add(point);
}
for (int i = 0; i < 4; i++) {
Point p = new Point(newX, newY, 20, pointCol);
newX-=100;
points.add(p);
}
}
public GAME() {
inicialization();
packman = new PackMan(0, 900, 900,packCol);
point = new point(900,800,20,pointCol);
check = false;
timer = new Timer(10,this);
timer.start();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
getContentPane().setBackground(Color.YELLOW);
JLabel lblNewLabel = new JLabel("Score:" + packman.getScore());
GroupLayout gl_contentPane = new GroupLayout(contentPane);
gl_contentPane.setHorizontalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addContainerGap()
.addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 107, GroupLayout.PREFERRED_SIZE)
.addContainerGap(309, Short.MAX_VALUE))
);
gl_contentPane.setVerticalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE)
.addContainerGap(229, Short.MAX_VALUE))
);
contentPane.setLayout(gl_contentPane);
this.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()) {
case 37: //left
count = 1;
repaint();
chechCollision();
break;
case 38: //up
count =2;
repaint();
chechCollision();
break;
case 39://right
count = 3;
repaint();
chechCollision();
break;
case 40://down
count = 4;
chechCollision();
repaint();
break;
}
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
});
}
#Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.fillRect(packman.getCoordinatesX(), packman.getCoordinatesY(), 50, 50);
g.setColor(point.getColor());
for (int i = 0; i < points.size(); i++) {
g.drawOval(points.get(i).getPointX(), points.get(i).getPointY(), 50, 50);
g.fillOval(points.get(i).getPointX(), points.get(i).getPointY(), 50, 50);
}
}
}
public void checkCollision() {
if (packman.getCoordinatesX() == point.getPointX() && packman.getCoordinatesY() == point.getPointY()) {
packman.setScore(packman.getScore() + point.getValueOfPoint());
lblNewLabel.setText("Score:" + packman.getScore() );
points(i);
repaint();
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (count == 1) {
packman.setCoordinatesX(packman.getCoordinates() - xVel);
gifLabel.setLocation(packman.getCoordinatesX(), packman.getCoordinatesY()-38);
repaint();
chechCollision();
}
if (count ==2) {
packman.setCoordinatesY(packman.getCoordinatesY() - yVel);
gifLabel.setLocation(packman.getCoordinatesX(), packman.getCoordinatesY()-38);
repaint();
chechCollision();
}
if (count ==3) {
packman.setCoordinatesX(packman.getSouradniceX() + xRychlost);
gifLabel.setLocation(packman.getSouradniceX(), packman.getSouradniceY()-38);
repaint();
chechCollision();
}
if (count ==4) {
packman.setCoordinatesY(packman.getCoordinatesY() + yVel);
gifLabel.setLocation(packman.getSouradniceX(), packman.getCoordinatesY()-38);
repaint();
chechCollision();
}
}
public class Direction {
private Directions directions;
private int index;
public Smery getDirection() {
return directions;
}
public void setSmery(Directions directions) {
this.directions = directions;
}
public directions(Directions directions) {
super();
this.directions = directions;
}
public enum Smery {
doprava,doleva,nahoru,dolu;
}
Introduction
I could not get your code to compile.
Here's my best guess as to what your GUI looks like when it starts up.
Here's what the GUI looks like after I've eaten a pellet.
Here's what the GUI looks like after I've eaten more pellets.
Explanation
Do not use static variables.
Do not use so many global variables. You created a model, so pass the model to the controller classes.
Why did you make the score variable a double? An int seems like it would work.
Model
You had the right idea. You created a Packman class to hold a packman and a Point class to hold a pellet. I renamed your Point class to Pellet because there's a java.awt.Point class that is useful for this code.
I saved the center point of the packman and the center point of each pellet. You'll see in the drawing code how I adjust the center point to draw an oval.
I created a GameModel class to hold a java.util.List of Pellet instances, and a Packman instance. The code to check for collisions is also included in this class.
The collision detection is simple. If the center of the pellet and the center of the packman are close enough together, then the packman eats the pellet.
View
I started the Swing application with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame, a score JPanel, and a drawing JPanel. I didn't extend a JFrame. I used a JFrame. I don't care how large the JFrame is, so I used the pack method to pack the components on the JFrame.
The score JPanel uses a FlowLayout to place the JLabel and JTextField in the JPanel.
The drawing JPanel extends JPanel so I can override the paintComponent method. It's much easier to draw on a drawing JPanel. You don't have to take the rest of the GUI into account. All the drawing Panel does is draw the packman and the pellets. Moving the packman and checking for collisions happen in the controller classes.
I used KeyBindings rather than a KeyListener. Generally, key bindings are more reliable than a key listener. It's too easy for the component to lose focus and for the keys to stop working. Sure, the key bindings code looks scary at first glance. I have to look up the format every time I want to use them, which is why I provided the link. But in the long run key bindings are better.
I coded indirect key actions. The key presses just change the Direction enum in the Packman class. The java.swing.Timer ActionListener actually moves the packman. This makes the motion much smoother.
If you want to make the packman move faster or slower, adjust the distance parameter in the Packman move method.
For some reason I can't determine, the left and right arrow keys don't respond. I coded the WSAD keys as a substitute. I know the code works because it works with the WSAD keys. If I ever figure out why the left and right arrow keys are unresponsive, I'll update this answer.
Controller
I have a NavigationAction class to listen for the key bindings and a MotionListener class to move the packman.
The NavigationAction class indirectly updates the Direction enum in the Packman class.
The MotionListener class moves the packman and checks for collisions. Since these methods are in other classes, the actionPerformed method is short and easy to understand.
Code
Here's the complete, formatted, runnable code. I made all the classes inner classes so I could post the code as one large block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class PackmanGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PackmanGUI());
}
private DrawingPanel drawingPanel;
private final GameModel model;
private JFrame frame;
private JPanel scorePanel;
private JTextField scoreField;
private Timer timer;
public PackmanGUI() {
this.model = new GameModel();
}
#Override
public void run() {
frame = new JFrame("Packman");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
scorePanel = createScorePanel();
frame.add(scorePanel, BorderLayout.BEFORE_FIRST_LINE);
drawingPanel = new DrawingPanel(model);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
setKeyBindings();
timer = new Timer(40, new MotionListener(this, model));
timer.start();
}
private void setKeyBindings() {
String up = "up";
String down = "down";
String left = "left";
String right = "right";
String pressed = " pressed";
String released = " released";
InputMap inputMap = drawingPanel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = drawingPanel.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), up + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), down + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), left + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), right + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), up + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), down + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), left + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), right + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), up + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), down + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), left + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), right + pressed);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), up + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), down + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), left + released);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), right + released);
Packman packman = model.getPackman();
actionMap.put(up + pressed, new NavigationAction(Direction.UP, packman));
actionMap.put(down + pressed, new NavigationAction(Direction.DOWN, packman));
actionMap.put(left + pressed, new NavigationAction(Direction.LEFT, packman));
actionMap.put(right + pressed, new NavigationAction(Direction.RIGHT, packman));
actionMap.put(up + released, new NavigationAction(Direction.NONE, packman));
actionMap.put(down + released, new NavigationAction(Direction.NONE, packman));
actionMap.put(left + released, new NavigationAction(Direction.NONE, packman));
actionMap.put(right + released, new NavigationAction(Direction.NONE, packman));
}
private JPanel createScorePanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBackground(Color.YELLOW);
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
Font font = panel.getFont().deriveFont(Font.BOLD, 36f);
JLabel label = new JLabel("Score:");
label.setFont(font);
panel.add(label);
scoreField = new JTextField(10);
scoreField.setBackground(Color.YELLOW);
scoreField.setEditable(false);
scoreField.setFont(font);
scoreField.setHorizontalAlignment(JTextField.TRAILING);
updateScore(model.getPackman().getScore());
panel.add(scoreField);
return panel;
}
public void updateScore(double score) {
scoreField.setText(Double.toString(score));
}
public JFrame getFrame() {
return frame;
}
public void repaint() {
drawingPanel.repaint();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final GameModel model;
public DrawingPanel(GameModel model) {
this.model = model;
this.setBackground(Color.BLACK);
this.setPreferredSize(new Dimension(800, 800));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Packman packman = model.getPackman();
Point point = packman.getCenterPoint();
// Draw body
int x = point.x - 25;
int y = point.y - 25;
g.setColor(Color.YELLOW);
g.fillArc(x, y, 50, 50, 30, 300);
// Draw eye
x = point.x;
y = point.y - 18;
g.setColor(Color.BLACK);
g.fillOval(x, y, 8, 8);
// Draw pellets
for (Pellet pellet : model.getPellets()) {
g.setColor(pellet.getColor());
point = pellet.getPoint();
x = point.x - 10;
y = point.y - 10;
g.fillOval(x, y, 20, 20);
}
}
}
public interface KeyDirectionActionHandler {
public void keyDirectionActionPerformed(Direction direction);
}
public class NavigationAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private Direction direction;
private KeyDirectionActionHandler keyDirectionActionHandler;
public NavigationAction(Direction direction,
KeyDirectionActionHandler keyDirectionActionHandler) {
this.direction = direction;
this.keyDirectionActionHandler = keyDirectionActionHandler;
}
#Override
public void actionPerformed(ActionEvent event) {
keyDirectionActionHandler.keyDirectionActionPerformed(direction);
}
}
public enum Direction {
NONE, UP, DOWN, RIGHT, LEFT;
}
public class MotionListener implements ActionListener {
private final PackmanGUI frame;
private final GameModel model;
public MotionListener(PackmanGUI frame, GameModel model) {
this.frame = frame;
this.model = model;
}
#Override
public void actionPerformed(ActionEvent event) {
model.getPackman().move();
model.checkCollisions();
frame.updateScore(model.getPackman().getScore());
frame.repaint();
}
}
public class GameModel {
private final List<Pellet> pellets;
private final Packman packman;
public GameModel() {
this.packman = new Packman(0, new Point(400, 400));
this.pellets = new ArrayList<>();
inicialization();
}
public void inicialization() {
int x = 600;
int y = 600;
for (int i = 0; i < 4; i++) {
Pellet pellet = new Pellet(new Point(x, y), 20, Color.WHITE);
y -= 100;
pellets.add(pellet);
}
for (int i = 0; i < 4; i++) {
Pellet pellet = new Pellet(new Point(x, y), 20, Color.WHITE);
x -= 100;
pellets.add(pellet);
}
}
public void checkCollisions() {
Point packmanPoint = packman.getCenterPoint();
for (int index = 0; index < pellets.size(); index++) {
Pellet pellet = pellets.get(index);
Point pelletPoint = pellet.getPoint();
// Collision detection
int deltaX = packmanPoint.x - pelletPoint.x;
int deltaY = packmanPoint.y - pelletPoint.y;
double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 20) {
pellets.remove(pellet);
packman.incrementScore(pellet.getValue());
}
}
}
public List<Pellet> getPellets() {
return pellets;
}
public Packman getPackman() {
return packman;
}
}
public class Packman implements KeyDirectionActionHandler {
private double score;
private Direction direction;
private Point centerPoint;
public Packman(double score, Point centerPoint) {
this.score = score;
this.centerPoint = centerPoint;
this.direction = Direction.NONE;
}
public double getScore() {
return score;
}
public void incrementScore(double score) {
this.score += score;
}
public Point getCenterPoint() {
return centerPoint;
}
public void setCenterPoint(Point centerPoint) {
this.centerPoint = centerPoint;
}
public void move() {
int distance = 6;
int deltaX = 0;
int deltaY = 0;
switch (direction) {
case NONE:
deltaX = 0;
deltaY = 0;
break;
case UP:
deltaX = 0;
deltaY = -distance;
break;
case DOWN:
deltaX = 0;
deltaY = distance;
break;
case LEFT:
deltaX = -distance;
deltaY = 0;
break;
case RIGHT:
deltaX = distance;
deltaY = 0;
}
int x = centerPoint.x + deltaX;
int y = centerPoint.y + deltaY;
this.centerPoint = new Point(x, y);
}
#Override
public void keyDirectionActionPerformed(Direction direction) {
this.direction = direction;
}
}
public class Pellet {
private double value;
private Color color;
private Point point;
public Pellet(Point point, double value, Color color) {
this.point = point;
this.value = value;
this.color = color;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
}
}

How to get my Buffered Image class to display in my GUI?

I have a program that does an animation using timers switching images. When the program is on its last image I use a class to create a buffered image of that image with text over it. When the last image of the animation is displayed I want to change the image displayed to the buffered image. I can't get it to work. The code as is plays as if the bolded section isnt there. If I delete the line above it, it displays the image with text over it and nothing else. What edits should I make to my code to fix this?
The Class that does the animation
**import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.*;
import java.io.*;
import java.io.File;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.swing.*;
import javax.swing.*;
import javax.imageio.ImageIO;
/**
* Write a description of class Reveal here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class Reveal extends JPanel
{
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private String[] image = {"Jack in the Box 1.png","Jack in the Box 2.png","Jack in the Box 3.png","Jack in the Box 4.png","Jack in the Box 5.png","Jack in the Box 6.png","Jack in the Box 7.png"}; //an array to hold the frames of the animation
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private JFrame f;
private TextOverlay TO;
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal(String name, int number)
{
TO = new TextOverlay("Jack in the Box 7.png", name, number);
for (int h = 0; h < 7; h++){
icon[h] = new ImageIcon(image[h]);
icon[h].getImage();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//Sets the size of the window
f.setSize(800,850);
panel = new JPanel();
label = new JLabel();
label.setIcon( icon[x] );
panel.add(label);
setVisible(true);
f.add(panel);
display(name, number);
**f.add(TO);**
}
public void display(String name, int number){
timer = new Timer(150, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (counter > 27){
timer.stop();
timer2.start(); //starts the second half of the animation
}else{
if (x != 3){
x++;
}else{
x = 0;
}
label.setIcon( icon[x] );
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(250, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (y > 6) {
timer2.stop();
}else{
label.setIcon( icon[y] );
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
}
**
The class that puts text over an image
import java.io.*;
import java.awt.*;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
/**
* #see https://stackoverflow.com/questions/2658663
*/
public class TextOverlay extends JPanel {
private BufferedImage image;
private String name;
private String fileX;
private int number;
public TextOverlay(String f, String s, int n) {
name = s;
number = n;
fileX = f;
try {
image = ImageIO.read(new File(fileX));
} catch (IOException e) {
e.printStackTrace();
}
image = process(image, name, number);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
private BufferedImage process(BufferedImage old, String name, int number) {
int w = old.getWidth();
int h = old.getHeight();
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(old, 0, 0, w, h, this);
g2d.setPaint(Color.black);
g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
String s1 = name;
String s2 = Integer.toString(number);;
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString(s1, 40, 90);
g2d.drawString(s2, 40, 140);
g2d.dispose();
return img;
}
}
So, you seem to have a misunderstanding of how Swing works, you might find How to Use Swing Timers and Concurrency in Swing of some assistance.
Basically, when you start a Timer, it doesn't block at this point until the timer ends (and even if it did, your wouldn't work the way you wanted it to). Instead, a new thread is created and after the specified period a request is placed on Event Dispatching Thread to execute the supplied Runnable.
This means that when you do something like...
f.add(panel);
display(name, number);
f.add(TO);
You are actually adding the TO component onto of the JLabel (because the frame is using a BorderLayout and the CENTRE position is the default position.
Instead, in your second timer completes, you need to remove the label and add the TO component...
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
Runnable Example...
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class Reveal extends JPanel {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new Reveal("Test", 5);
}
});
}
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private JFrame f;
private TextOverlay TO;
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal(String name, int number) {
TO = new TextOverlay("Jack in the Box 7.png", name, number);
for (int h = 0; h < 7; h++) {
icon[h] = new ImageIcon(makeImage(h));
icon[h].getImage();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//Sets the size of the window
f.setSize(800, 850);
panel = new JPanel(new GridBagLayout());
label = new JLabel();
label.setIcon(icon[x]);
label.setBorder(new LineBorder(Color.RED));
panel.add(label);
f.add(panel);
display(name, number);
// f.add(TO);
setVisible(true);
}
public void display(String name, int number) {
timer = new Timer(150, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (counter > 27) {
timer.stop();
timer2.start(); //starts the second half of the animation
} else {
if (x != 3) {
x++;
} else {
x = 0;
}
label.setIcon(icon[x]);
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(250, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
Container parent = label.getParent();
parent.remove(label);
parent.add(TO);
parent.revalidate();
} else {
label.setIcon(icon[y]);
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
protected BufferedImage makeImage(int h) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(h);
int x = (100 - fm.stringWidth(text)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, 100, 100);
g2d.setColor(Color.BLACK);
g2d.drawString(text, x, y);
g2d.dispose();
return img;
}
public class TextOverlay extends JPanel {
private BufferedImage image;
private String name;
private String fileX;
private int number;
public TextOverlay(String f, String s, int n) {
name = s;
number = n;
fileX = f;
image = makeImage(n);
image = process(image, name, number);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
private BufferedImage process(BufferedImage old, String name, int number) {
int w = old.getWidth();
int h = old.getHeight();
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(old, 0, 0, w, h, this);
g2d.setPaint(Color.black);
g2d.setFont(new Font("Franklin Gothic Demi Cond", Font.PLAIN, 30));
String s1 = name;
String s2 = Integer.toString(number);;
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString(s1, 40, 90);
g2d.drawString(s2, 40, 140);
g2d.dispose();
return img;
}
}
}
A "slightly" different approach...
Animation is actually a really complex subject which is not easy to implement well.
This is why, when faced with problems like these, I prefer to look at libraries which have already been implemented to help solve them. I'd recommend having a look at:
The Timing Framework
Trident
universal-tween-engine
as some starting points.
While I prefer to use libraries, sometimes it's not possible or the libraries don't fit my overall needs ... that and I like to dabble ... it's kind of a hobby.
Based on what I can understand from your code, you're trying to start out with a fast animation and then slow it down till you get to the last frame. In animation theory, this is commonly known as easement, more specifically, "slow/ease out".
The following borrows from a bunch of snippets I've been playing with (to devise a more reusable library) that will basically (randomly) display the images over a period of 4 seconds, with the animation slowing down and finally, presenting the "lucky" number
nb The gif animation is actually really slow, you'll need to run it to see the difference
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
public class Reveal extends JPanel {
public static void main(String[] args) {
new Reveal();
}
public Reveal() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private IntAnimatable animatable;
private List<ImageIcon> icons = new ArrayList<>(25);
private JLabel label = new JLabel();
public TestPane() {
setLayout(new GridBagLayout());
IntRange range = new IntRange(0, 111);
animatable = new IntAnimatable(range, Duration.ofSeconds(4), Easement.SLOWOUT, new AnimatableListener<Integer>() {
#Override
public void animationChanged(Animatable<Integer> animator) {
int value = animator.getValue();
int index = value % 7;
ImageIcon icon = icons.get(index);
if (label.getIcon() != icon) {
label.setIcon(icon);
}
}
}, new AnimatableLifeCycleAdapter<Integer>() {
#Override
public void animationCompleted(Animatable<Integer> animator) {
BufferedImage img = makeImage(3);
writeTextOverImage("Lucky number", img);
ImageIcon luckNumber = new ImageIcon(img);
label.setIcon(luckNumber);
}
});
for (int index = 0; index < 7; index++) {
icons.add(new ImageIcon(makeImage(index)));
}
Collections.shuffle(icons);
add(label);
Animator.INSTANCE.add(animatable);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
protected void writeTextOverImage(String text, BufferedImage img) {
Graphics2D g2d = img.createGraphics();
Font font = g2d.getFont();
font = font.deriveFont(Font.BOLD, font.getSize2D() + 2);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = img.getWidth();
int height = img.getWidth();
int x = (width - fm.stringWidth(text)) / 2;
int y = fm.getAscent();
g2d.setColor(Color.YELLOW);
g2d.drawString(text, x, y);
g2d.dispose();
}
protected BufferedImage makeImage(int h) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(h);
int x = (100 - fm.stringWidth(text)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, 100, 100);
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
return img;
}
/**** Range ****/
/*
A lot of animation is done from one point to another, this just
provides a self contained concept of a range which can be used to
calculate the value based on the current progression over time
*/
public abstract class Range<T> {
private T from;
private T to;
public Range(T from, T to) {
this.from = from;
this.to = to;
}
public T getFrom() {
return from;
}
public T getTo() {
return to;
}
#Override
public String toString() {
return "From " + getFrom() + " to " + getTo();
}
public abstract T valueAt(double progress);
}
public class IntRange extends Range<Integer> {
public IntRange(Integer from, Integer to) {
super(from, to);
}
public Integer getDistance() {
return getTo() - getFrom();
}
#Override
public Integer valueAt(double progress) {
int distance = getDistance();
int value = (int) Math.round((double) distance * progress);
value += getFrom();
return value;
}
}
/**** Animatable ****/
/*
The core concept of something that is animatable. This basic wraps up the
logic for calculating the progression of the animation over a period of time
and then use that to calculate the value of the range and then the observers
are notified so they can do stuff
*/
public class IntAnimatable extends AbstractAnimatableRange<Integer> {
public IntAnimatable(IntRange animationRange, Duration duration, Easement easement, AnimatableListener<Integer> listener, AnimatableLifeCycleListener<Integer> lifeCycleListener) {
super(animationRange, duration, easement, listener, lifeCycleListener);
}
}
public interface AnimatableListener<T> {
public void animationChanged(Animatable<T> animator);
}
public interface AnimatableLifeCycleListener<T> {
public void animationStopped(Animatable<T> animator);
public void animationCompleted(Animatable<T> animator);
public void animationStarted(Animatable<T> animator);
public void animationPaused(Animatable<T> animator);
}
public interface Animatable<T> {
public T getValue();
public void tick();
public Duration getDuration();
public Easement getEasement();
// Wondering if these should be part of a secondary interface
// Provide a "self managed" unit of work
public void start();
public void stop();
public void pause();
}
public class AnimatableLifeCycleAdapter<T> implements AnimatableLifeCycleListener<T> {
#Override
public void animationStopped(Animatable<T> animator) {
}
#Override
public void animationCompleted(Animatable<T> animator) {
}
#Override
public void animationStarted(Animatable<T> animator) {
}
#Override
public void animationPaused(Animatable<T> animator) {
}
}
public abstract class AbstractAnimatable<T> implements Animatable<T> {
private LocalDateTime startTime;
private Duration duration = Duration.ofSeconds(5);
private AnimatableListener<T> animatableListener;
private AnimatableLifeCycleListener<T> lifeCycleListener;
private Easement easement;
private double rawOffset;
public AbstractAnimatable(Duration duration, AnimatableListener<T> listener) {
this.animatableListener = listener;
this.duration = duration;
}
public AbstractAnimatable(Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
this(duration, listener);
this.lifeCycleListener = lifeCycleListener;
}
public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener) {
this(duration, listener);
this.easement = easement;
}
public AbstractAnimatable(Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
this(duration, easement, listener);
this.lifeCycleListener = lifeCycleListener;
}
public void setEasement(Easement easement) {
this.easement = easement;
}
#Override
public Easement getEasement() {
return easement;
}
public Duration getDuration() {
return duration;
}
protected void setDuration(Duration duration) {
this.duration = duration;
}
public double getCurrentProgress(double rawProgress) {
Easement easement = getEasement();
double progress = Math.min(1.0, Math.max(0.0, getRawProgress()));
if (easement != null) {
progress = easement.interpolate(progress);
}
return Math.min(1.0, Math.max(0.0, progress));
}
public double getRawProgress() {
if (startTime == null) {
return 0.0;
}
Duration duration = getDuration();
Duration runningTime = Duration.between(startTime, LocalDateTime.now());
double progress = rawOffset + (runningTime.toMillis() / (double) duration.toMillis());
return Math.min(1.0, Math.max(0.0, progress));
}
#Override
public void tick() {
if (startTime == null) {
startTime = LocalDateTime.now();
fireAnimationStarted();
}
double rawProgress = getRawProgress();
double progress = getCurrentProgress(rawProgress);
if (rawProgress >= 1.0) {
progress = 1.0;
}
tick(progress);
fireAnimationChanged();
if (rawProgress >= 1.0) {
fireAnimationCompleted();
}
}
protected abstract void tick(double progress);
#Override
public void start() {
if (startTime != null) {
// Restart?
return;
}
Animator.INSTANCE.add(this);
}
#Override
public void stop() {
stopWithNotitifcation(true);
}
#Override
public void pause() {
rawOffset += getRawProgress();
stopWithNotitifcation(false);
double remainingProgress = 1.0 - rawOffset;
Duration remainingTime = getDuration().minusMillis((long) remainingProgress);
setDuration(remainingTime);
lifeCycleListener.animationStopped(this);
}
protected void fireAnimationChanged() {
if (animatableListener == null) {
return;
}
animatableListener.animationChanged(this);
}
protected void fireAnimationCompleted() {
stopWithNotitifcation(false);
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationCompleted(this);
}
protected void fireAnimationStarted() {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationStarted(this);
}
protected void fireAnimationPaused() {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationPaused(this);
}
protected void stopWithNotitifcation(boolean notify) {
Animator.INSTANCE.remove(this);
startTime = null;
if (notify) {
if (lifeCycleListener == null) {
return;
}
lifeCycleListener.animationStopped(this);
}
}
}
public abstract class AbstractAnimatableRange<T> extends AbstractAnimatable<T> {
private Range<T> range;
private T value;
public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener) {
super(duration, listener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
super(duration, listener, lifeCycleListener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener) {
super(duration, easement, listener);
this.range = range;
}
public AbstractAnimatableRange(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener) {
super(duration, easement, listener, lifeCycleListener);
this.range = range;
}
protected void tick(double progress) {
setValue(range.valueAt(progress));
}
protected void setValue(T value) {
this.value = value;
}
#Override
public T getValue() {
return value;
}
}
/*
Easement, complicated, but fun
*/
public enum Easement {
SLOWINSLOWOUT(1d, 0d, 0d, 1d), FASTINSLOWOUT(0d, 0d, 1d, 1d), SLOWINFASTOUT(0d, 1d, 0d, 0d), SLOWIN(1d, 0d, 1d, 1d), SLOWOUT(0d, 0d, 0d, 1d);
private final double[] points;
private final List<PointUnit> normalisedCurve;
private Easement(double x1, double y1, double x2, double y2) {
points = new double[]{x1, y1, x2, y2};
final List<Double> baseLengths = new ArrayList<>();
double prevX = 0;
double prevY = 0;
double cumulativeLength = 0;
for (double t = 0; t <= 1; t += 0.01) {
Point2D xy = getXY(t);
double length = cumulativeLength + Math.sqrt((xy.getX() - prevX) * (xy.getX() - prevX) + (xy.getY() - prevY) * (xy.getY() - prevY));
baseLengths.add(length);
cumulativeLength = length;
prevX = xy.getX();
prevY = xy.getY();
}
normalisedCurve = new ArrayList<>(baseLengths.size());
int index = 0;
for (double t = 0; t <= 1; t += 0.01) {
double length = baseLengths.get(index++);
double normalLength = length / cumulativeLength;
normalisedCurve.add(new PointUnit(t, normalLength));
}
}
public double interpolate(double fraction) {
int low = 1;
int high = normalisedCurve.size() - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
if (fraction > normalisedCurve.get(mid).getPoint()) {
low = mid + 1;
} else if (mid > 0 && fraction < normalisedCurve.get(mid - 1).getPoint()) {
high = mid - 1;
} else {
break;
}
}
/*
* The answer lies between the "mid" item and its predecessor.
*/
final PointUnit prevItem = normalisedCurve.get(mid - 1);
final double prevFraction = prevItem.getPoint();
final double prevT = prevItem.getDistance();
final PointUnit item = normalisedCurve.get(mid);
final double proportion = (fraction - prevFraction) / (item.getPoint() - prevFraction);
final double interpolatedT = prevT + (proportion * (item.getDistance() - prevT));
return getY(interpolatedT);
}
protected Point2D getXY(double t) {
final double invT = 1 - t;
final double b1 = 3 * t * invT * invT;
final double b2 = 3 * t * t * invT;
final double b3 = t * t * t;
final Point2D xy = new Point2D.Double((b1 * points[0]) + (b2 * points[2]) + b3, (b1 * points[1]) + (b2 * points[3]) + b3);
return xy;
}
protected double getY(double t) {
final double invT = 1 - t;
final double b1 = 3 * t * invT * invT;
final double b2 = 3 * t * t * invT;
final double b3 = t * t * t;
return (b1 * points[2]) + (b2 * points[3]) + b3;
}
protected class PointUnit {
private final double distance;
private final double point;
public PointUnit(double distance, double point) {
this.distance = distance;
this.point = point;
}
public double getDistance() {
return distance;
}
public double getPoint() {
return point;
}
}
}
/**** Core Animation Engine ****/
public enum Animator {
INSTANCE;
private Timer timer;
private List<Animatable> properies;
private Animator() {
properies = new ArrayList<>(5);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<Animatable> copy = new ArrayList<>(properies);
Iterator<Animatable> it = copy.iterator();
while (it.hasNext()) {
Animatable ap = it.next();
ap.tick();
}
if (properies.isEmpty()) {
timer.stop();
}
}
});
}
public void add(Animatable ap) {
properies.add(ap);
timer.start();
}
protected void removeAll(List<Animatable> completed) {
properies.removeAll(completed);
}
public void remove(Animatable ap) {
properies.remove(ap);
if (properies.isEmpty()) {
timer.stop();
}
}
}
}

how to separate the logic from the form in java swing?

I have difficulties in separating the logic of the game from the view. The view should be dumb and simply render the current state of the model. But I can not figure out how to do that. Could you please help me in making the code simpler and separating the logic from the form? Because I want to extend the game and add new things but I can not because of the confusing form of the code. I am new in java.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
public class ProjectileShooterTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(600, 600);
final ProjectileShooter projectileShooter = new ProjectileShooter();
ProjectileShooterPanel projectileShooterPanel = new ProjectileShooterPanel(
projectileShooter);
projectileShooter.setPaintingComponent(projectileShooterPanel);
JPanel controlPanel = new JPanel(new GridLayout(1, 0));
controlPanel.add(new JLabel("Angle"));
final JSlider angleSlider = new JSlider(0, 90, 45);
controlPanel.add(angleSlider);
controlPanel.add(new JLabel("Power"));
final JSlider powerSlider = new JSlider(0, 100, 50);
controlPanel.add(powerSlider);
JButton shootButton = new JButton("Shoot");
shootButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int angleDeg = angleSlider.getValue();
int power = powerSlider.getValue();
projectileShooter.setAngle(Math.toRadians(angleDeg));
projectileShooter.setPower(power);
projectileShooter.shoot();
}
});
controlPanel.add(shootButton);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(controlPanel, BorderLayout.NORTH);
f.getContentPane().add(projectileShooterPanel, BorderLayout.CENTER);
f.setVisible(true);
}
}
class ProjectileShooter {
private double angleRad = Math.toRadians(45);
private double power = 50;
private Projectile projectile;
private JComponent paintingComponent;
void setPaintingComponent(JComponent paintingComponent) {
this.paintingComponent = paintingComponent;
}
void setAngle(double angleRad) {
this.angleRad = angleRad;
}
void setPower(double power) {
this.power = power;
}
void shoot() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
executeShot();
}
});
t.setDaemon(true);
t.start();
}
private void executeShot() {
if (projectile != null) {
return;
}
projectile = new Projectile();
Point2D velocity = AffineTransform.getRotateInstance(angleRad).transform(
new Point2D.Double(1, 0), null);
velocity.setLocation(velocity.getX() * power * 0.5, velocity.getY() * power * 0.5);
projectile.setVelocity(velocity);
long prevTime = System.nanoTime();
while (projectile.getPosition().getY() >= 0) {
long currentTime = System.nanoTime();
double dt = 3 * (currentTime - prevTime) / 1e8;
projectile.performTimeStep(dt);
prevTime = currentTime;
paintingComponent.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
projectile = null;
paintingComponent.repaint();
}
Projectile getProjectile() {
return projectile;
}
}
class Projectile {
private final Point2D ACCELERATION = new Point2D.Double(0, -9.81 * 0.1);
private final Point2D position = new Point2D.Double();
private final Point2D velocity = new Point2D.Double();
public Point2D getPosition() {
return new Point2D.Double(position.getX(), position.getY());
}
public void setPosition(Point2D point) {
position.setLocation(point);
}
public void setVelocity(Point2D point) {
velocity.setLocation(point);
}
void performTimeStep(double dt) {
scaleAddAssign(velocity, dt, ACCELERATION);
scaleAddAssign(position, dt, velocity);
}
private static void scaleAddAssign(Point2D result, double factor, Point2D addend) {
double x = result.getX() + factor * addend.getX();
double y = result.getY() + factor * addend.getY();
result.setLocation(x, y);
}
}
class ProjectileShooterPanel extends JPanel {
private final ProjectileShooter projectileShooter;
public ProjectileShooterPanel(ProjectileShooter projectileShooter) {
this.projectileShooter = projectileShooter;
}
#Override
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
Projectile projectile = projectileShooter.getProjectile();
if (projectile != null) {
g.setColor(Color.RED);
Point2D position = projectile.getPosition();
int x = (int) position.getX();
int y = getHeight() - (int) position.getY();
Ellipse2D.Double gerd = new Ellipse2D.Double(x - 01, y - 10, 20, 20);
g.draw(gerd);
// g.fillOval(x-01, y-10, 20, 20);
}
Rectangle hadaf1 = new Rectangle(450, 450, 50, 50);
Rectangle hadaf2 = new Rectangle(500, 450, 50, 50);
Rectangle hadaf3 = new Rectangle(475, 400, 50, 50);
g.draw(hadaf1);
g.draw(hadaf2);
g.draw(hadaf3);
}
}
I will be so thankful.

Adding more JComponents to JFrame

Single keys (in code KeyboardButtons) extends JComponent. When i'm trying to add the to main JFrame, i can do that for single key, when trying to add another one, the first one is not showing.
Can you please look at the code a tell me where the problem is?
MainFrame.java:
package keyboard;
import java.awt.*;
import javax.swing.JFrame;
public class MainFrame extends JFrame {
public MainFrame() throws HeadlessException {
setTitle("Keyboard");
setSize(1024, 768);
}
public static void main(String[] args) throws InterruptedException {
JFrame mainWindow = new MainFrame();
mainWindow.setDefaultCloseOperation(EXIT_ON_CLOSE);
Point left5 = new Point(210, 210);
Point left4 = new Point(410, 110);
Point left3 = new Point(580, 120);
Point left2 = new Point(680, 200);
Point left1 = new Point(800, 500);
Keyboard kb = new Keyboard(left1, left2, left3, left4, left5);
KeyboardButton[] buttons = kb.registerKeys();
Container c = mainWindow.getContentPane();
c.add(buttons[0]);
c.add(buttons[1]);
mainWindow.setVisible(true);
}
}
KeyboardButton.java:
package keyboard;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.*;
import javax.swing.JComponent;
public class KeyboardButton extends JComponent implements MouseListener {
Polygon polygon;
boolean isActive;
final Color ACTIVE_COLOR = Color.red;
final Color INACTIVE_COLOR = Color.blue;
public KeyboardButton(Polygon p) {
polygon = p;
addMouseListener(this);
}
private void checkMousePosition(MouseEvent e) {
if (polygon.contains(e.getX(), e.getY())) {
setState(true);
}
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
checkMousePosition(e);
System.out.println(this + " pressed");
}
public void mouseReleased(MouseEvent e) {
setState(false);
System.out.println(this + " released");
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(isActive ? ACTIVE_COLOR : INACTIVE_COLOR);
g.drawPolygon(polygon);
}
void setState(boolean state) {
isActive = state;
repaint();
}
}
Keyboard.java:
package keyboard;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import javax.swing.JComponent;
public class Keyboard extends JComponent {
Point[] leftFingers;
Point leftCenter = new Point(300, 600);
public Keyboard(Point left1, Point left2, Point left3, Point left4, Point left5) {
leftFingers = new Point[5];
leftFingers[0] = left1;
leftFingers[1] = left2;
leftFingers[2] = left3;
leftFingers[3] = left4;
leftFingers[4] = left5;
}
public KeyboardButton[] registerKeys() {
Polygon[] polygons = generateKeyPolygons(calculateBordersOfKeys(calculateCentersBetweenEachTwoFingers(leftFingers)));
KeyboardButton[] buttons = new KeyboardButton[5];
for (int i = 0; i < polygons.length; i++) {
buttons[i] = new KeyboardButton(polygons[i]);
}
return buttons;
}
private Point[] calculateBordersOfKeys(Point[] fingers) {
Point[] centers = calculateCentersBetweenEachTwoFingers(fingers);
Point[] result = new Point[6];
result[0] = calculateCentralSymmetry(centers[0], fingers[0]);
System.arraycopy(centers, 0, result, 1, centers.length);
result[5] = calculateCentralSymmetry(centers[3], fingers[4]);
return result;
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);
g.drawOval(leftCenter.x - 25, leftCenter.y - 25, 50, 50);
for (int i = 0; i < leftFingers.length; i++) {
g.drawOval(leftFingers[i].x, leftFingers[i].y, 10, 10);
}
}
private Polygon[] generateKeyPolygons(Point[] borders) {
Polygon[] polygons = new Polygon[5];
for (int i = 0; i < borders.length - 1; i++) {
Polygon p = new Polygon();
p.addPoint(leftCenter.x, leftCenter.y);
p.addPoint(borders[i].x, borders[i].y);
p.addPoint(borders[i + 1].x, borders[i + 1].y);
polygons[i] = p;
}
return polygons;
}
private Point[] calculateCentersBetweenEachTwoFingers(Point[] fingers) {
Point[] centers = new Point[4];
for (int i = 0; i < fingers.length - 1; i++) {
centers[i] = new Point(((fingers[i].x + fingers[i + 1].x) / 2), ((fingers[i].y + fingers[i + 1].y) / 2));
}
return centers;
}
private Point calculateCentralSymmetry(Point toReflected, Point center) {
Point reflection = new Point();
if (toReflected.x > center.x) {
reflection.x = center.x - Math.abs(center.x - toReflected.x);
} else {
reflection.x = center.x + Math.abs(center.x - toReflected.x);
}
if (toReflected.y > center.y) {
reflection.y = center.y - Math.abs(center.y - toReflected.y);
} else {
reflection.y = center.y + Math.abs(center.y - toReflected.y);
}
return reflection;
}
}
Try to use another LayoutManager, or for what it seems to me it looks like you are trying to manualy paint shapes on the screen, i'd suggest painting them all on one layer. (have one JComponent's paintComponent() method which calls to KeyboardButton.paint() and other painting methods, then you can just add that one JComponent)

Categories

Resources