How to implement a physics engine to jump in game [duplicate] - java

This question already has an answer here:
Implementing gravity in simple 2d game
(1 answer)
Closed 7 years ago.
I need to use a physics engine or something similar to achieve a downward parabolic jump in the game that I have. Right now, the up arrow will just cause the character to float, but I need him to jump.
Here is my code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Level extends JPanel {
public static String chicken = "chicken.gif";
public static String chicken2 = "chicken2.gif";
private static final int PREF_W = 1440;
private static final int PREF_H = 500;
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
private static final String KEY_RELEASE = "key release";
public static final int TRANSLATE_SCALE = 3;
public static final String back = "back.gif";
public static final String corn = "corn.gif";
private EnumMap<Direction, Boolean> dirMap = new EnumMap<Level.Direction, Boolean>(
Direction.class);
private BufferedImage image = null;
private BufferedImage image2 = null;
private BufferedImage image3 = null;
private static int imgX = 20;
private static int imgY = 320;
private static Random rand = new Random();
private static int cImgX = rand.nextInt(1420) + 10;
private static int cImgY = 335;
private static int cornCounter = 0;
private static String Counter = Integer.toString(cornCounter);
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private int bgStringX;
public Level() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
try {
File bback = new File(back);
image2 = ImageIO.read(bback);
File ccorn = new File(corn);
image3 = ImageIO.read(ccorn);
File img = new File(chicken);
image = ImageIO.read(img);
} catch (IOException e) {
e.printStackTrace();
}
new Timer(TIMER_DELAY, new TimerListener()).start();
// here we set up our key bindings
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
// for the key down key stroke
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0,
false);
inputMap.put(keyStroke, dir.name() + KEY_DOWN);
actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
// for the key release key stroke
keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyStroke, dir.name() + KEY_RELEASE);
actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, false);
}
});
}
FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT);
int w = fontMetrics.stringWidth(Counter);
bgStringX = (PREF_W - w) / 2;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (image != null) {
g.drawImage(image2, 0, 0, this);
validate();
g.drawImage(image3, cImgX, cImgY, this);
validate();
g.drawImage(image, imgX, imgY, this);
validate();
}
g.setFont(BG_STRING_FONT);
g.setColor(Color.BLACK);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString(Counter, bgStringX, 190);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
imgX += dir.getX() * TRANSLATE_SCALE;
imgY += dir.getY() * TRANSLATE_SCALE;
//System.out.println(imgY);
if(imgX >= 188 && imgX <= 380) {
if(imgY <= 190 && imgY >= 199) {
imgY = 194;
}
}
if (imgX >= (cImgX - 10) && imgX <= (cImgX + 10)) {
cImgX = rand.nextInt(1420) + 10;
repaint();
validate();
cornCounter += 1;
Counter = Integer.toString(cornCounter);
//System.out.println(cornCounter);
repaint();
validate();
}
if(imgX <= -60) {
imgX = 1373;
repaint();
validate();
}
else if(imgX >= 1442) {
imgX = 1;
repaint();
validate();
}
if(imgY >= 320) {
imgY = 320;
}
else if(imgY <= 1) {
imgY = 1;
}
}
}
repaint();
};
}
enum Direction {
Up(KeyEvent.VK_UP, 0, -1),
Left(KeyEvent.VK_A, -1, 0), Right(KeyEvent.VK_D, 1, 0),
LeftA(KeyEvent.VK_LEFT, -1, 0), RightA(KeyEvent.VK_RIGHT, 1, 0),
Down(KeyEvent.VK_DOWN, 0, 1);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getKeyCode() {
return keyCode;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
Level mainPanel = new Level();
JFrame frame = new JFrame("Cockadoodle Duty: Awakening");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGui();
}
});
}
}

That would be an overkill if you just want to achieve a simple jumping motion.
int vy = 0; //velocity of y
//.... heres your update code:
y += vy; //y is the yposition of your image or character
vy += GRAVITY; //GRAVITY is some arbitrary positive value
//...here's your onJump method:
vy = jumpSpeed; //jumpSpeed is some arbitrary negative value

Related

How to make an animated number counter using Java Swing?

