I have the following code to show you:
public class Test extends JPanel implements ActionListener, KeyListener
{
Timer tm = new Timer(5, this);
int x = 0, y = 0, velX = 0, velY = 0;
public Test()
{
tm.start(); //starts the timer
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paint(Graphics g)
{
super.paint(g);
ImageIcon s = new ImageIcon("C:\\Users\\Owner\\Pictures\\Stick.jpg");
s.paintIcon(this,g,x,y);
}
public void actionPerformed(ActionEvent e)
{
if (x < 0)
{
velX = 0;
x = 0;
}
if (x > 630)
{
velX = 0;
x = 630;
}
if(y < 0)
{
velY = 0;
y = 0;
}
if(y > 430)
{
velY = 0;
y = 430;
}
x = x + velX;
y = y + velY;
repaint();
}
public void keyPressed(KeyEvent e)
{
int c = e.getKeyCode();
if (c == KeyEvent.VK_LEFT)
{
velX = -1;
velY = 0;
}
if(c == KeyEvent.VK_UP)
{
velX = 0;
velY = -1;
}
if(c == KeyEvent.VK_RIGHT)
{
velX = 1;
velY = 0;
}
if(c == KeyEvent.VK_DOWN)
{
velX = 0;
velY = 1;
}
}
public void keyTyped(KeyEvent e){}
public void keyReleased(KeyEvent e)
{
velX = 0;
velY = 0;
}
public static void main(String[] args)
{
Test t = new Test();
JFrame jf = new JFrame();
jf.setTitle("Tutorial");
jf.setSize(700, 600);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(t);
jf.setVisible(true);
}
My problem is I whenever the user holds the right arrow on the keyboard it changes an image, when the user lets go it goes back the the default image. Please tell me how to do that. I think it is a series of if statements in the Graphics class then calling them to the key input but I'm not quite sure. I am also using Eclipse. Thank You.
Override paintComponent instead of paint. See Performing Custom Painting and Painting in AWT and Swing for more details
Use the key bindings API instead of KeyListener, it will cause you less issues. See How to Use Key Bindings for more details
Essentially, you could just have a Image as a class instance field, which was painted by the paintComponent method. When the key was pressed, you would change the image to the "move image" and when it was released, change it back to the "default image"
Updated with example
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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 interface Mover {
public enum Direction {
LEFT, RIGHT, NONE;
}
public void setDirection(Direction direction);
public Direction getDirection();
}
public class TestPane extends JPanel implements Mover {
private BufferedImage left;
private BufferedImage right;
private BufferedImage stand;
private BufferedImage current;
private Direction direction = Direction.NONE;
private int xPos;
private int yPos;
public TestPane() {
try {
left = ImageIO.read(getClass().getResource("/Left.png"));
right = ImageIO.read(getClass().getResource("/Right.png"));
stand = ImageIO.read(getClass().getResource("/Stand.png"));
current = stand;
xPos = 100 - (current.getWidth() / 2);
yPos = 100 - (current.getHeight() / 2);
} catch (IOException exp) {
exp.printStackTrace();
}
bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "move.left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), new MoveAction(this, Direction.LEFT));
bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "stop.left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), new MoveAction(this, Direction.NONE));
bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "move.right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), new MoveAction(this, Direction.RIGHT));
bindKeyStrokeTo(WHEN_IN_FOCUSED_WINDOW, "stop.right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), new MoveAction(this, Direction.NONE));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updatePosition();
repaint();
}
});
timer.start();
}
protected void bindKeyStrokeTo(int condition, String name, KeyStroke keyStroke, Action action) {
InputMap im = getInputMap(condition);
ActionMap am = getActionMap();
im.put(keyStroke, name);
am.put(name, action);
}
#Override
public Direction getDirection() {
return direction;
}
#Override
public void setDirection(Direction direction) {
this.direction = direction;
}
protected void updatePosition() {
switch (getDirection()) {
case LEFT:
current = left;
xPos -= 1;
break;
case RIGHT:
current = right;
xPos += 1;
break;
case NONE:
current = stand;
break;
}
if (xPos < 0) {
xPos = 0;
current = stand;
} else if (xPos + current.getWidth() > getWidth()) {
current = stand;
xPos = getWidth() - current.getWidth();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(current, xPos, yPos, this);
g2d.dispose();
}
}
public class MoveAction extends AbstractAction {
private Mover mover;
private Mover.Direction direction;
public MoveAction(Mover mover, Mover.Direction direction) {
this.mover = mover;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
mover.setDirection(direction);
}
}
}
Related
I'm trying to make a racing game with the top down view on a static player in the middle of the screen, so instead of moving the player through the map, the map would move around the player. Since it's a racing game, I wanted it to also be somewhat similar to a car, but I've been having trouble with rotating the map around the player and having that work with translations.
I've tried keeping track of the center by adding or subtracting from it, which is what I did for the translations, but it doesn't work with the rotate method. The rotate function wouldn't rotate about the player and instead would rotate the player around some other point, and the translations would snap to a different location from the rotations. I'm sure my approach is flawed, and I have read about layers and such, but I'm not sure what I can do with them or how to use them. Also, any recommendations as to how to use java graphics in general would be greatly appreciated!
This is what I have in my main:
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class game
{
public static void main(String []args)
{
JFrame frame = new JFrame();
final int FRAME_WIDTH = 1000;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Map b = new Map();
frame.add(b,BorderLayout.CENTER);
frame.setVisible(true);
b.startAnimation();
}
}
And this is the class that handles all the graphics
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Map extends JComponent implements Runnable, KeyListener
{
private int speed = 5;
private int xcenter = 500; // starts on player
private int ycenter = 300;
private double angle = 0.0;
private int[] xcords = {xcenter+10, xcenter, xcenter+20};
private int[] ycords = {ycenter-10, ycenter+20, ycenter+20};
private boolean moveNorth = false;
private boolean moveEast = false;
private boolean moveSouth = false;
private boolean moveWest = false;
public Map()
{
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void startAnimation()
{
Thread t = new Thread(this);
t.start();
}
public void paintComponent(Graphics g)
{
g.fillPolygon(xcords, ycords, 3);
// move screen
if(moveNorth)
{
ycenter += speed;
g.translate(xcenter, ycenter);
}
else if(moveEast)
{
angle += ((1 * Math.PI/180) % (2 * Math.PI));
((Graphics2D) g).rotate(angle, 0, 0);
}
else if(moveSouth)
{
System.out.println(xcenter + ", " + ycenter);
ycenter -= speed;
((Graphics2D) g).rotate(angle, 0, 0);
g.translate(xcenter, ycenter);
}
else if(moveWest)
{
angle -= Math.toRadians(1) % (2 * Math.PI);
((Graphics2D) g).rotate(angle, 0, 0);
}
for(int i = -10; i < 21; i++)
{
g.drawLine(i * 50, -1000, i * 50, 1000);
g.drawLine(-1000, i * 50, 1000, i * 50);
}
g.drawOval(0, 0, 35, 35);
}
public void run()
{
while (true)
{
try
{
if(moveNorth || moveEast || moveSouth || moveWest)
{
repaint();
}
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
}
}
public void keyPressed(KeyEvent e)
{
if(e.getExtendedKeyCode() == 68) // d
{
moveEast = true;
}
else if(e.getExtendedKeyCode() == 87) // w
{
moveNorth = true;
}
else if(e.getExtendedKeyCode() == 65) // a
{
moveWest = true;
}
else if(e.getExtendedKeyCode() == 83) // s
{
moveSouth = true;
}
}
public void keyReleased(KeyEvent e)
{
moveNorth = false;
moveEast = false;
moveSouth = false;
moveWest = false;
}
public void keyTyped(KeyEvent e)
{
}
}
You have to keep in mind that transformations are compounding, so if you rotate the Graphics context by 45 degrees, everything painted after it will be rotated 45 degrees (around the point of rotation), if you rotate it again by 45 degrees, everything painted after it will be rotated a total of 90 degrees.
If you want to paint additional content after a transformation, then you either need to undo the transformation, or, preferably, take a snapshot of the Graphics context and dispose of it (the snapshot) when you're done.
You also need to beware of the point of rotation, Graphics2D#rotate(double) will rotate the Graphics around the point of origin (ie 0x0), which may not be desirable. You can change this by either changing the origin point (ie translate) or using Graphics2D#rotate(double, double, double), which allows you to define the point of rotation.
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
enum Direction {
LEFT, RIGHT;
}
protected enum InputAction {
PRESSED_LEFT, PRESSED_RIGHT, RELEASED_LEFT, RELEASED_RIGHT
}
private BufferedImage car;
private BufferedImage road;
private Set<Direction> directions = new TreeSet<>();
private double directionOfRotation = 0;
public TestPane() throws IOException {
car = ImageIO.read(getClass().getResource("/images/Car.png"));
road = ImageIO.read(getClass().getResource("/images/Road.png"));
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), InputAction.PRESSED_LEFT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), InputAction.RELEASED_LEFT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), InputAction.PRESSED_RIGHT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), InputAction.RELEASED_RIGHT);
am.put(InputAction.PRESSED_LEFT, new DirectionAction(Direction.LEFT, true));
am.put(InputAction.RELEASED_LEFT, new DirectionAction(Direction.LEFT, false));
am.put(InputAction.PRESSED_RIGHT, new DirectionAction(Direction.RIGHT, true));
am.put(InputAction.RELEASED_RIGHT, new DirectionAction(Direction.RIGHT, false));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (directions.contains(Direction.RIGHT)) {
directionOfRotation += 1;
} else if (directions.contains(Direction.LEFT)) {
directionOfRotation -= 1;
}
// No doughnuts for you :P
if (directionOfRotation > 180) {
directionOfRotation = 180;
} else if (directionOfRotation < -180) {
directionOfRotation = -180;
}
repaint();
}
});
timer.start();
}
protected void setDirectionActive(Direction direction, boolean active) {
if (active) {
directions.add(direction);
} else {
directions.remove(direction);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(213, 216);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawRoadSurface(g2d);
drawCar(g2d);
g2d.dispose();
}
protected void drawCar(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
int x = (getWidth() - car.getWidth()) / 2;
int y = (getHeight() - car.getHeight()) / 2;
g2d.drawImage(car, x, y, this);
g2d.dispose();
}
protected void drawRoadSurface(Graphics2D g2d) {
g2d = (Graphics2D) g2d.create();
// This sets the point of rotation at the center of the window
int midX = getWidth() / 2;
int midY = getHeight() / 2;
g2d.rotate(Math.toRadians(directionOfRotation), midX, midY);
// We then need to offset the top/left corner so that what
// we want draw appears to be in the center of the window,
// and thus will be rotated around it's center
int x = midX - (road.getWidth() / 2);
int y = midY - (road.getHeight() / 2);
g2d.drawImage(road, x, y, this);
g2d.dispose();
}
protected class DirectionAction extends AbstractAction {
private Direction direction;
private boolean active;
public DirectionAction(Direction direction, boolean active) {
this.direction = direction;
this.active = active;
}
#Override
public void actionPerformed(ActionEvent e) {
setDirectionActive(direction, active);
}
}
}
}
I am currently doing a new project and long story short I am trying to implement action listener on a rectangle object so it can be moved up and down.
But here the code completely crashes when making an object of well in this case test2 class in the test3 class (yes I am aware of the need for a uppercase for classes). As I have replicated the problem making a new project and just used the code that is needed.
public class test {
test2 board = new test2();
public void frame() {
JFrame b = new JFrame("test");
b.setSize(905,705);
b.setLocation(300,60);
b.setResizable(false);
b.setVisible(true);
b.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b.add(board);
}
public static void main(String[] args) {
test start = new test();
start.frame();
}
}
public class test2 extends JPanel {
public int playerScore = 0;
public int opponentScore = 0;
test3 player = new test3();
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
board(g);
g.setColor(Color.WHITE);
g2d.fill(player.player);
}
public void board(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, 900, 900);
}
}
public class test3 {
public boolean down = false;
public boolean up = false;
public int playerXpos = 45;
public int playerYpos = 300;
public int playerWidth = 15;
public int playerHeight = 80;
Rectangle player = new Rectangle(playerXpos, playerYpos, playerWidth, playerHeight);
test2 theBoard = new test2();
public void actionPerformed(ActionEvent e) {
if (down) {
down = true;
if (up != true) {
down = true;
} else {
up = true;
down = false;
}
}
if (up) {
up = true;
if (down != true) {
up = true;
} else {
up = false;
down = true;
}
}
}
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
for (int r = playerYpos; r >=0; r--) {
if (r == 0) {
playerYpos = playerYpos - 20;
} else {
playerYpos = playerYpos - 1;
}
if (playerYpos < 50) {
playerYpos = 50;
}
}
theBoard.repaint();
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
for (int r = playerYpos; r >= 0; r--) {
if (r == 0) {
playerYpos = playerYpos + 20;
} else {
playerYpos = playerYpos - 1;
}
if (playerYpos > 800){
playerYpos = 800;
}
}
theBoard.repaint();
}
}
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Your code doesn't compile.
I took some of your code and created the following GUI.
Here's the GUI after moving the player rectangle.
Oracle has a helpful tutorial, Creating a GUI With JFC/Swing. Skip the Netbeans section.
The first thing I did was start 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 Player class to hold the player rectangle. This way, you can change the player shape in one place.
I created a JFrame. The JFrame methods have to be called in a specific order. This is the order I use for my Swing applications.
I created a drawing JPanel. This JPanel is similar to yours, except I override the paintComponent method. I pass an instance of Player to the drawing JPanel so the JPanel can draw the player rectangle.
I used key bindings instead of a key listener. This makes the keys functional whether the drawing JPanel is in focus or not. Key bindings also made it easy for me to add the WASD keys as well as the arrow keys.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class ExampleGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ExampleGUI());
}
private final DrawingPanel drawingPanel;
private final Player player;
public ExampleGUI() {
this.player = new Player();
this.drawingPanel = new DrawingPanel(player);
}
#Override
public void run() {
JFrame frame = new JFrame("Example GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
setKeyBindings(drawingPanel);
frame.setVisible(true);
}
private void setKeyBindings(JPanel panel) {
String up = "up";
String down = "down";
String left = "left";
String right = "right";
InputMap inputMap = panel.getInputMap();
ActionMap actionMap = panel.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), up);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), down);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), left);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), right);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), up);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), down);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), left);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), right);
actionMap.put(up, new PlayerAction(this, player, 0, -10));
actionMap.put(down, new PlayerAction(this, player, 0, +10));
actionMap.put(left, new PlayerAction(this, player, -10, 0));
actionMap.put(right, new PlayerAction(this, player, +10, 0));
}
public void repaint() {
drawingPanel.repaint();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final Player player;
public DrawingPanel(Player player) {
this.player = player;
this.setPreferredSize(new Dimension(600, 600));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawBoard(g2d);
drawPlayer(g2d);
}
private void drawBoard(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
private void drawPlayer(Graphics2D g2d) {
g2d.setColor(player.getColor());
g2d.fill(player.getPosition());
}
}
public class PlayerAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private final ExampleGUI frame;
private final Player player;
private final Point moveDirection;
public PlayerAction(ExampleGUI frame, Player player, int x, int y) {
this.frame = frame;
this.player = player;
this.moveDirection = new Point(x, y);
}
#Override
public void actionPerformed(ActionEvent event) {
player.move(moveDirection);
frame.repaint();
}
}
public class Player {
private final Color color;
private Rectangle position;
public Player() {
this.color = Color.WHITE;
this.position = new Rectangle(300, 300, 20, 80);
}
public void move(Point point) {
position.x += point.x;
position.y += point.y;
}
public Color getColor() {
return color;
}
public Rectangle getPosition() {
return position;
}
}
}
I'm trying to make Boy1 move but in the second class I'm getting redlines under setX. Anyone know what's wrong?
First Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class MyGame extends JPanel implements ActionListener, KeyListener {
Timer t = new Timer(5, this);
int x = 0, y = 0, velx =0, vely =0;
public MyGame() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x,y,50,30);
}
public void actionPerformed(ActionEvent e) {
if(x < 0)
{
velx=0;
x = 0;
}
if(x > 530)
{
velx=0;
x = 530;
}
if(y < 0)
{
vely=0;
y = 0;
}
if(y > 330)
{
vely=0;
y = 330;
}
x += velx;
y += vely;
repaint();
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_DOWN){
vely = 1;
velx = 0;
}
if (code == KeyEvent.VK_UP){
vely = -1;
velx = 0;
}
if (code == KeyEvent.VK_LEFT){
vely = 0;
velx = -1;
}
if (code == KeyEvent.VK_RIGHT){
vely = 0;
velx = 1;
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
velx=0;
vely=0;
}
public static void main (String arge[]){
JFrame f = new JFrame();
MyGame s = new MyGame();
f.add(s);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(600,400);
f.setVisible(true);
}
}
Problem is with this last class. Perhaps it needs to be KeyAdapter? But I tried that and seems like that totally doesn't work.
The compiler is flagging you for a reason -- you're trying to call methods on a variable as if it were a variable that the class contains when it isn't -- it's held by a different class. And this isn't Kosher in Java.
What you should do is give the class that holds the variable public methods that outside classes can call and that will allow outside classes to be able to move the label. Then give your control object (the listener) an instance of the class that has these methods.
As an aside, you're usually better off using Key Bindings and not using a KeyListener.
I can give you an example of code that uses Key Bindings to move a JLabel around a JPanel:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.EnumMap;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
public static final String DUKE_IMG_PATH = // https://duke.kenai.com/iconSized/duke.gif
"https://duke.kenai.com/iconSized/duke4.gif";
private static final int PREF_W = 800;
private static final int PREF_H = 800;
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
private static final String KEY_RELEASE = "key release";
public static final int TRANSLATE_SCALE = 3;
private static final String BACKGROUND_STRING = "Use Arrow Keys to Move Image";
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int imgX = 0;
private int imgY = 0;
private int bgStringX;
private int bgStringY;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
try {
URL imgUrl = new URL(DUKE_IMG_PATH);
image = ImageIO.read(imgUrl);
Icon icon = new ImageIcon(image);
JOptionPane.showMessageDialog(null, icon);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
new Timer(TIMER_DELAY, new TimerListener()).start();
// here we set up our key bindings
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
// for the key down key stroke
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0,
false);
inputMap.put(keyStroke, dir.name() + KEY_DOWN);
actionMap.put(dir.name() + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
// for the key release key stroke
keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0, true);
inputMap.put(keyStroke, dir.name() + KEY_RELEASE);
actionMap.put(dir.name() + KEY_RELEASE, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, false);
}
});
}
FontMetrics fontMetrics = getFontMetrics(BG_STRING_FONT);
int w = fontMetrics.stringWidth(BACKGROUND_STRING);
int h = fontMetrics.getHeight();
bgStringX = (PREF_W - w) / 2;
bgStringY = (PREF_H - h) / 2;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.drawString(BACKGROUND_STRING, bgStringX, bgStringY);
if (image != null) {
g.drawImage(image, imgX, imgY, this);
}
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
imgX += dir.getX() * TRANSLATE_SCALE;
imgY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
};
}
enum Direction {
Up(KeyEvent.VK_UP, 0, -1), Down(KeyEvent.VK_DOWN, 0, 1), Left(
KeyEvent.VK_LEFT, -1, 0), Right(KeyEvent.VK_RIGHT, 1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getKeyCode() {
return keyCode;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I am working on a Java project to simulate the flight of a helicopter in a frame. The helicopter moves on the screen using the arrow keys. I want the helicopter to be able to move infinitely, that is, when the helicopter reaches the edge of the frame, the background should move in the opposite direction to have the effect of endless terrain.
Here is the code I have so far:
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MainFrame extends JFrame
{
private static int FRAME_WIDTH = 800;
private static int FRAME_HEIGHT = 500;
public MainFrame()
{
add(new AnotherBackground(FRAME_WIDTH, FRAME_HEIGHT));
setTitle("Helicopter Background Test");
setSize(FRAME_WIDTH,FRAME_HEIGHT);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
class AnotherBackground extends JPanel
{
private BufferedImage heliImage = null;
private BufferedImage backImage = null;
private int heliX = 0;
private int heliY = 0;
private int backX = 0;
private int backY = 0;
private int frameWidth = 0;
private int frameHeight = 0;
private int backWidth = 0;
private int backHeight = 0;
public AnotherBackground(int fWidth, int fHeight)
{
frameWidth = fWidth;
frameHeight = fHeight;
this.setFocusable(true);
this.addKeyListener(new HeliListener());
try
{
heliImage = ImageIO.read(new URL("http://imageshack.us/a/img7/2133/helicopter2f.png"));
// 2.7 Meg Crap that is a humungous image! Substitute dummy.
backImage = new BufferedImage(1918,1200,BufferedImage.TYPE_INT_RGB);
}
catch(IOException ex)
{
System.out.println("Problem durinng loading heli image");
}
backWidth = backImage.getWidth();
backHeight = backImage.getHeight();
HeliPainter l = new HeliPainter();
new Thread(l).start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(backImage, backX, backY, null);
g.drawImage(heliImage, heliX, heliY, null);
}
class HeliListener extends KeyAdapter
{
#Override
public void keyPressed(KeyEvent e)
{
System.out.println(heliX + " " + heliY + " " + backX + " " + backY);
if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
if(heliX > 0)
{
heliX -= 5;
}
else
{
backX += 5;
}
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
{
if(heliX < frameWidth)
{
heliX += 5;
}
else
{
backX -= 5;
}
}
else if (e.getKeyCode() == KeyEvent.VK_UP)
{
if(heliY > 0)
{
heliY -= 5;
}
else
{
backY += 5;
}
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
if(heliY < frameHeight)
{
heliY += 5;
}
else
{
backY -= 5;
}
}
}
}
class HeliPainter implements Runnable
{
#Override
public void run()
{
try
{
while(true)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
repaint();
}
});
Thread.sleep(1);
}
}
catch(InterruptedException ex)
{
System.out.println("Problem putting thread to sleep");
}
}
}
}
Now there's two images in the code. One is that of a small helicopter, and the other is a large (2.7 meg) background. They are here:
background
helicopter http://imageshack.us/a/img7/2133/helicopter2f.png
How to show the BG continuously?
Have a look through this source which behaves in a more predictable manner, and also includes a nice tweak to the chopper image (animated). ;)
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class MainFrame
{
public MainFrame()
{
JFrame f = new JFrame("Helicopter Background Test");
f.add(new AnotherBackground());
//setTitle("Helicopter Background Test"); Redundant..
// Set a preferred size for the content area and pack() the frame instead!
// setSize(FRAME_WIDTH,FRAME_HEIGHT);
// setLocationRelativeTo(null); Better to..
f.setLocationByPlatform(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack(); // Size the GUI - VERY MPORTANT!
f.setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
class AnotherBackground extends JPanel
{
private static int PREFERRED_WIDTH = 400;
private static int PREFERRED_HEIGHT = 200;
private BufferedImage heliImage = null;
private BufferedImage heliLeftImage = null;
private BufferedImage heliRightImage = null;
private BufferedImage backImage = null; //getFlippedImage(
private int heliX = 0;
private int heliY = 0;
private int backX = 0;
private int backY = 0;
private int frameWidth = 0;
private int frameHeight = 0;
private int backWidth = 0;
private int backHeight = 0;
public AnotherBackground()
{
frameWidth = PREFERRED_WIDTH;
frameHeight = PREFERRED_HEIGHT;
this.setFocusable(true);
this.addKeyListener(new HeliListener());
try
{
heliLeftImage = ImageIO.read(
new URL("http://imageshack.us/a/img7/2133/helicopter2f.png"));
heliRightImage = getFlippedImage(heliLeftImage);
heliImage = heliLeftImage;
// 2.7 Meg Crap that is an humungous image! Substitute dummy.
backImage = getTileImage(250);
//ImageIO.read(
// new URL("http://i.stack.imgur.com/T5uTa.png"));
backWidth = backImage.getWidth();
backHeight = backImage.getHeight();
//HeliPainter l = new HeliPainter(); // see mention of repaint()
//new Thread(l).start();
} catch(IOException ex) {
// THERE IS NO POINT CONTINUING AFTER THIS POINT!
// unless it is to pop an option pane error message..
System.err.println("Problem during loading heli image");
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int normalizeX = (heliRealX-heliX)%backImage.getWidth();
int normalizeY = (heliRealY-heliY)%backImage.getHeight();
int timesRepeatX = (getWidth()/backImage.getWidth())+2;
int timesRepeatY = (getHeight()/backImage.getHeight())+2;
for (int xx=-1; xx<timesRepeatX; xx++) {
for (int yy=-1; yy<timesRepeatY; yy++) {
g.drawImage(
backImage,
(xx*backImage.getWidth())-normalizeX,
(yy*backImage.getHeight())-normalizeY,
this); // A JPanel IS AN ImageObserver!
g.drawImage(heliImage, heliX, heliY, this);
}
}
g.setColor(Color.BLACK);
}
private int heliRealX = 0;
private int heliRealY = 0;
class HeliListener extends KeyAdapter
{
#Override
public void keyPressed(KeyEvent e)
{
int pad = 5;
if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
if(heliX > 0)
{
heliX -= 5;
}
else
{
backX += 5;
}
heliRealX-=5;
heliImage = heliLeftImage;
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
{
// correct for image size + padding
if(heliX+heliImage.getWidth()+pad < getWidth())
{
heliX += 5;
}
else
{
backX -= 5;
}
heliRealX+=5;
heliImage = heliRightImage;
}
else if (e.getKeyCode() == KeyEvent.VK_UP)
{
if(heliY > 0)
{
heliY -= 5;
}
else
{
backY += 5;
}
heliRealY-=5;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
// correct for image size + padding
if(heliY+heliImage.getHeight()+pad < getHeight())
{
heliY += 5;
}
else
{
backY -= 5;
}
heliRealY+=5;
}
repaint(); // Replaces need for threads for this simple demo!
}
}
public BufferedImage getFlippedImage(BufferedImage original) {
BufferedImage bi = new BufferedImage(
original.getWidth(),
original.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
AffineTransform at = AffineTransform.getTranslateInstance(bi.getWidth(),1d);
at.concatenate(AffineTransform.getScaleInstance(-1d,1d));
g.setTransform(at);
g.drawImage(original,0,0,this);
g.dispose();
return bi;
}
public BufferedImage getTileImage(int s) {
BufferedImage bi = new BufferedImage(s,s,BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
GradientPaint gp1 = new GradientPaint(
(float)0,(float)s/4, Color.YELLOW,
(float)s/4,0f, Color.GREEN,
true);
g.setPaint(gp1);
g.fillRect(0,0,s,s);
int trans = 165;
GradientPaint gp2 = new GradientPaint(
(float)s/2,(float)s/2, new Color(255,0,0,trans),
0f,(float)s/2, new Color(255,255,255,trans),
true);
g.setPaint(gp2);
g.fillRect(0,0,s,s);
g.dispose();
return bi;
}
}
This is a really simple example (you can only move in a single direction). The basic idea is that there is a prepareView method that is responsible for generating a view of the world based on the available viewable area. If the view is trying to view an area off the map, the map is titled to make up for it.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class InfiniteBackground {
public static void main(String[] args) {
new InfiniteBackground();
}
public InfiniteBackground() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static final int DELTA = 5;
private BufferedImage terrian;
private BufferedImage heli;
private Point pov;
private Point heliPoint;
private BufferedImage view;
public TestPane() {
pov = new Point();
heliPoint = new Point();
try {
terrian = ImageIO.read(getClass().getResource("/terrain_map.jpg"));
heli = ImageIO.read(getClass().getResource("/helicopter2f.png"));
pov.x = terrian.getWidth() - getPreferredSize().width;
pov.y = ((terrian.getHeight() - getPreferredSize().height) / 2);
heliPoint.x = getPreferredSize().width / 2;
heliPoint.y = getPreferredSize().height / 2;
prepareView();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "goLeft");
am.put("goLeft", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
heliPoint.x -= DELTA;
if (heliPoint.x - (heli.getWidth() / 2) < 0) {
heliPoint.x = (heli.getWidth() / 2);
prepareView();
pov.x -= DELTA;
}
repaint();
}
});
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
protected void prepareView() {
if (getWidth() > 0 && getHeight() > 0) {
view = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = view.createGraphics();
if (pov.x < 0) {
pov.x = terrian.getWidth();
}
g2d.drawImage(terrian, -pov.x, -pov.y, this);
if (pov.x + getWidth() > terrian.getWidth()) {
g2d.drawImage(terrian, -pov.x + terrian.getWidth(), -pov.y, this);
}
g2d.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (terrian != null) {
Graphics2D g2d = (Graphics2D) g.create();
if (view == null) {
prepareView();
}
g2d.drawImage(view, 0, 0, this);
g2d.drawImage(heli, heliPoint.x - (heli.getWidth() / 2), heliPoint.y - (heli.getHeight() / 2), this);
g2d.dispose();
}
}
}
}
I am working on moving a shape along a "grid". I seem to have finally figured things out, but I'm having a slight problem; no matter how close i try to get the specific time interval in between each movement, is seems to still go off of the grid. I would like to use the current method I am using, as I understand how thigs are working this way. I know it is getting a random movement "glitch" sometimes, because if I move it back and fourth it should be locked onto the grid no matter what. Here is my code (Sorry for the lack of comments and weird placement of things I am just testing the code):
Main Class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JFrame;
public class WhyOhWhy {
public int x;
public int y;
public static void main(String[] args) {
JFrame f = new JFrame();
InputHandler input = new InputHandler();
f.add(input);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800,600);
input.doStuff();
}
}
InputHandler Class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class InputHandler extends JPanel implements ActionListener, KeyListener {
Timer t = new Timer(5, this);
int x = 0, y = 0, velX = 0, velY = 0;
int i = 0, j = 0;
TimeKeeper timeStart;
public void doStuff(){
velX = 0;
velY = 0;
}
public InputHandler() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fill(new Ellipse2D.Double(x, y, 32, 32));
for(int i = 0;i <500;i+=32){
g2.drawRect(i, j, 32, 32);
for(int j=0;j<500;j+=32){
g2.drawRect(i, j, 32, 32);
}
}
}
public void actionPerformed(ActionEvent e) {
repaint();
x += velX;
y += velY;
if(TimeKeeper.isFinished() == true){
System.out.println("DONE");
TimeKeeper.resetTimer(false);
velX = 0;
velY = 0;
setEnabled(true);
}
}
public void up() {
//System.out.println("Moving up");
timeStart = new TimeKeeper(185);
velY = -1;
velX = 0;
setEnabled(false);
}
public void down() {
//System.out.println("Moving down");
setEnabled(false);
timeStart = new TimeKeeper(185);
velY = 1;
velX = 0;
}
public void left() {
//System.out.println("Moving left");
setEnabled(false);
timeStart = new TimeKeeper(185);
velY = 0;
velX = -1;
}
public void right() {
//System.out.println("Moving right");
setEnabled(false);
timeStart = new TimeKeeper(185);
velY = 0;
velX = 1;
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_W) {
up();
}
if (code == KeyEvent.VK_S) {
down();
}
if (code == KeyEvent.VK_A) {
left();
}
if (code == KeyEvent.VK_D) {
right();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
velX = 0;
velY = 0;
}
}
TimeKeeper Class:
import java.util.Timer;
import java.util.TimerTask;
public class TimeKeeper {
Timer timer;
public static boolean isDone = false;
public TimeKeeper(int seconds) {
timer = new Timer();
isDone = false;
timer.schedule(new RemindTask(), seconds );
}
class RemindTask extends TimerTask {
public void run() {
//System.out.println("Time's up!");
isDone = true;
timer.cancel(); //Terminate the timer thread
}
}
public static boolean isFinished() {
return isDone;
}
public static void resetTimer(boolean done) {
isDone = done;
}
}
Thank you!
Never mind, figured it out. Needed to use the % operator to see if the number that the shape moved was divisible by 32 each step, in both the x and y direction.