I want to make an animated numeric counter, like this one:
I want to be able to input the value and have the counter update with animation.
I can find how to do this on Android from Google, but I cannot find any information on how to make it in Java Swing. How would I make something like this in Swing?
This isn't a complete answer, but this is a working example of a sliding JPanel. This code could be modified to create the display in the question.
Here's the complete runnable example.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SlidingDigitGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new SlidingDigitGUI());
}
private SlidingPanel secondPanel;
#Override
public void run() {
JFrame frame = new JFrame("Sliding Digit");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createSlidingPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Animation animation = new Animation();
new Thread(animation).start();
}
public JPanel createSlidingPanel() {
String[] digitValues = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 4, false));
panel.setPreferredSize(new Dimension(300, 100));
Font font = panel.getFont();
Font derivedFont = font.deriveFont(Font.BOLD, 48F);
secondPanel = new SlidingPanel(digitValues, derivedFont);
secondPanel.setPanelValue("0");
panel.add(secondPanel);
return panel;
}
public class SlidingPanel extends JPanel {
private static final long serialVersionUID = 661553022861652947L;
private static final int MARGIN = 4;
private int imageY;
private BufferedImage slidingImage;
private Dimension characterDimension;
private final Font font;
private String currentValue;
private final String[] panelValues;
public SlidingPanel(String[] panelValues, Font font) {
this.panelValues = panelValues;
this.font = font;
this.characterDimension = calculateFontSize();
this.slidingImage = generateSlidingImage();
this.setPreferredSize(characterDimension);
}
private Dimension calculateFontSize() {
int maxWidth = 0;
int maxHeight = 0;
FontRenderContext frc = new FontRenderContext(null, true, true);
for (String s : panelValues) {
Rectangle2D r2D = font.getStringBounds(s, frc);
int rWidth = (int) Math.round(r2D.getWidth());
int rHeight = (int) Math.round(r2D.getHeight());
maxWidth = Math.max(maxWidth, rWidth);
maxHeight = Math.max(maxHeight, rHeight);
}
return new Dimension(maxWidth, maxHeight);
}
private BufferedImage generateSlidingImage() {
int height = calculateStringHeight() * (panelValues.length + 1);
BufferedImage slidingImage = new BufferedImage(characterDimension.width,
height, BufferedImage.TYPE_INT_RGB);
Graphics g = slidingImage.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, characterDimension.width, height);
g.setColor(Color.BLACK);
g.setFont(font);
int y = characterDimension.height - MARGIN;
for (String s : panelValues) {
g.drawString(s, 0, y);
y += calculateStringHeight();
}
g.drawString(panelValues[0], 0, y);
g.dispose();
return slidingImage;
}
public void setPanelValue(String value) {
int index = getValueIndex(value);
this.currentValue = value;
this.imageY = calculateStringHeight() * index;
repaint();
}
public void updatePanelValue(String value) {
if (!currentValue.equals(value)) {
int index = getValueIndex(value);
int finalY = calculateStringHeight() * index;
SliderAnimation sliderAnimation = new SliderAnimation(imageY, finalY);
new Thread(sliderAnimation).start();
this.currentValue = value;
}
}
private int getValueIndex(String value) {
for (int index = 0; index < panelValues.length; index++) {
if (value.equals(panelValues[index])) {
return index;
}
}
return -1;
}
private int calculateStringHeight() {
return characterDimension.height + MARGIN;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage subImage = slidingImage.getSubimage(0, imageY,
characterDimension.width,
characterDimension.height);
g.drawImage(subImage, 0, 0, this);
}
public class SliderAnimation implements Runnable {
private int originalY;
private int finalY;
public SliderAnimation(int originalY, int finalY) {
this.originalY = originalY;
this.finalY = finalY;
}
#Override
public void run() {
int differenceY = finalY - originalY;
if (finalY == 0) {
differenceY = characterDimension.height + MARGIN;
}
int steps = 10;
double difference = (double) differenceY / steps;
for (int index = 1; index <= steps; index++) {
imageY = (int) Math.round(difference * index + originalY);
update();
sleep(120L);
}
if (finalY == 0) {
imageY = 0;
update();
} else {
imageY = finalY;
}
}
private void update() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SlidingPanel.this.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Animation implements Runnable {
#Override
public void run() {
while (true) {
update("3");
sleep(2000L);
update("8");
sleep(2000L);
}
}
private void update(final String value) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
secondPanel.updatePanelValue(value);
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Animation {
boolean loop=true;//true //loop
static int start=1;//1 //start
static int end=10+1;//20 // end + 1
int delay1=1000;//1s // delay
int delay2=1;//1ms // delay
Font font=new Font("Helvetica",Font.BOLD,25); //Font
Timer timer=new Timer(delay1, e -> move());JPanel panel = new JPanel();int[] size = {50,100};Point point = new Point(210,size[1]);
Timer timer1=null;int value=start-1;int i;
public static void main(String[] args) {if (start!=end) {new Animation();}}
public void move() {
timer.stop();value ++;
if (!loop && value==end) {timer.stop();}i=0;
timer1 = new Timer(delay2, e -> {
if (i==100) {
timer1.stop();
timer.start();
}
point.setLocation(point.getX(), point.getY()-1);
panel.setLocation(point);
panel.revalidate();
i++;
});timer1.start();
if (loop && value==end-1) {point.setLocation(point.getX(),size[1]+size[1]);value = start-1;}else if (value==end-1 && !loop){System.exit(0);}
}
public Animation() {
JFrame frame = new JFrame();frame.setSize(500,300);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(panel);frame.setLocationRelativeTo(null);
panel.setLocation(point);frame.setLayout(null);frame.revalidate();panel.setSize(new Dimension(size[0],size[1]*Math.abs(end-(start-1))));
panel.setLayout(new GridLayout(Math.abs(end-start)+start,1));if (!(end-start<0)) {for (int i=start;i!=(end-start)+start;i++) {JLabel la = new JLabel(String.valueOf(i));la.setFont(font);panel.add(la);
}}timer.start();frame.revalidate();frame.setVisible(true);
}
}

Java 2D Platformer Collision Detection for multiple Rectangles

I am working on a 2D platformer in Java for an assignment. The assignment specifies I must use an abstract shape class to draw shapes. The problem I am having is getting my collision detection to work with multiple Rectangle Objects which I am using as platforms - I store these in a list. At the moment my collision detection will move my player regardless of which platform I have collided with, so if I collide with a platform from the right it will move me to the top of that platform because it's still checking another platform below me, and will assume I've hit that, therefore moving me to the top of the platform. I was wondering how I can change this so that my collision detection also detects which platform I have collided with and collide relative to that platform (as opposed to every platform).
Here's what is currently happening:
Expected Output:
My player should stop where it is when colliding with platforms.
My App class:
package A2;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class App extends JFrame {
public App() {
final Player player = new Player(200, 100);
final ArrayList<Rectangle> platforms = new ArrayList<>();
platforms.add(new Rectangle(100, 500, 400 ,10));
platforms.add(new Rectangle(500, 100, 10 ,400));
JPanel mainPanel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
player.draw(g);
for(Rectangle r : platforms){
r.draw(g);
}
}
};
mainPanel.addKeyListener(new InputControl(this, player, platforms));
mainPanel.setFocusable(true);
add(mainPanel);
setLayout(new GridLayout());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(600, 600);
}
public static void main(String[] args) {
App app = new App();
app.setVisible(true);
}
}
abstract class Shape {
public void draw(Graphics g) { }
}
My input handling class:
package A2;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import static java.awt.event.KeyEvent.*;
public class InputControl implements ActionListener, KeyListener {
App app;
Player player;
Timer time = new Timer(5, this);
ArrayList<Rectangle> platforms;
ArrayList<Integer> keyList = new ArrayList<>();
boolean keyReleased = false;
boolean keyPressed = false;
boolean[] keyUp = new boolean[256];
boolean[] keyDown = new boolean[256];
boolean topCollision = false;
boolean rightCollision = false;
boolean leftCollision = false;
boolean botCollision = false;
boolean onGround = false;
private static final double GRAVITY = 2;
private static final int MAX_FALL_SPEED = 5;
double Xoverlap = 0;
double Yoverlap = 0;
boolean collision = false;
public InputControl(App app, Player player, ArrayList<Rectangle> platforms) {
this.app = app;
this.player = player;
this.platforms = platforms;
time.start();
}
public void gravity(){
if(player.yVelocity > MAX_FALL_SPEED){
player.yVelocity = MAX_FALL_SPEED;
}
player.yVelocity += GRAVITY;
}
public void momentum(){}
public void actionPerformed(ActionEvent e) {
Rectangle2D offset = player.getOffsetBounds();
for(int i = 0; i < platforms.size(); i++) {
Rectangle r = platforms.get(i);
//If collision
if (offset.intersects(r.obj.getBounds2D())) {
//Collision on Y axis
if (offset.getX() + offset.getWidth() > r.obj.getX() &&
offset.getX() < r.obj.getX() + r.obj.getWidth() &&
offset.getY() + offset.getHeight() > r.obj.getY() &&
offset.getY() + player.yVelocity < r.obj.getY() + r.obj.getHeight()) {
player.y -= (offset.getY() + offset.getHeight()) - r.obj.getY();
}
//Collision on X axis
if (offset.getX() + offset.getWidth() + player.xVelocity > r.obj.getX() &&
offset.getX() + player.xVelocity < r.obj.getX() + r.obj.getWidth() &&
offset.getY() + offset.getHeight() > r.obj.getY() &&
offset.getY() < r.obj.getY() + r.obj.getHeight()) {
player.x -= (offset.getX() + offset.getHeight()) - r.obj.getX();
}
}
else {
player.x += player.xVelocity;
player.y += player.yVelocity;
}
}
player.x += player.xVelocity;
player.y += player.yVelocity;
app.repaint();
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() >= 0 && e.getKeyCode() < 256) {
keyDown[e.getKeyCode()] = true;
keyUp[e.getKeyCode()] = false;
keyPressed = true;
keyReleased = false;
}
if (keyDown[VK_UP]) {
player.yVelocity = -1;
}
if (keyDown[VK_RIGHT]){
player.xVelocity = 1;
}
if (keyDown[VK_LEFT]) {
player.xVelocity = -1;
}
if (keyDown[VK_DOWN]) {
player.yVelocity = 1;
}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() >= 0 && e.getKeyCode() < 256) {
keyDown[e.getKeyCode()] = false;
keyUp[e.getKeyCode()] = true;
keyPressed = false;
keyReleased = true;
}
if(keyUp[VK_RIGHT] || keyUp[VK_LEFT]){
player.xVelocity = 0;
}
if(keyUp[VK_UP]){
player.yVelocity = 0;
}
}
public void keyTyped(KeyEvent e) { }
}
My Player class:
package A2;
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class Player extends Shape {
public double xVelocity;
public double yVelocity;
public double x;
public double y;
public double width = 60;
public double height = 60;
public double weight = 5;
public Rectangle2D obj;
public Player(double x, double y) {
this.x = x;
this.y = y;
obj = new Rectangle2D.Double(x, y, width, height);
}
public Rectangle2D getOffsetBounds(){
return new Rectangle2D.Double( x, y, width, height);
}
public void draw(Graphics g) {
Color c =new Color(1f,0f,0f,0.2f );
obj = new Rectangle2D.Double(x, y, width, height);
g.setColor(Color.BLACK);
Graphics2D g2 = (Graphics2D)g;
g2.fill(obj);
g.drawString("(" + String.valueOf(x) + ", " + String.valueOf(y) + ")", 10, 20);
g.drawString("X Speed: " + String.valueOf(xVelocity) + " Y Speed: " + String.valueOf(yVelocity) + ")", 10, 35);
g.setColor(c);
}
}
My Rectangle class:
package A2;
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class Rectangle extends Shape {
public double width;
public double height;
public double x;
public double y;
boolean hasCollided = false;
public Rectangle2D obj;
public Rectangle(double x, double y, double width, double height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
obj = new Rectangle2D.Double(x, y, width, height);
}
public void draw(Graphics g) {
g.setColor(Color.ORANGE);
Graphics2D g2 = (Graphics2D)g;
g2.fill(obj);
}
}
First, define ColorRectangle class that extends Shape and provide logic for drawing:
// extends java.awt.Shape
abstract class ColorRectangle extends Rectangle {
private static final long serialVersionUID = -3626687047605407698L;
private final Color color;
protected ColorRectangle(int x, int y, int width, int height, Color color) {
super(x, y, width, height);
this.color = color;
}
public void draw(Graphics2D g) {
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
Second, define separate class for represent Player and Platform:
public final class Player extends ColorRectangle {
private static final long serialVersionUID = -3909362955417024742L;
public Player(int width, int height, Color color) {
super(0, 0, width, height, color);
}
}
public final class Platform extends ColorRectangle {
private static final long serialVersionUID = 6602359551348037628L;
public Platform(int x, int y, int width, int height, Color color) {
super(x, y, width, height, color);
}
public boolean intersects(Player player, int offsX, int offsY) {
return intersects(player.x + offsX, player.y + offsY, player.width, player.height);
}
}
Third, define class that contains logic of demo application. Actually, you could split logic for demo and logic for board (including actiona and key listeners), but these classes are very simple, so in given case, no need to split it.
public class CollisionDetectionDemo extends JFrame {
public CollisionDetectionDemo() {
setLayout(new GridLayout());
add(new MainPanel());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(600, 600);
}
private static class MainPanel extends JPanel implements ActionListener, KeyListener {
private static final long serialVersionUID = 8771401446680969350L;
private static final int OFFS = 5;
private final Player player = new Player(60, 60, Color.BLACK);
private final List<Platform> platforms = Arrays.asList(
new Platform(100, 500, 400, 10, Color.ORANGE),
new Platform(500, 100, 10, 410, Color.RED),
new Platform(150, 300, 100, 10, Color.BLUE),
new Platform(150, 100, 100, 10, Color.GREEN));
private MainPanel() {
player.x = 200;
player.y = 200;
new Timer(5, this).start();
setFocusable(true);
addKeyListener(this);
}
private void drawPlayerPosition(Graphics g) {
g.setColor(Color.BLACK);
g.drawString(String.format("(%d, %d)", player.x, player.y), 10, 20);
}
private void movePlayer(int offsX, int offsY) {
if (!intersects(player, offsX, offsY)) {
player.x += offsX;
player.y += offsY;
}
}
private boolean intersects(Player player, int offsX, int offsY) {
for (Platform platform : platforms)
if (platform.intersects(player, offsX, offsY))
return true;
return false;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Color color = g.getColor();
drawPlayerPosition(g);
player.draw((Graphics2D)g);
platforms.forEach(platform -> platform.draw((Graphics2D)g));
g.setColor(color);
}
#Override
public void actionPerformed(ActionEvent event) {
repaint();
}
#Override
public void keyTyped(KeyEvent event) {
}
#Override
public void keyPressed(KeyEvent event) {
if (event.getKeyCode() == VK_UP)
movePlayer(0, -OFFS);
else if (event.getKeyCode() == VK_DOWN)
movePlayer(0, OFFS);
else if (event.getKeyCode() == VK_LEFT)
movePlayer(-OFFS, 0);
else if (event.getKeyCode() == VK_RIGHT)
movePlayer(OFFS, 0);
}
#Override
public void keyReleased(KeyEvent event) {
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new CollisionDetectionDemo().setVisible(true));
}
}

Display X Y coordinates whilst moving Cube in JFrame

I'm learning Java and would really appreciate some help.
I'm trying to get this effect http://tinypic.com/r/339p0ud/8
I using stack overflow 'MOVE CUBE' example:
How Do I Use KeyEventDispatcher
and
stack overflow 'Click coordinate' example:
Using the coordinate plane in the JFrame
I wish to join them together, so that I have a cube I can move around with my keys and
as I do the coordinates show up next to the cube (so the coordinates move with the cube) and change as the position changes.
I would be fascinated to see how this is done as I have tried my best the past week with no success.
thanks in advance for any help or tips you provide here,
Joe
MOVE.java (move the block around with keys)
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Move extends JPanel {
public static final long serialVersionUID = 1L;
public static final String IMAGE_PATH = "http://mathforum.org/alejandre/magic.square/4x4grid.gif";
public static final String IMAGE_PATH_PLAYER = "http://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Square-symbol.svg/50px-Square-symbol.svg.png";
public static final int STEP = 3;
public static final int TIMER_DELAY = STEP * 8;
public BufferedImage bkgrndImage = null;
public BufferedImage playerImage = null;
public Map<Direction, Boolean> directionMap = new HashMap<Direction, Boolean>();
public int playerX = 0;
public int playerY = 0;
enum Direction {
UP(KeyEvent.VK_UP, 0, -1), DOWN(KeyEvent.VK_DOWN, 0, 1),
LEFT(KeyEvent.VK_LEFT, -1, 0), RIGHT(KeyEvent.VK_RIGHT, 1, 0);
public int keyCode;
public int xDirection;
public int yDirection;
private Direction(int keyCode, int xDirection, int yDirection) {
this.keyCode = keyCode;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
public int getKeyCode() {
return keyCode;
}
public int getXDirection() {
return xDirection;
}
public int getYDirection() {
return yDirection;
}
}
public Move() {
try {
URL bkgrdImageURL = new URL(IMAGE_PATH);
URL playerImageURL = new URL(IMAGE_PATH_PLAYER);
bkgrndImage = ImageIO.read(bkgrdImageURL);
playerImage = ImageIO.read(playerImageURL);
setPreferredSize(new Dimension(bkgrndImage.getWidth(), bkgrndImage.getHeight()));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (Direction direction : Direction.values()) {
directionMap.put(direction, false);
}
setKeyBindings();
Timer timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
public void setKeyBindings() {
InputMap inMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actMap = getActionMap();
for (final Direction direction : Direction.values()) {
KeyStroke pressed = KeyStroke.getKeyStroke(direction.getKeyCode(), 0, false);
KeyStroke released = KeyStroke.getKeyStroke(direction.getKeyCode(), 0, true);
inMap.put(pressed, direction.toString() + "pressed");
inMap.put(released, direction.toString() + "released");
actMap.put(direction.toString() + "pressed", new AbstractAction() {
public static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
directionMap.put(direction, true);
}
});
actMap.put(direction.toString() + "released", new AbstractAction() {
public static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
directionMap.put(direction, false);
}
});
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bkgrndImage != null) {
g.drawImage(bkgrndImage, 0, 0, null);
}
if (playerImage != null) {
g.drawImage(playerImage, playerX, playerY, null);
}
}
public class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
boolean moved = false;
for (Direction direction : Direction.values()) {
if (directionMap.get(direction)) {
playerX += STEP * direction.getXDirection();
playerY += STEP * direction.getYDirection();
moved = true;
}
}
if (moved) {
int x = playerX - 2 * STEP;
int y = playerY - 2 * STEP;
int w = playerImage.getWidth() + 4 * STEP;
int h = playerImage.getHeight() + 4 * STEP;
Move.this.repaint(x, y, w, h); // !! repaint just the player
}
}
}
public static void createAndShowUI() {
JFrame frame = new JFrame("MoveIcon");
frame.getContentPane().add(new Move());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowUI();
}
});
}
COORDINATE.java (get x and y position in JFrame)
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Coordinate
{
private int x;
private int y;
private String text;
private DrawingBase canvas;
private void displayGUI()
{
JFrame frame = new JFrame("Drawing Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas = new DrawingBase();
canvas.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent me)
{
text = "X : " + me.getX() + " Y : " + me.getY();
x = me.getX();
y = me.getY();
canvas.setValues(text, x, y);
}
});
frame.setContentPane(canvas);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new Coordinate().displayGUI();
}
});
}
}
class DrawingBase extends JPanel
{
private String clickedAt = "";
private int x = 0;
private int y = 0;
public void setValues(String text, int x, int y)
{
clickedAt = text;
this.x = x;
this.y = y;
repaint();
}
public Dimension getPreferredSize()
{
return (new Dimension(500, 400));
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString(clickedAt, x, y);
}
}
No sure if this is what you want, I removed Coordinate and did everything in one class
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.TileObserver;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.events.MouseEvent;
import org.w3c.dom.views.AbstractView;
public class Move extends JPanel{
public static final long serialVersionUID = 1L;
public static final String IMAGE_PATH = "http://mathforum.org/alejandre/magic.square/4x4grid.gif";
public static final String IMAGE_PATH_PLAYER = "http://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Square-symbol.svg/50px-Square-symbol.svg.png";
public static final int STEP = 3;
public static final int TIMER_DELAY = STEP * 8;
public BufferedImage bkgrndImage = null;
public BufferedImage playerImage = null;
public Map<Direction, Boolean> directionMap = new HashMap<Direction, Boolean>();
public int playerX = 0;
public int playerY = 0;
private static int xPosition = 0;
private static int yPosition = 0;
private String s = "";
private JLabel jlbl1 = new JLabel("");
enum Direction {
UP(KeyEvent.VK_UP, 0, -1), DOWN(KeyEvent.VK_DOWN, 0, 1),
LEFT(KeyEvent.VK_LEFT, -1, 0), RIGHT(KeyEvent.VK_RIGHT, 1, 0);
public int keyCode;
public int xDirection;
public int yDirection;
private Direction(int keyCode, int xDirection, int yDirection) {
this.keyCode = keyCode;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
public int getKeyCode() {
return keyCode;
}
public int getXDirection() {
return xDirection;
}
public int getYDirection() {
return yDirection;
}
}
public Move() {
this.add(jlbl1,new Dimension(100,100));
try {
URL bkgrdImageURL = new URL(IMAGE_PATH);
URL playerImageURL = new URL(IMAGE_PATH_PLAYER);
bkgrndImage = ImageIO.read(bkgrdImageURL);
playerImage = ImageIO.read(playerImageURL);
setPreferredSize(new Dimension(bkgrndImage.getWidth(), bkgrndImage.getHeight()));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (Direction direction : Direction.values()) {
directionMap.put(direction, false);
}
setKeyBindings();
Timer timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
public void setKeyBindings() {
InputMap inMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actMap = getActionMap();
for (final Direction direction : Direction.values()) {
KeyStroke pressed = KeyStroke.getKeyStroke(direction.getKeyCode(), 0, false);
KeyStroke released = KeyStroke.getKeyStroke(direction.getKeyCode(), 0, true);
inMap.put(pressed, direction.toString() + "pressed");
inMap.put(released, direction.toString() + "released");
actMap.put(direction.toString() + "pressed", new AbstractAction() {
public static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
directionMap.put(direction, true);
}
});
actMap.put(direction.toString() + "released", new AbstractAction() {
public static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
directionMap.put(direction, false);
}
});
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bkgrndImage != null) {
g.drawImage(bkgrndImage, 0, 0, null);
}
if (playerImage != null) {
g.drawImage(playerImage, playerX, playerY, null);
}
}
public class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
boolean moved = false;
for (Direction direction : Direction.values()) {
if (directionMap.get(direction)) {
playerX += STEP * direction.getXDirection();
playerY += STEP * direction.getYDirection();
moved = true;
}
}
if (moved) {
int x = playerX - 2 * STEP;
int y = playerY - 2 * STEP;
int w = playerImage.getWidth() + 4 * STEP;
int h = playerImage.getHeight() + 4 * STEP;
Move.this.repaint(x, y, w, h); // !! repaint just the player
}
s = playerX+", "+playerY;
jlbl1.setText(s);
}
}
public static void createAndShowUI() {
JFrame frame = new JFrame("MoveIcon");
frame.getContentPane().add(new Move());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowUI();
}
});
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bkgrndImage != null) {
g.drawImage(bkgrndImage, 0, 0, null);
}
if (playerImage != null) {
g.drawImage(playerImage, playerX, playerY, null);
//offsetX and offsetY are the location relative to the cube to draw the string
//also note that the y coordinate is the location where the BOTTOM of the string begins
g.drawString("Clicked at: " + playerX + "," + playerY + ".", playerX + offsetX, playerY + offsetY)
}
}
This will draw to the location of the player. I can't imagine that it requires much effort to adjust it to draw to the clicked position.

Java keyboard input - game development

I have a specific "problem" with a game I'm creating for class.
The game is an implementation of "Break it". To move the platform at the bottom I just used a key listener. The problem is that after the first key press there is a short "lag" or "stutter" before the platform starts moving. How could I prevent this to get a smooth response? Is there another way than KeyListener? KeyBindings?
Here is the key listener implementation
private class KeyControl implements KeyListener {
private int dx = 20;
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
if(dx < 0 )
dx = -dx;
gamePanel.movePlatform(dx);
}
if(e.getKeyCode() == KeyEvent.VK_LEFT) {
if(dx > 0 )
dx = -dx;
gamePanel.movePlatform(dx);
}
if(e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("space");
gamePanel.play();
}
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
gamePanel.pause();
}
}
}
and here is the method that moves the platform around
public void movePlatform(int dx) {
int nextDX = dx;
if(paused || init) {
dx = 0;
}
// make sure platform doesnt exceed right border
if(platform.getX() + platform.getWidth() + dx> size.getWidth()) {
if(nextDX < 0)
dx = nextDX;
else
dx = 0;
}
// make sure platform doesnt exceed left border
if(platform.getX() + dx <= 0) {
if(nextDX > 0)
dx = nextDX;
else
dx = 0;
}
platform.setFrame(platform.getX() + dx, platform.getY(), platform.getWidth(), platform.getHeight());
platformIntervalX = new Interval((int)platform.getX(), (int)(platform.getX() + platform.getWidth()));
platformIntervalY = new Interval((int)(platform.getY() - platform.getHeight()), (int)platform.getY());
repaint();
}
The solution is not to use the KeyListener's key press for moving your sprite. The key is not to rely on the hardware-specific key press frequency, to use a Swing Timer to create your own frequency. Instead use Key Bindings and a Swing Timer. Start the time on key press and stop it on key release.
For example, run this code and press and release the up-arrow key:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class KeyBindingEg extends JPanel {
private static final String UP_KEY_PRESSED = "up key pressed";
private static final String UP_KEY_RELEASED = "up key released";
private static final int UP_TIMER_DELAY = 50;
private static final Color FLASH_COLOR = Color.red;
private Timer upTimer;
private JLabel label = new JLabel();
public KeyBindingEg() {
label.setFont(label.getFont().deriveFont(Font.BOLD, 32));
label.setOpaque(true);
add(label);
setPreferredSize(new Dimension(400, 300));
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
KeyStroke upKeyPressed = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false);
KeyStroke upKeyReleased = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true);
inputMap.put(upKeyPressed, UP_KEY_PRESSED);
inputMap.put(upKeyReleased, UP_KEY_RELEASED);
actionMap.put(UP_KEY_PRESSED, new UpAction(false));
actionMap.put(UP_KEY_RELEASED, new UpAction(true));
}
private class UpAction extends AbstractAction {
private boolean onKeyRelease;
public UpAction(boolean onKeyRelease) {
this.onKeyRelease = onKeyRelease;
}
#Override
public void actionPerformed(ActionEvent evt) {
if (!onKeyRelease) {
if (upTimer != null && upTimer.isRunning()) {
return;
}
System.out.println("key pressed");
label.setText(UP_KEY_PRESSED);
upTimer = new Timer(UP_TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Color c = label.getBackground();
if (FLASH_COLOR.equals(c)) {
label.setBackground(null);
label.setForeground(Color.black);
} else {
label.setBackground(FLASH_COLOR);
label.setForeground(Color.white);
}
}
});
upTimer.start();
} else {
System.out.println("Key released");
if (upTimer != null && upTimer.isRunning()) {
upTimer.stop();
upTimer = null;
}
label.setText("");
}
}
}
private static void createAndShowGui() {
KeyBindingEg mainPanel = new KeyBindingEg();
JFrame frame = new JFrame("KeyBindingEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Edit
Or a better example, one that moves a sprite in any direction based on a key press of one of the arrow keys. No delay encountered:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
#SuppressWarnings("serial")
public class KeyBindingEg2 extends JPanel {
enum Dir {
LEFT("Left", KeyEvent.VK_LEFT, -1, 0),
RIGHT("Right", KeyEvent.VK_RIGHT, 1, 0),
UP("Up", KeyEvent.VK_UP, 0, -1),
DOWN("Down", KeyEvent.VK_DOWN, 0, 1);
private String name;
private int keyCode;
private int deltaX;
private int deltaY;
private Dir(String name, int keyCode, int deltaX, int deltaY) {
this.name = name;
this.keyCode = keyCode;
this.deltaX = deltaX;
this.deltaY = deltaY;
}
public String getName() {
return name;
}
public int getKeyCode() {
return keyCode;
}
public int getDeltaX() {
return deltaX;
}
public int getDeltaY() {
return deltaY;
}
}
public static final int TIMER_DELAY = 10;
public static final int DELTA_X = 2;
public static final int DELTA_Y = DELTA_X;
public static final int SPRITE_WIDTH = 10;
public static final int SPRITE_HEIGHT = SPRITE_WIDTH;
private static final String PRESSED = "pressed";
private static final String RELEASED = "released";
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private Map<Dir, Boolean> dirMap = new EnumMap<>(Dir.class);
private int spriteX = 0;
private int spriteY = 0;
private BufferedImage sprite;
private Timer animationTimer = new Timer(TIMER_DELAY, new AnimationListener());
public KeyBindingEg2() {
for (Dir dir : Dir.values()) {
dirMap.put(dir, Boolean.FALSE);
}
sprite = createSprite();
setKeyBindings();
animationTimer.start();
}
private BufferedImage createSprite() {
BufferedImage sprt = new BufferedImage(SPRITE_WIDTH, SPRITE_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics g = sprt.getGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, SPRITE_WIDTH, SPRITE_HEIGHT);
g.dispose();
return sprt;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (sprite != null) {
g.drawImage(sprite, spriteX, spriteY, this);
}
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (Dir dir : Dir.values()) {
KeyStroke keyPressed = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, false);
KeyStroke keyReleased = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyPressed, dir.toString() + PRESSED);
inputMap.put(keyReleased, dir.toString() + RELEASED);
actionMap.put(dir.toString() + PRESSED, new DirAction(dir, PRESSED));
actionMap.put(dir.toString() + RELEASED, new DirAction(dir, RELEASED));
}
}
private class AnimationListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int newX = spriteX;
int newY = spriteY;
for (Dir dir : Dir.values()) {
if (dirMap.get(dir)) {
newX += dir.getDeltaX() * DELTA_X;
newY += dir.getDeltaY() * DELTA_Y;
}
}
if (newX < 0 || newY < 0) {
return;
}
if (newX + SPRITE_WIDTH > getWidth() || newY + SPRITE_HEIGHT > getHeight()) {
return;
}
spriteX = newX;
spriteY = newY;
repaint();
}
}
private class DirAction extends AbstractAction {
private String pressedOrReleased;
private Dir dir;
public DirAction(Dir dir, String pressedOrReleased) {
this.dir = dir;
this.pressedOrReleased = pressedOrReleased;
}
#Override
public void actionPerformed(ActionEvent evt) {
if (pressedOrReleased.equals(PRESSED)) {
dirMap.put(dir, Boolean.TRUE);
} else if (pressedOrReleased.equals(RELEASED)) {
dirMap.put(dir, Boolean.FALSE);
}
}
}
private static void createAndShowGui() {
KeyBindingEg2 mainPanel = new KeyBindingEg2();
JFrame frame = new JFrame("KeyBindingEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

How to make an image move while listening to a keypress in Java.

I'm starting to learn java programming and I think it's cool to learn java through game development. I know how to draw image and listen to a keypress then move that image. But is it possible to make the image move back and forth to the window while the window is listening to a keypress? Like for example, while the image or object(like spaceship) is moving left to right in the window, then if I press space key, a laser will fire at the bottom of the screen( cool huh :D ). But basically I just want to know how to make the image move left to right while the window is listening to a keypress.
I'm thinking that I will add a key listener to my window then fire an infinite loop to move the image. Or do I need to learn about threading so that another thread will move the object?
Please advise.
Many thanks.
Yep, a Swing Timer and Key Bindings would work well. Here's another example (mine) :)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class AnimationWithKeyBinding {
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel(); // the drawing JPanel
JFrame frame = new JFrame("Animation With Key Binding");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class AnimationPanel extends JPanel {
public static final int SPRITE_WIDTH = 20;
public static final int PANEL_WIDTH = 400;
public static final int PANEL_HEIGHT = 400;
private static final int MAX_MSTATE = 25;
private static final int SPIN_TIMER_PERIOD = 16;
private static final int SPRITE_STEP = 3;
private int mState = 0;
private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
private int oldMX = mX;
private int oldMY = mY;
private boolean moved = false;
// an array of sprite images that are drawn sequentially
private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
public AnimationPanel() {
// create and start the main animation timer
new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.white);
createSprites(); // create the images
setupKeyBinding();
}
private void setupKeyBinding() {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = getInputMap(condition);
ActionMap actMap = getActionMap();
// this uses an enum of Direction that holds ints for the arrow keys
for (Direction direction : Direction.values()) {
int key = direction.getKey();
String name = direction.name();
// add the key bindings for arrow key and shift-arrow key
inMap.put(KeyStroke.getKeyStroke(key, 0), name);
inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK), name);
actMap.put(name, new MyKeyAction(this, direction));
}
}
// create a bunch of buffered images and place into an array,
// to be displayed sequentially
private void createSprites() {
for (int i = 0; i < spriteImages.length; i++) {
spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = spriteImages[i].createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double theta = i * Math.PI / (2 * spriteImages.length);
double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x2, y2, x1);
g2.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(spriteImages[mState], mX, mY, null);
}
public void incrementX(boolean right) {
oldMX = mX;
if (right) {
mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
} else {
mX = Math.max(0, mX - SPRITE_STEP);
}
moved = true;
}
public void incrementY(boolean down) {
oldMY = mY;
if (down) {
mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
} else {
mY = Math.max(0, mY - SPRITE_STEP);
}
moved = true;
}
public void tick() {
mState = (mState + 1) % MAX_MSTATE;
}
private class SpinTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
tick();
int delta = 20;
int width = SPRITE_WIDTH + 2 * delta;
int height = width;
// make sure to erase the old image
if (moved) {
int x = oldMX - delta;
int y = oldMY - delta;
repaint(x, y, width, height);
}
int x = mX - delta;
int y = mY - delta;
// draw the new image
repaint(x, y, width, height);
moved = false;
}
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private AnimationPanel draw;
private Direction direction;
public MyKeyAction(AnimationPanel draw, Direction direction) {
this.draw = draw;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
draw.incrementY(false);
break;
case DOWN:
draw.incrementY(true);
break;
case LEFT:
draw.incrementX(false);
break;
case RIGHT:
draw.incrementX(true);
break;
default:
break;
}
}
}
Here is another example that uses this sprite sheet:
obtained from this site.
Again it's an example of drawing within a JPanel's paintComponent method and using Key Bindings to tell which direction to move.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mcve3 extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 640;
private static final int TIMER_DELAY = 50;
private int spriteX = 400;
private int spriteY = 320;
private SpriteDirection spriteDirection = SpriteDirection.RIGHT;
private MySprite sprite = null;
private Timer timer = null;
public Mcve3() {
try {
sprite = new MySprite(spriteDirection, spriteX, spriteY);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
setBackground(Color.WHITE);
setKeyBindings(SpriteDirection.LEFT, KeyEvent.VK_LEFT);
setKeyBindings(SpriteDirection.RIGHT, KeyEvent.VK_RIGHT);
setKeyBindings(SpriteDirection.FORWARD, KeyEvent.VK_DOWN);
setKeyBindings(SpriteDirection.AWAY, KeyEvent.VK_UP);
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
private void setKeyBindings(SpriteDirection dir, int keyCode) {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
KeyStroke keyPressed = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke keyReleased = KeyStroke.getKeyStroke(keyCode, 0, true);
inputMap.put(keyPressed, keyPressed.toString());
inputMap.put(keyReleased, keyReleased.toString());
actionMap.put(keyPressed.toString(), new MoveAction(dir, false));
actionMap.put(keyReleased.toString(), new MoveAction(dir, true));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
sprite.draw(g);
}
private class MoveAction extends AbstractAction {
private SpriteDirection dir;
private boolean released;
public MoveAction(SpriteDirection dir, boolean released) {
this.dir = dir;
this.released = released;
}
#Override
public void actionPerformed(ActionEvent e) {
if (released) {
sprite.setMoving(false);
} else {
sprite.setMoving(true);
sprite.setDirection(dir);
}
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (sprite.isMoving()) {
sprite.tick();
}
repaint();
}
}
private static void createAndShowGui() {
Mcve3 mainPanel = new Mcve3();
JFrame frame = new JFrame("MCVE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MySprite {
private static final String SPRITE_SHEET_PATH = "http://"
+ "orig12.deviantart.net/7db3/f/2010/338/3/3/"
+ "animated_sprite_sheet_32x32_by_digibody-d3479l2.gif";
private static final int MAX_MOVING_INDEX = 4;
private static final int DELTA = 4;
private SpriteDirection direction;
private Map<SpriteDirection, Image> standingImgMap = new EnumMap<>(SpriteDirection.class);
private Map<SpriteDirection, List<Image>> movingImgMap = new EnumMap<>(SpriteDirection.class);
private int x;
private int y;
private boolean moving = false;
private int movingIndex = 0;
public MySprite(SpriteDirection direction, int x, int y) throws IOException {
this.direction = direction;
this.x = x;
this.y = y;
createSprites();
}
public void draw(Graphics g) {
Image img = null;
if (!moving) {
img = standingImgMap.get(direction);
} else {
img = movingImgMap.get(direction).get(movingIndex);
}
g.drawImage(img, x, y, null);
}
private void createSprites() throws IOException {
URL spriteSheetUrl = new URL(SPRITE_SHEET_PATH);
BufferedImage img = ImageIO.read(spriteSheetUrl);
// get sub-images (sprites) from the sprite sheet
// magic numbers for getting sprites from sheet, all obtained by trial and error
int x0 = 0;
int y0 = 64;
int rW = 32;
int rH = 32;
for (int row = 0; row < 4; row++) {
SpriteDirection dir = SpriteDirection.values()[row];
List<Image> imgList = new ArrayList<>();
movingImgMap.put(dir, imgList);
int rY = y0 + row * rH;
for (int col = 0; col < 5; col++) {
int rX = x0 + col * rW;
BufferedImage subImg = img.getSubimage(rX, rY, rW, rH);
if (col == 0) {
// first image is standing
standingImgMap.put(dir, subImg);
} else {
// all others are moving
imgList.add(subImg);
}
}
}
}
public SpriteDirection getDirection() {
return direction;
}
public void setDirection(SpriteDirection direction) {
if (this.direction != direction) {
setMoving(false);
}
this.direction = direction;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isMoving() {
return moving;
}
public void setMoving(boolean moving) {
this.moving = moving;
if (!moving) {
movingIndex = 0;
}
}
public void tick() {
if (moving) {
switch (direction) {
case RIGHT:
x += DELTA;
break;
case LEFT:
x -= DELTA;
break;
case FORWARD:
y += DELTA;
break;
case AWAY:
y -= DELTA;
}
movingIndex++;
movingIndex %= MAX_MOVING_INDEX;
}
}
public int getMovingIndex() {
return movingIndex;
}
public void setMovingIndex(int movingIndex) {
this.movingIndex = movingIndex;
}
}
enum SpriteDirection {
FORWARD, LEFT, AWAY, RIGHT
}
As an alternative to KeyListener, consider using actions and key bindings, discussed here. Derived from this example, the program below moves a line left, down, up or right using either buttons or keys.
import java.awt.BasicStroke;
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.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* #see https://stackoverflow.com/questions/6991648
* #see https://stackoverflow.com/questions/6887296
* #see https://stackoverflow.com/questions/5797965
*/
public class LinePanel extends JPanel {
private MouseHandler mouseHandler = new MouseHandler();
private Point p1 = new Point(100, 100);
private Point p2 = new Point(540, 380);
private boolean drawing;
public LinePanel() {
this.setPreferredSize(new Dimension(640, 480));
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(8,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
drawing = true;
p1 = e.getPoint();
p2 = p1;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
p2 = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (drawing) {
p2 = e.getPoint();
repaint();
}
}
}
private class ControlPanel extends JPanel {
private static final int DELTA = 10;
public ControlPanel() {
this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
}
private class MoveButton extends JButton {
KeyStroke k;
int dx, dy;
public MoveButton(String name, int code, final int dx, final int dy) {
super(name);
this.k = KeyStroke.getKeyStroke(code, 0);
this.dx = dx;
this.dy = dy;
this.setAction(new AbstractAction(this.getText()) {
#Override
public void actionPerformed(ActionEvent e) {
LinePanel.this.p1.translate(dx, dy);
LinePanel.this.p2.translate(dx, dy);
LinePanel.this.repaint();
}
});
ControlPanel.this.getInputMap(
WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
MoveButton.this.doClick();
}
});
}
}
}
private void display() {
JFrame f = new JFrame("LinePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.add(new ControlPanel(), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new LinePanel().display();
}
});
}
}
But basically I just want to know how to make the image move left to right while the window is listening to a keypress
You can use a Swing Timer to animate an image:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerAnimation extends JLabel implements ActionListener
{
int deltaX = 2;
int deltaY = 3;
int directionX = 1;
int directionY = 1;
public TimerAnimation(
int startX, int startY,
int deltaX, int deltaY,
int directionX, int directionY,
int delay)
{
this.deltaX = deltaX;
this.deltaY = deltaY;
this.directionX = directionX;
this.directionY = directionY;
setIcon( new ImageIcon("dukewavered.gif") );
// setIcon( new ImageIcon("copy16.gif") );
setSize( getPreferredSize() );
setLocation(startX, startY);
new javax.swing.Timer(delay, this).start();
}
public void actionPerformed(ActionEvent e)
{
Container parent = getParent();
// Determine next X position
int nextX = getLocation().x + (deltaX * directionX);
if (nextX < 0)
{
nextX = 0;
directionX *= -1;
}
if ( nextX + getSize().width > parent.getSize().width)
{
nextX = parent.getSize().width - getSize().width;
directionX *= -1;
}
// Determine next Y position
int nextY = getLocation().y + (deltaY * directionY);
if (nextY < 0)
{
nextY = 0;
directionY *= -1;
}
if ( nextY + getSize().height > parent.getSize().height)
{
nextY = parent.getSize().height - getSize().height;
directionY *= -1;
}
// Move the label
setLocation(nextX, nextY);
}
public static void main(String[] args)
{
JPanel panel = new JPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().setLayout(null);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
frame.getContentPane().add( new TimerAnimation(300, 100, 3, 2, -1, 1, 20) );
// frame.getContentPane().add( new TimerAnimation(0, 000, 5, 0, 1, 1, 20) );
frame.getContentPane().add( new TimerAnimation(0, 200, 5, 0, 1, 1, 80) );
frame.setSize(400, 400);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
// frame.getContentPane().add( new TimerAnimation(10, 10, 3, 0, 1, 1, 10) );
}
}
You can add a KeyListener to the panel and it will operate independently of the image animation.

Categories

Resources