Hey so im trying to make a D&D game for a school project. When I draw my background image it works and draws it. But when I draw my background image and my player image only the player image shows up.
My Code
Well does your player have transparency? If the player doesn't have alpha then it will draw over the bg.
Also, I would recommend using this render method instead in conjunction with a proper game loop:
private void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics2D g2d = (Graphics2D) bs.getDrawGraphics();
g2d.setColor(Color.black);
g2d.fillRect(0, 0, WIDTH, HEIGHT); // background (can also be an image)
player.render(g2d); // player
g2d.dispose();
bs.show();
}
So, your basic problem can be summed up here...
public class UIFrame extends JFrame {
//...
public UIFrame() {
setSize(size);
add(background);
add(userScreen);
Two thins are wrong with this. First, JFrame uses a BorderLayout to layout it's components, meaning that only the last component added will actually be laid out.
See Laying Out Components Within a Container for more details
Second, components are painted in LIFO order, so in your case, that would be userScreen then background - but since background is never laid out (it's size is 0x0) you won't see it.
The reason I mention it is because, you shouldn't be trying to design you program this way.
Next issue ...
public static Image[] scheme_player = new Image[1];
public static boolean imagesLoaded = false;
public static boolean leftPress = false;
public static boolean rightPress = false;
public static boolean upPress = false;
public static boolean downPress = false;
public static boolean mouseClick = false;
This is a pretty good sign of a bad design. static is not your friend and can lead to some very interesting (and difficult to debug) behaviour
Instead, we want to think about your design from in a more agnostic manner.
Disclaimer
This is an "example", intended to provide a "base line" of ideas from which you can grow your own API/concept from. There are some things in the example which I might do differently if I were designing my own solution, but have been done for brevity and to reduce the overall complexity
Basic concepts
You have stuff that paints, you have stuff that moves, you probably have stuff that collides and other stuff to. You need some way to manage and coordinate all this "stuff"
A basic principle of OOP (and programming in general) is the concept of "model-view-controller". The idea is to seperate your concepts into smaller, decoupled units of work, which can then be put back together, in what ever way you need, to form a large picture (or body of work)
So, let's start with the basic building blocks, the "entity". A "entity" is something in your program which carries information and may perform different tasks and roles. Because you might have any number of types of entities, I would start with a series of basic interfaces which describe basic behaviour of different types of entities...
public interface Entity {
// Other common properties
}
public interface MovableEntity extends Entity {
public void move(GameModel model);
// Other "movable" entities might also have
// speed adjustments, but I might consider those as
// seperate entities in of themselves, but that's me
}
public interface PaintableEntity extends Entity {
public void paint(Graphics2D g2d, GameModel model);
}
Notice, I didn't make movable or paintable into a single entity (you could create a fourth interface which does), this is because you might have a movable entity which isn't painted or a painted entity which doesn't move, like the background!
Next, we need some kind container to hold the important information which the rest of the game might need in order to do their jobs, some kind of, model!
public interface GameModel {
public enum Input {
UP, DOWN, LEFT, RIGHT;
}
public boolean hasInput(Input input);
public Dimension getViewableArea();
// Other properties which might be needed by the
// entities
}
This is pretty basic, but this is the concept around which information is shared. Here I've focused only on those elements which the entities really needed (no need to expose functionality that they don't really need).
I've also prepared the state for "input", which is agnostic from how that input actually occurs. The rest of the API doesn't care, they only want to know if the input is available or not.
From this, I can start building some basic entities...
public class BackgroundEntity implements PaintableEntity {
#Override
public void paint(Graphics2D g2d, GameModel model) {
g2d.setColor(Color.BLUE);
Dimension bounds = model.getViewableArea();
g2d.fillRect(0, 0, bounds.width, bounds.height);
}
}
public class PlayerEntity implements PaintableEntity, MovableEntity {
private int speed = 2;
private int x, y;
public PlayerEntity() {
// load the player image
}
public int getSpeed() {
return speed;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
#Override
public void paint(Graphics2D g2d, GameModel model) {
g2d.setColor(Color.RED);
g2d.drawRect(getX(), getY(), 10, 10);
}
#Override
public void move(GameModel model) {
int speed = getSpeed();
int x = getX();
int y = getY();
if (model.hasInput(GameModel.Input.UP)) {
y -= speed;
} else if (model.hasInput(GameModel.Input.DOWN)) {
y += speed;
}
if (model.hasInput(GameModel.Input.LEFT)) {
x -= speed;
} else if (model.hasInput(GameModel.Input.RIGHT)) {
x += speed;
}
Dimension bounds = model.getViewableArea();
if (y < 0) {
y = 0;
} else if (y + 10 > bounds.height) {
y = bounds.height - 10;
}
if (x < 0) {
x = 0;
} else if (x + 10 > bounds.width) {
x = bounds.width - 10;
}
setX(x);
setY(y);
}
}
Okay, that's all fine and good, but we need some way to actually update the state, in this case, we devise a concept of mutable model. The intent been to "hide" functionality, so we don't expose it to parts of the API which don't need (the player doesn't need to be able to add/remove new entities)
public interface MutableGameModel extends GameModel {
public void setInput(Input input, boolean enabled);
public void add(Entity entity);
public void remove(Entity entity);
public void update();
// Decision, who needs access to these lists
public List<PaintableEntity> paintableEntities();
public List<MovableEntity> moveableEntities();
}
And because we actually need some way to work...
public class DefaultGameModel implements MutableGameModel {
private Set<Input> inputs = new HashSet<>();
private List<Entity> entities = new ArrayList<>(25);
#Override
public boolean hasInput(Input input) {
return inputs.contains(input);
}
#Override
public void setInput(Input input, boolean enabled) {
if (enabled) {
inputs.add(input);
} else {
inputs.remove(input);
}
}
#Override
public Dimension getViewableArea() {
return new Dimension(400, 400);
}
public void update() {
for (MovableEntity entity : moveableEntities()) {
entity.move(this);
}
}
// This is not the most efficent approach. You might consider
// caching each entity type into seperate lists when they are added
// instead
public List<PaintableEntity> paintableEntities() {
return entities.stream()
.filter(e -> e instanceof PaintableEntity)
.map(e -> (PaintableEntity) e)
.collect(Collectors.toList());
}
public List<MovableEntity> moveableEntities() {
return entities.stream()
.filter(e -> e instanceof MovableEntity)
.map(e -> (MovableEntity) e)
.collect(Collectors.toList());
}
#Override
public void add(Entity entity) {
entities.add(entity);
}
#Override
public void remove(Entity entity) {
entities.remove(entity);
}
}
This is pretty basic concept, but it puts into place the foundations of the rest of work - please not this is a "EXAMPLE", I've cut some corners for brevity and so I could get it up and running quickly, but the basic concept should hold up
And finally, we get to the "view" (and controller). This is the part where we monitor for input states, update the model accordingly, run the main loop to update the model state, based on the current inputs, schedule and performing painting
public class GamePane extends JPanel {
private MutableGameModel model;
public GamePane(MutableGameModel model) {
this.model = model;
setupBindings();
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.update();
repaint();
}
});
timer.start();
}
public void setupBindings() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");
actionMap.put("Up.pressed", new InputAction(model, GameModel.Input.UP, true));
actionMap.put("Up.released", new InputAction(model, GameModel.Input.UP, false));
actionMap.put("Down.pressed", new InputAction(model, GameModel.Input.DOWN, true));
actionMap.put("Down.released", new InputAction(model, GameModel.Input.DOWN, false));
actionMap.put("Left.pressed", new InputAction(model, GameModel.Input.LEFT, true));
actionMap.put("Left.released", new InputAction(model, GameModel.Input.LEFT, false));
actionMap.put("Right.pressed", new InputAction(model, GameModel.Input.RIGHT, true));
actionMap.put("Right.released", new InputAction(model, GameModel.Input.RIGHT, false));
}
#Override
public Dimension getPreferredSize() {
return model.getViewableArea().getSize();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (PaintableEntity entity : model.paintableEntities()) {
Graphics2D g2d = (Graphics2D) g.create();
entity.paint(g2d, model);
g2d.dispose();
}
}
}
public class InputAction extends AbstractAction {
private MutableGameModel model;
private GameModel.Input input;
private boolean pressed;
public InputAction(MutableGameModel model, GameModel.Input input, boolean pressed) {
this.model = model;
this.input = input;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
model.setInput(input, pressed);
}
}
Pretty simple :P
The solution makes use of the Key Bindings which is generally less troublesome them KeyListener and should be used in 99% of cases you want to monitor a subset of input.
I've also used a Swing Timer as my "main-loop". This is a safer option (in Swing) as it does not violate the single threaded nature of the API
Example...
Because I know how hard it can be to try and put together "snippets" of code...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class 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();
}
DefaultGameModel model = new DefaultGameModel();
model.add(new BackgroundEntity());
model.add(new PlayerEntity());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GamePane(model));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
private MutableGameModel model;
public GamePane(MutableGameModel model) {
this.model = model;
setupBindings();
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.update();
repaint();
}
});
timer.start();
}
public void setupBindings() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");
actionMap.put("Up.pressed", new InputAction(model, GameModel.Input.UP, true));
actionMap.put("Up.released", new InputAction(model, GameModel.Input.UP, false));
actionMap.put("Down.pressed", new InputAction(model, GameModel.Input.DOWN, true));
actionMap.put("Down.released", new InputAction(model, GameModel.Input.DOWN, false));
actionMap.put("Left.pressed", new InputAction(model, GameModel.Input.LEFT, true));
actionMap.put("Left.released", new InputAction(model, GameModel.Input.LEFT, false));
actionMap.put("Right.pressed", new InputAction(model, GameModel.Input.RIGHT, true));
actionMap.put("Right.released", new InputAction(model, GameModel.Input.RIGHT, false));
}
#Override
public Dimension getPreferredSize() {
return model.getViewableArea().getSize();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (PaintableEntity entity : model.paintableEntities()) {
Graphics2D g2d = (Graphics2D) g.create();
entity.paint(g2d, model);
g2d.dispose();
}
}
}
public class InputAction extends AbstractAction {
private MutableGameModel model;
private GameModel.Input input;
private boolean pressed;
public InputAction(MutableGameModel model, GameModel.Input input, boolean pressed) {
this.model = model;
this.input = input;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
model.setInput(input, pressed);
}
}
public class DefaultGameModel implements MutableGameModel {
private Set<Input> inputs = new HashSet<>();
private List<Entity> entities = new ArrayList<>(25);
#Override
public boolean hasInput(Input input) {
return inputs.contains(input);
}
#Override
public void setInput(Input input, boolean enabled) {
if (enabled) {
inputs.add(input);
} else {
inputs.remove(input);
}
}
#Override
public Dimension getViewableArea() {
return new Dimension(400, 400);
}
public void update() {
for (MovableEntity entity : moveableEntities()) {
entity.move(this);
}
}
// This is not the most efficent approach. You might consider
// caching each entity type into seperate lists when they are added
// instead
public List<PaintableEntity> paintableEntities() {
return entities.stream()
.filter(e -> e instanceof PaintableEntity)
.map(e -> (PaintableEntity) e)
.collect(Collectors.toList());
}
public List<MovableEntity> moveableEntities() {
return entities.stream()
.filter(e -> e instanceof MovableEntity)
.map(e -> (MovableEntity) e)
.collect(Collectors.toList());
}
#Override
public void add(Entity entity) {
entities.add(entity);
}
#Override
public void remove(Entity entity) {
entities.remove(entity);
}
}
public interface GameModel {
public enum Input {
UP, DOWN, LEFT, RIGHT;
}
public boolean hasInput(Input input);
public Dimension getViewableArea();
// Other properties which might be needed by the
// entities
}
public interface MutableGameModel extends GameModel {
public void setInput(Input input, boolean enabled);
public void add(Entity entity);
public void remove(Entity entity);
public void update();
// Decision, who needs access to these lists
public List<PaintableEntity> paintableEntities();
public List<MovableEntity> moveableEntities();
}
public interface Entity {
// Other common properties
}
public interface MovableEntity extends Entity {
public void move(GameModel model);
// Other "movable" entities might also have
// speed adjustments, but I might consider those as
// seperate entities in of themselves, but that's me
}
public interface PaintableEntity extends Entity {
public void paint(Graphics2D g2d, GameModel model);
}
public class BackgroundEntity implements PaintableEntity {
#Override
public void paint(Graphics2D g2d, GameModel model) {
g2d.setColor(Color.BLUE);
Dimension bounds = model.getViewableArea();
g2d.fillRect(0, 0, bounds.width, bounds.height);
}
}
public class PlayerEntity implements PaintableEntity, MovableEntity {
private int speed = 2;
private int x, y;
public PlayerEntity() {
// load the player image
}
public int getSpeed() {
return speed;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
#Override
public void paint(Graphics2D g2d, GameModel model) {
g2d.setColor(Color.RED);
g2d.drawRect(getX(), getY(), 10, 10);
}
#Override
public void move(GameModel model) {
int speed = getSpeed();
int x = getX();
int y = getY();
if (model.hasInput(GameModel.Input.UP)) {
y -= speed;
} else if (model.hasInput(GameModel.Input.DOWN)) {
y += speed;
}
if (model.hasInput(GameModel.Input.LEFT)) {
x -= speed;
} else if (model.hasInput(GameModel.Input.RIGHT)) {
x += speed;
}
Dimension bounds = model.getViewableArea();
if (y < 0) {
y = 0;
} else if (y + 10 > bounds.height) {
y = bounds.height - 10;
}
if (x < 0) {
x = 0;
} else if (x + 10 > bounds.width) {
x = bounds.width - 10;
}
setX(x);
setY(y);
}
}
}
nb - For every one whose going to tell more how horribly inefficient it is to use List#stream this way, yes, you are correct, I've mentioned that several times - the point for using is brevity.
If I was doing this, I'd either constraint the requirements at the MutableModelLevel (using things like add(PaintableEntity) or have the add method determine which series of Lists the entity needs to be added to) or make a series of generic "models" which constraint the functionality, so that when I devise my implementation, I can pick and choose which functionality I want to use - but that's just me
Related
I'm new to swing and I am trying to make the square move but only when the key is released (ex W) but when i hold down the key the square just moves
KeyListener Class
I want to make sure that a key was pressed and if it is still pressed it should return false but true if it was pressed then released
package javaGD.GameAssistant.Input;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class KeyManager implements KeyListener {
private boolean[] keys;
public KeyManager() {
keys = new boolean[256];
}
#Override
public void keyPressed(KeyEvent e) {
keys[e.getKeyCode()] = true;
}
#Override
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
#Override
public void keyTyped(KeyEvent e) {
}
public boolean KeyPressed(int keycode) {
return keys[keycode];
}
public boolean KeyReleased(int keycode) {
return keys[keycode];
}
}
Class where the square should move.
KeyListener is inherited from GameAssistant(JFrame is created with the KeyListener)
package TestCode;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import javaGD.GameAssistant.GameAssistant;
public class Game extends GameAssistant {
public int x, xSpeed, y, ySpeed;
public Game(String title, int width, int height, boolean makeResizable) {
super(title, width, height, makeResizable);
}
#Override
public void setup() {
x = 0;
y = 0;
xSpeed = 5;
ySpeed = 5;
}
#Override
public void update() {
if (this.Keyboard().KeyReleased(KeyEvent.VK_D)) {
x += xSpeed;
}
if (this.Keyboard().KeyReleased(KeyEvent.VK_A)) {
x -= xSpeed;
}
if (this.Keyboard().KeyReleased(KeyEvent.VK_W)) {
y -= ySpeed;
}
if (this.Keyboard().KeyReleased(KeyEvent.VK_S)) {
y += ySpeed;
}
}
#Override
public void draw(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(x, y, 20, 20);
}
}
KeyReleased should be returning !keys[keycode] Otherwise it will return false when released and true when pressed
public boolean KeyReleased(int keycode) {
return !keys[keycode];
}
I would also recommend using the Key bindings API over KeyListener as it's more reliable and re-usable.
If you are only planning on a limited number of input operations, I would use Set and a enum, this way you can decouple the "how" from the "what".
You update method doesn't care "how" the inputs are managed, only the "what is the state"
Conceptually, maybe something like...
public enum GameInput {
UP, DOWN, LEFT, RIGHT;
}
public class KeyManager implements KeyListener {
private Set<GameInput> inputs = new HashSet<>();
public KeyManager() {
}
#Override
public void keyPressed(KeyEvent e) {
// Check the key code, verify if it's one of the configured
// actions keys
// The key code could come from a configuration file which might
// be customisable by the user...
if (e.getKeyCode() == KeyEvent.VK_W) {
inputs.add(GameInput.UP);
} else if (e.getKeyCode() == KeyEvent.VK_S) {
// etc...
} // etc...
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
inputs.remove(GameInput.UP);
} else if (e.getKeyCode() == KeyEvent.VK_S) {
// etc...
} // etc...
}
#Override
public void keyTyped(KeyEvent e) {
}
public boolean isKeyPressed(GameInput input) {
return inputs.contains(input);
}
public boolean isKeyReleased(GameInput input) {
return !isKeyPressed(input);
}
}
And your update method might look like...
#Override
public void update() {
if (this.Keyboard().isKeyReleased(GameInput.RIGHT)) {
x += xSpeed;
}
if (this.Keyboard().isKeyReleased(GameInput.LEFT)) {
x -= xSpeed;
}
if (this.Keyboard().isKeyReleased(GameInput.UP)) {
y -= ySpeed;
}
if (this.Keyboard().isKeyReleased(GameInput.DOWN)) {
y += ySpeed;
}
}
Now your update method doesn't care "how" the inputs are generated, only what to do when they are (or aren't) set.
Personally, I'd use a InputManager class and further decouple it, so inputs could be generate via other means, like buttons, mouse inputs, game pads, etc...
Runnable example...
Conceptually, this should work. I say "conceptually", because while I was testing I came across a number of "weird" issues that I can't contribute to either Java (1.8) or MacOS - or because they are a combination of both...more details below...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
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 Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Box box = new Box();
public TestPane() {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");
am.put("Up.pressed", new KeyAction(InputAction.UP, true));
am.put("Up.released", new KeyAction(InputAction.UP, false));
am.put("Down.pressed", new KeyAction(InputAction.DOWN, true));
am.put("Down.released", new KeyAction(InputAction.DOWN, false));
am.put("Left.pressed", new KeyAction(InputAction.LEFT, true));
am.put("Left.released", new KeyAction(InputAction.LEFT, false));
am.put("Right.pressed", new KeyAction(InputAction.RIGHT, true));
am.put("Right.released", new KeyAction(InputAction.RIGHT, false));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
box.update(getBounds());
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
box.draw(g);
}
}
public class Box {
public int x, xSpeed, y, ySpeed, width, height;
public Box() {
x = 0;
y = 0;
xSpeed = 1;
ySpeed = 1;
width = 10;
height = 10;
}
public void update(Rectangle bounds) {
if (!InputManager.INSTANCE.isSet(InputAction.LEFT)) {
x -= xSpeed;
}
if (!InputManager.INSTANCE.isSet(InputAction.RIGHT)) {
x += xSpeed;
}
if (InputManager.INSTANCE.isSet(InputAction.UP) && InputManager.INSTANCE.isSet(InputAction.DOWN)) {
//
} else if (!InputManager.INSTANCE.isSet(InputAction.UP)) {
y -= ySpeed;
} else if (!InputManager.INSTANCE.isSet(InputAction.DOWN)) {
y += ySpeed;
}
if (x < bounds.x) {
x = 0;
} else if (x + width > (bounds.x + bounds.width)) {
x = bounds.x + (bounds.width - width);
}
if (y < bounds.y) {
y = 0;
} else if (y + height > (bounds.y + bounds.height)) {
y = bounds.y + (bounds.height - height);
}
}
public void draw(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
}
public enum InputAction {
UP, DOWN, LEFT, RIGHT;
}
public enum InputManager {
INSTANCE;
private Set<InputAction> actions = new HashSet<>();
public void set(InputAction action) {
actions.add(action);
}
public void remove(InputAction action) {
actions.remove(action);
}
public boolean isSet(InputAction action) {
return actions.contains(action);
}
}
public class KeyAction extends AbstractAction {
private InputAction action;
private boolean apply;
public KeyAction(InputAction action, boolean apply) {
this.action = action;
this.apply = apply;
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("!");
if (apply) {
System.out.println("Apply " + action);
InputManager.INSTANCE.set(action);
} else {
System.out.println("Remove " + action);
InputManager.INSTANCE.remove(action);
}
}
}
}
TL;DR
While testing the above example, I came across a bizarre number of issues, I've not seen before (not that I've been doing this kind of thing recently).
When using KeyListener, if I pressed (and held) two buttons, I could see the "pressed" action, but there was no repeating events, which is something I would normally expect (for any keys). When I released on key, I saw the "release" action, but when I pressed (and held it), no new "press" action was generated.
I tried the key bindings API (as demonstrated above) and still had no success (similar results).
I then attached a AWTEventListener directly to the event queue and monitored ALL the key strokes.
I noted that, some times (even is just tapping a key repeatedly) that "pressed" might not be generated.
I also noted that holding one or more keys down, releasing and pressing a key again, more often then not, did not generate a new press event (only release events)
I'm using macOS 10.13.6 and Java 1.8.0_144-b01 - it could be bug in either or both, but I don't have the means to test it otherwise
Update...
So, after updating from Java 1.8 to Java 1.10, the above mentioned issue seems to be resoled - however, this highlights another, hardware issue, where only a certain number of keys can be actively pressed at a time - See How do I remove the limit on PC keyboard button presses? for more details
I am creating a Space Invaders Game with just one Invader. The images are stuttering and are only visible ~10% of the time. What is wrong with my code?
package spaceinvader;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class spaceInvaders extends JApplet implements KeyListener, ActionListener
{
//Declare components and variables
JPanel mainPanel = new JPanel();
ImageIcon carImage = new ImageIcon("ship.png");
ImageIcon invaderImage = new ImageIcon("invader.png");
int intPosX = 240;
int intPosY = 330;
int intXAmount = 15;
boolean shipMoveLeft = false;
boolean shipMoveRight = false;
Timer shipTimer = new Timer(100,this);
int intBulletX = -50;
int intBulletY = -50;
boolean bulletMove = false;
boolean bulletActive = false;
Timer bulletTimer = new Timer(50,this);
int intInvaderX = 0;
int intInvaderY = 0;
int invaderXAmount = 10;
boolean invaderMove = true;
Timer invaderTimer= new Timer(1000,this);
public void init()
{
addKeyListener(this);
setFocusable(true);
resize(600,400);
setContentPane(mainPanel);
shipTimer.start();
bulletTimer.start();
invaderTimer.start();
}
public void actionPerformed(ActionEvent e)
{
requestFocus();
if(shipMoveLeft)
intPosX += intXAmount;
else if(shipMoveRight)
intPosX -= intXAmount;
if(bulletMove && bulletActive){
intBulletY -= 15;
if(intBulletY <= -50){
bulletMove = false;
bulletActive = false;
}
}
if(invaderMove){
intInvaderX += invaderXAmount;
if(intInvaderX > getWidth() - 60 || intInvaderX < 0){
intInvaderY += 40;
invaderXAmount *= -1;
}
}
repaint();
}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == 37){
shipMoveRight = true;
}
else if (key == 39){
shipMoveLeft = true;
}
else if (key == 32){
if(bulletActive == false){
intBulletX = intPosX;
intBulletY = intPosY;
}
bulletMove = true;
bulletActive = true;
}
}
public void keyReleased(KeyEvent e)
{
shipMoveLeft = false;
shipMoveRight = false;
}
public void keyTyped(KeyEvent e)
{
}
public void paint(Graphics gr)
{
super.paint(gr);
gr.setColor(Color.red);
gr.fillOval(intBulletX, intBulletY, 10, 25);
carImage.paintIcon(this,gr, intPosX, intPosY); //Draw image in new spot
invaderImage.paintIcon(this,gr, intInvaderX, intInvaderY);
}
}
So there are a number of issues which jump out immediately...
Applets are a dead end, most browsers will actively block them and/or have dropped support for the plugin
You're adding a JPanel to the applet, but overriding the applet's paint method, because of the way painting can work, the panel can be painted independently of the applet, causing it to paint over whatever you might have painted
You don't need multiple timers, you just need to have better delta values (smaller been faster)
KeyListener is a poor choice for detecting keyboard input, it's rather low level and has focus related issues which are easily overcome by using the Key Bindings API
I'd start by having a look at Performing Custom Painting, Painting in AWT and Swing and How to Use Key Bindings for more details.
So how would you start fixing it? Start by using a JPanel as you basic container, override it's paintComponent and place all your paint logic here.
Use a single Timer to manage the updates
There are any number of approaches you can take, but I'd start with defining some contracts that define what can go on in the game
public interface GameSpace extends ImageObserver {
public Dimension getGameSpace();
public boolean hasInput(Input input);
}
public interface Entity {
public void paint(GameSpace gameSpace, Graphics2D g2d);
public boolean update(GameSpace gameSpace);
public Rectangle getBounds();
}
public abstract class AbstractEntity implements Entity {
private int x;
private int y;
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
protected abstract int getWidth();
protected abstract int getHeight();
public Rectangle getBounds() {
return new Rectangle(getX(), getY(), getWidth(), getHeight());
}
}
public abstract class AbstractImageEntity extends AbstractEntity {
protected abstract BufferedImage getImage();
#Override
protected int getWidth() {
return getImage().getWidth();
}
#Override
protected int getHeight() {
return getImage().getHeight();
}
}
This separates some of management of key elements, allowing for a more flexible design. You might have a lot more entities, one's which could move, ones which could not, some which are painted, some which are not, but all which provide support for the core engine to get work done.
Once you have that you can start defining some core entities you need
public class ShipEntity extends AbstractImageEntity {
private BufferedImage ship;
public ShipEntity(GameSpace gameSpace) throws IOException {
ship = ImageIO.read(getClass().getResource("/resources/ship.png"));
setY(gameSpace.getGameSpace().height - getBounds().height);
setX((gameSpace.getGameSpace().width - getBounds().width) / 2);
}
#Override
public BufferedImage getImage() {
return ship;
}
#Override
public void paint(GameSpace gameSpace, Graphics2D g2d) {
g2d.drawImage(ship, getX(), getY(), gameSpace);
}
#Override
public boolean update(GameSpace gameSpace) {
int x = getX();
if (gameSpace.hasInput(Input.LEFT)) {
x -= 2;
}
if (gameSpace.hasInput(Input.RIGHT)) {
x += 2;
}
if (x < 0) {
x = 0;
} else if (x + getWidth() > gameSpace.getGameSpace().width) {
x = gameSpace.getGameSpace().width - getWidth();
}
setX(x);
return true;
}
}
public class InvaderEntity extends AbstractImageEntity {
private BufferedImage invader;
public InvaderEntity() throws IOException {
invader = ImageIO.read(getClass().getResource("/resources/Invader.png"));
}
#Override
protected BufferedImage getImage() {
return invader;
}
#Override
public void paint(GameSpace gameSpace, Graphics2D g2d) {
g2d.drawImage(invader, getX(), getY(), gameSpace);
}
#Override
public boolean update(GameSpace gameSpace) {
return true;
}
}
public class ProjectileEntity extends AbstractEntity {
private int delta;
public ProjectileEntity(int delta) {
this.delta = delta;
}
#Override
protected int getWidth() {
return 10;
}
#Override
protected int getHeight() {
return 10;
}
#Override
public void paint(GameSpace gameSpace, Graphics2D g2d) {
g2d.setColor(Color.RED);
int width = getWidth();
int height = getHeight();
g2d.fillOval(getX() - width / 2, getY() - height / 2, width, height);
}
#Override
public boolean update(GameSpace gameSpace) {
int y = getY() + delta;
setY(getY() + delta);
return y + getHeight() >= 0 && y + getHeight() <= gameSpace.getGameSpace().height;
}
}
Basically, they contain the logic required for getting their jobs done.
Finally, you need to setup the actual game UI
public class GamePane extends JPanel implements GameSpace {
private Set<Input> inputs;
private Entity playerEntity;
private List<Entity> projectileEntities;
private List<Entity> invaderEntities;
private long timeOfLastProjectile = -1;
public GamePane() throws IOException {
setBackground(Color.BLACK);
inputs = new HashSet<>(2);
playerEntity = new ShipEntity(this);
projectileEntities = new ArrayList<>(25);
invaderEntities = new ArrayList<>(25);
InvaderEntity invader = new InvaderEntity();
invader.setX((getGameSpace().width - invader.getBounds().width) / 2);
invader.setY((getGameSpace().height - invader.getBounds().height) / 2);
invaderEntities.add(invader);
addKeyBinding(Input.LEFT, "left", KeyEvent.VK_LEFT);
addKeyBinding(Input.RIGHT, "right", KeyEvent.VK_RIGHT);
addKeyBinding(Input.SPACE, "space", KeyEvent.VK_SPACE);
Timer timer = new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateState();
processCollisions();
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void updateState() {
playerEntity.update(this);
if (hasInput(Input.SPACE)) {
long time = System.currentTimeMillis() - timeOfLastProjectile;
if (time < 0 || time > 1000) {
timeOfLastProjectile = System.currentTimeMillis();
Rectangle bounds = playerEntity.getBounds();
ProjectileEntity projectile = new ProjectileEntity(-1);
int x = bounds.x + ((bounds.width - projectile.getWidth()) / 2);
int y = bounds.y - projectile.getHeight();
projectile.setX(x);
projectile.setY(y);
projectileEntities.add(projectile);
}
}
for (Entity entity : invaderEntities) {
entity.update(this);
}
List<Entity> outOfBounds = new ArrayList<>(25);
for (Entity entity : projectileEntities) {
if (!entity.update(this)) {
outOfBounds.add(entity);
}
}
projectileEntities.removeAll(outOfBounds);
}
protected void processCollisions() {
Set<Entity> hitInvaders = new HashSet<>(25);
Set<Entity> hitProjectiles = new HashSet<>(25);
for (Entity invader : invaderEntities) {
for (Entity projectile : projectileEntities) {
if (projectile.getBounds().intersects(invader.getBounds())) {
// Maybe lots of cool explosiions
hitInvaders.add(invader);
hitProjectiles.add(projectile);
}
}
}
invaderEntities.removeAll(hitInvaders);
projectileEntities.removeAll(hitProjectiles);
}
protected void addKeyBinding(Input input, String name, int virtualKey) {
ActionMap am = getActionMap();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name + ".pressed");
im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name + ".released");
am.put(name + ".pressed", new KeyAction(inputs, input, true));
am.put(name + ".released", new KeyAction(inputs, input, false));
}
#Override
public Dimension getGameSpace() {
return getPreferredSize();
}
#Override
public boolean hasInput(Input input) {
return inputs.contains(input);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
playerEntity.paint(this, g2d);
g2d.dispose();
for (Entity entity : invaderEntities) {
g2d = (Graphics2D) g.create();
entity.paint(this, g2d);
g2d.dispose();
}
for (Entity entity : projectileEntities) {
g2d = (Graphics2D) g.create();
entity.paint(this, g2d);
g2d.dispose();
}
}
}
public class KeyAction extends AbstractAction {
private Input input;
private Set<Input> inputs;
private boolean pressed;
public KeyAction(Set<Input> inputs, Input input, boolean pressed) {
this.input = input;
this.inputs = inputs;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
if (pressed) {
inputs.add(input);
} else {
inputs.remove(input);
}
}
}
Now you could go a little further and generate an "engine" class which controls the entities and performs all the required updating, but I'm lazy ;)
That's a really rough idea of some the basic concepts you need to develop to be able to move forward, hope it helps
My head gets so messed up working with this right-to-left stuff that I'm not even sure where the problem is, but essentially, I found scroll bars to seem "not quite right" when set to right-to-left orientation. I boiled this down to a test program, which works like this:
The scroll bars start at UP and LEFT (which feels wrong) and you can see purple.
If you resize the window to fit everything, you see that the red is in the upper left.
If you resize the window smaller and move the scroll bars to UP and LEFT, you get purple, which seems wrong.
If the scroll bars are UP and RIGHT, you get the red.
Swiping on the mouse moves the scroll bar in the right position but seems to move the contents in the wrong direction.
I can't tell which bits are backwards and which bits are right, but it seems vaguely correct that the viewport start at the upper right, but the scrollbar being on the left is surely inconsistent. However, if I do try to flip the rendering of the scroll bar, the bar itself doesn't get painted, as if Aqua has detected me trying to cheat somehow.
AquaScrollBarUI does not contain a single call to isLeftToRight(), but maybe the scroll bar is fine and it's the pane itself which is wrong?
I assume this is a Swing bug, so I'll probably be reporting it, but is there an elegant solution to this mess? I am writing a custom look and feel which subclasses Aqua, so that I can put this sort of fix as close as possible to Aqua.
import javax.swing.*;
import java.awt.*;
public class ScrollPaneOrientationTest implements Runnable {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new ScrollPaneOrientationTest());
}
#Override
public void run() {
JPanel view = new ViewPanel();
JScrollPane scrollPane = new JScrollPane(view);
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.add(scrollPane, BorderLayout.CENTER);
frame.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class ViewPanel extends JPanel implements Scrollable {
private static final int SIZE = 1000;
private final Paint paint = new GradientPaint(new Point(0, 0), Color.RED, new Point(SIZE, SIZE), Color.BLUE);
#Override
protected void paintComponent(Graphics g) {
((Graphics2D) g).setPaint(paint);
((Graphics2D) g).fill(new Rectangle(0, 0, SIZE, SIZE));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(400, 400);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 8;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 400;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
}
Here's the solution I'm going with initially. I guess I'll accept my own answer if a long time passes with no better options.
public class HaquaScrollPaneUI extends AquaScrollPaneUI {
#NotNull
#SuppressWarnings("UnusedDeclaration") // called via reflection
public static ComponentUI createUI(JComponent component) {
return new HaquaScrollPaneUI();
}
#Override
protected MouseWheelListener createMouseWheelListener() {
return new FixedXYMouseWheelHandler();
}
protected class FixedXYMouseWheelHandler extends XYMouseWheelHandler {
#Override
public void mouseWheelMoved(MouseWheelEvent event) {
if (event.isShiftDown() &&
!scrollpane.getComponentOrientation().isLeftToRight()) {
// fixes mouse wheel over scroll pane
event = new MouseWheelEvent(
event.getComponent(), event.getID(), event.getWhen(),
event.getModifiers(),
event.getX(), event.getY(),
event.getXOnScreen(), event.getYOnScreen(),
event.getClickCount(), event.isPopupTrigger(),
event.getScrollType(),
-event.getScrollAmount(),
-event.getWheelRotation(),
-event.getPreciseWheelRotation());
}
super.mouseWheelMoved(event);
}
}
}
public class HaquaScrollBarUI extends AquaScrollBarUI {
#NotNull
#SuppressWarnings("UnusedDeclaration") // called via reflection
public static ComponentUI createUI(JComponent component) {
return new HaquaScrollBarUI();
}
#Override
public void update(Graphics graphics, JComponent component) {
if (fScrollBar.getOrientation() == Adjustable.HORIZONTAL &&
!fScrollBar.getComponentOrientation().isLeftToRight()) {
// fixes painting direction
Graphics2D graphicsCopy = (Graphics2D) graphics.create();
try {
graphicsCopy.translate(fScrollBar.getWidth() - 1, 0);
graphicsCopy.scale(-1, 1);
super.update(graphicsCopy, component);
} finally {
graphicsCopy.dispose();
}
} else {
super.update(graphics, component);
}
}
#Override
protected TrackListener createTrackListener() {
// fixes mouse behaviour
return new FlippedTrackListener();
}
private class FlippedTrackListener extends TrackListener {
#Override
public void mouseReleased(MouseEvent event) {
super.mouseReleased(flipEvent(event));
}
#Override
public void mousePressed(MouseEvent event) {
super.mousePressed(flipEvent(event));
}
#Override
public void mouseDragged(MouseEvent event) {
super.mouseDragged(flipEvent(event));
}
// These ones aren't used, but I figured it would be sane
// to override them anyway, just in case.
#Override
public void mouseClicked(MouseEvent event) {
super.mouseClicked(flipEvent(event));
}
#Override
public void mouseEntered(MouseEvent event) {
super.mouseEntered(flipEvent(event));
}
#Override
public void mouseExited(MouseEvent event) {
super.mouseExited(flipEvent(event));
}
#Override
public void mouseWheelMoved(MouseWheelEvent event) {
super.mouseWheelMoved(flipEvent(event));
}
#Override
public void mouseMoved(MouseEvent event) {
super.mouseMoved(flipEvent(event));
}
private MouseEvent flipEvent(MouseEvent event) {
if (fScrollBar.getOrientation() == Adjustable.HORIZONTAL &&
!fScrollBar.getComponentOrientation().isLeftToRight()) {
int x = fScrollBar.getWidth() - 1 - event.getX();
int y = event.getY();
Point scrollBarLocationOnScreen = fScrollBar.getLocationOnScreen();
int xOnScreen = x + scrollBarLocationOnScreen.x;
int yOnScreen = y + scrollBarLocationOnScreen.y;
return new MouseEvent(
event.getComponent(), event.getID(), event.getWhen(),
event.getModifiersEx(),
x, y, xOnScreen, yOnScreen,
event.getClickCount(), event.isPopupTrigger(), event.getButton());
} else {
return event;
}
}
private MouseWheelEvent flipEvent(MouseWheelEvent event) {
if (fScrollBar.getOrientation() == Adjustable.HORIZONTAL &&
!fScrollBar.getComponentOrientation().isLeftToRight()) {
int x = event.getX();
x = fScrollBar.getWidth() - 1 - x;
int y = event.getY();
Point scrollBarLocationOnScreen = fScrollBar.getLocationOnScreen();
int xOnScreen = x + scrollBarLocationOnScreen.x;
int yOnScreen = y + scrollBarLocationOnScreen.y;
return new MouseWheelEvent(
event.getComponent(), event.getID(), event.getWhen(),
event.getModifiersEx(),
x, y, xOnScreen, yOnScreen,
event.getClickCount(), event.isPopupTrigger(),
event.getScrollType(), event.getScrollAmount(),
event.getWheelRotation(),
event.getPreciseWheelRotation());
} else {
return event;
}
}
}
}
I'm creating a Frogger-like game and my sprite(frog)(image), if you will, is created but does not move on my keypress, and I believe the error is occurring somewhere in the draw() method for the actual frog.java class. This is my own project and I'm trying to incorporate polymorphism and inheritance. Is there a way to move my sprite using the draw and move methods that I have, else what would be the correct way to do so. This is being done in Netbeans unfortunately.
import java.awt.*;
import java.awt.event.*;
public class FroggerForm extends Frame implements ActionListener
{
private Frog frog;
private javax.swing.Timer moveTimer = new javax.swing.Timer(500, this);
/**
Creates new form FroggerForm
*/
public FroggerForm()
{
initComponents();
this.setSize(400, 400);
frog = new Frog(froggerPanel);
moveTimer.start();
}
/**
This method is called from within the constructor to initialize the form.
WARNING: Do NOT modify this code. The content of this method is always
regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents()
{
froggerPanel = new java.awt.Panel();
setMinimumSize(new java.awt.Dimension(500, 500));
addWindowListener(new java.awt.event.WindowAdapter()
{
public void windowClosing(java.awt.event.WindowEvent evt)
{
exitForm(evt);
}
});
setLayout(null);
froggerPanel.setBackground(new java.awt.Color(190, 190, 190));
froggerPanel.setPreferredSize(new java.awt.Dimension(400, 400));
froggerPanel.addKeyListener(new java.awt.event.KeyAdapter()
{
public void keyPressed(java.awt.event.KeyEvent evt)
{
keyAdapter(evt);
}
});
add(froggerPanel);
froggerPanel.setBounds(100, 0, 400, 400);
pack();
}// </editor-fold>
/**
Exit the Application
*/
private void exitForm(java.awt.event.WindowEvent evt) {
System.exit(0);
}
private void keyAdapter(java.awt.event.KeyEvent evt)
{
frog.hide();
if(evt.getSource() == KeyEvent.VK_UP)
frog.move(0, -10);
else if(evt.getSource() == KeyEvent.VK_DOWN)
frog.move(0, 10);
else if(evt.getSource() == KeyEvent.VK_RIGHT)
frog.move(10, 0);
else if(evt.getSource() == KeyEvent.VK_LEFT)
frog.move(-10 , 0);
}
/**
#param args the command line arguments
*/
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
new FroggerForm().setVisible(true);
}
});
}
// Variables declaration - do not modify
private java.awt.Panel froggerPanel;
// End of variables declaration
#Override
public void actionPerformed(ActionEvent ae)
{
frog.draw();
frog.move();
}
}
public abstract class PFigure implements Comparable
{
protected int x, y; // Current position of the figure
protected int width, height; // Drawn (displayed) this size
protected int priority; // Can use to determine "winner"
protected Panel panel; // Panel the figure lives on
public PFigure ( int startX, int startY, int _width, int _height,
int pr, Panel p )
{
x = startX;
y = startY;
width = _width;
height = _height;
priority = pr;
panel = p;
}
// Can use this in "battles", which figures is "greater"
public int compareTo(Object o)
{
if( o instanceof PFigure )
return priority - ((PFigure)o).priority;
return Integer.MAX_VALUE;
}
// Has "this" figure collided with p?
public boolean collidedWith ( PFigure p )
{
if ( p == null )
return false;
return ( x + width ) >= p.x && ( p.x + p.width ) >= x &&
( y + height ) >= p.y && ( p.y + p.height ) >= y;
}
// Can be used for moving by keyboard or mouse
public void move ( int deltaX, int deltaY )
{
x = x + deltaX;
y = y + deltaY;
}
public void hide()
{
Graphics g = panel.getGraphics();
Color oldColor = g.getColor();
g.setColor(panel.getBackground() );
g.fillRect(x, y, width, height);
g.setColor(oldColor);
}
// Can be automatic move, for example, called based on timer
public void move()
{
}
// Draw the figure.
// Each derived class will write their own drawing method.
// The first line should be:
// Graphics g = panel.getGraphics();
abstract public void draw();
}
import java.awt.*;
import java.io.File;
import javax.imageio.ImageIO;
public class Frog extends PFigure
{
private Image frogImg;
public Frog(Panel p)
{
super(200, 350, 30, 30, 0, p);
try
{
File file = new File("frogger.png");
frogImg = ImageIO.read(file);
}
catch ( Exception e )
{
System.out.println("Crashing: " + e);
}
}
#Override
public void draw()
{
if(frogImg != null)
{
Graphics g = panel.getGraphics();
g.drawImage(frogImg, x, y, width, height, null);
}
}
#Override
public void move(int deltaX, int deltaY)
{
if ( x <= 0)
x = 1;
else if(x >= panel.getSize().width)
x = panel.getSize().width - 1;
else if(y >= panel.getSize().height)
y = panel.getSize().height - 1;
x += deltaX;
y += deltaY;
}
}
I believe your first error is in the use of KeyListener.
KeyListener will only respond to key strokes when the component it is registered to has focus AND is focusable.
Use Key Bindings instead, they allow you to define the focus level where key events will be generated
Updated
As an example...
import java.awt.Dimension;
import java.awt.EventQueue;
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.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class WizardFireball {
public static void main(String[] args) {
new WizardFireball();
}
public WizardFireball() {
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 GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
private GameModel model;
public GamePane() {
try {
model = new DefaultModel();
ActionMap am = getActionMap();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "fire");
am.put("up-pressed", new KeyPressedAction(getModel(), Key.UP));
am.put("down-pressed", new KeyPressedAction(getModel(), Key.DOWN));
am.put("left-pressed", new KeyPressedAction(getModel(), Key.LEFT));
am.put("right-pressed", new KeyPressedAction(getModel(), Key.RIGHT));
am.put("up-released", new KeyReleasedAction(getModel(), Key.UP));
am.put("down-released", new KeyReleasedAction(getModel(), Key.DOWN));
am.put("left-released", new KeyReleasedAction(getModel(), Key.LEFT));
am.put("right-released", new KeyReleasedAction(getModel(), Key.RIGHT));
am.put("fire", new FireBallAction(getModel()));
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Asset asset : getModel().getAssets()) {
asset.update(GamePane.this);
}
repaint();
}
});
timer.start();
}
public GameModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Asset asset : getModel().getAssets()) {
asset.paint(this, g2d);
}
g2d.dispose();
}
}
public enum Key {
LEFT,
RIGHT,
UP,
DOWN
}
public interface GameModel {
public void keyPressed(Key key);
public void keyReleased(Key key);
public boolean isKeyPressed(Key key);
public void addFireball();
public void remove(Asset asset);
public Wizard getWizard();
public Iterable<Asset> getAssets();
}
public class DefaultModel implements GameModel {
private Set<Key> keys;
private Wizard wizard;
private List<Asset> assets;
public DefaultModel() throws IOException {
keys = new HashSet<>(25);
assets = new ArrayList<>(25);
wizard = new Wizard();
}
#Override
public void keyPressed(Key key) {
keys.add(key);
}
#Override
public void keyReleased(Key key) {
keys.remove(key);
}
#Override
public boolean isKeyPressed(Key key) {
return keys.contains(key);
}
#Override
public Wizard getWizard() {
return wizard;
}
#Override
public void addFireball() {
try {
assets.add(new Fireball(getWizard()));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public void remove(Asset asset) {
assets.remove(asset);
}
#Override
public Iterable<Asset> getAssets() {
List<Asset> proxy = new ArrayList<>(assets);
proxy.add(wizard);
return proxy;
}
}
public abstract class AbstractGameAction extends AbstractAction {
private GameModel model;
public AbstractGameAction(GameModel model) {
this.model = model;
}
public GameModel getModel() {
return model;
}
}
public abstract class AbstractKeyAction extends AbstractGameAction {
private Key key;
public AbstractKeyAction(GameModel model, Key key) {
super(model);
this.key = key;
}
public Key getKey() {
return key;
}
}
public class KeyPressedAction extends AbstractKeyAction {
public KeyPressedAction(GameModel model, Key key) {
super(model, key);
}
#Override
public void actionPerformed(ActionEvent e) {
getModel().keyPressed(getKey());
}
}
public class KeyReleasedAction extends AbstractKeyAction {
public KeyReleasedAction(GameModel model, Key key) {
super(model, key);
}
#Override
public void actionPerformed(ActionEvent e) {
getModel().keyReleased(getKey());
}
}
public class FireBallAction extends AbstractGameAction {
public FireBallAction(GameModel model) {
super(model);
}
#Override
public void actionPerformed(ActionEvent e) {
getModel().addFireball();
}
}
public interface Asset {
public void paint(GamePane surface, Graphics2D g2d);
public void update(GamePane surface);
public Rectangle getBounds();
}
public class Fireball implements Asset {
private final BufferedImage fireball;
private final Rectangle bounds;
public Fireball(Wizard wizard) throws IOException {
fireball = ImageIO.read(getClass().getResource("/Fireball.png"));
bounds = new Rectangle();
bounds.x = wizard.getBounds().x + wizard.getBounds().width;
bounds.y = wizard.getBounds().y + (wizard.getBounds().height - fireball.getHeight());
bounds.setSize(fireball.getWidth(), fireball.getHeight());
}
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public void paint(GamePane surface, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.drawImage(fireball, bounds.x, bounds.y, surface);
}
#Override
public void update(GamePane surface) {
Rectangle bounds = getBounds();
bounds.x += 8;
if (bounds.x > surface.getWidth()) {
surface.getModel().remove(this);
}
}
}
public class Wizard implements Asset {
private final BufferedImage wizard;
private final Rectangle bounds;
private boolean initialised = false;
public Wizard() throws IOException {
wizard = ImageIO.read(getClass().getResource("/Wizard.png"));
bounds = new Rectangle(wizard.getWidth(), wizard.getHeight());
}
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public void paint(GamePane surface, Graphics2D g2d) {
Rectangle bounds = getBounds();
Point point = bounds.getLocation();
if (!initialised) {
point = new Point(5, (surface.getHeight() - bounds.height) / 2);
bounds.setLocation(point);
initialised = true;
}
g2d.drawImage(wizard, point.x, point.y, surface);
}
#Override
public void update(GamePane surface) {
Rectangle bounds = getBounds();
Point point = bounds.getLocation();
if (initialised) {
GameModel model = surface.getModel();
if (model.isKeyPressed(Key.UP)) {
point.y -= 4;
} else if (model.isKeyPressed(Key.DOWN)) {
point.y += 4;
}
if (point.y < 0) {
point.y = 0;
} else if (point.y + bounds.height > surface.getHeight()) {
point.y = surface.getHeight() - bounds.height;
}
bounds.setLocation(point);
}
}
}
}
package Objects;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class PowerBar extends JPanel implements ActionListener, KeyListener {
private double x;
private final double y = 420;
private double xv = 0;
private final int width, height;
private boolean left = false, right = false;
private Timer t = new Timer(5, this);
public PowerBar(JPanel j) {
width = j.getWidth();
height = j.getHeight();
x = 180;
t.start();
this.addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void move() {
}
public void powerbarPosition() {
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double(x, y, 100, 15);
g2.setColor(Color.DARK_GRAY);
g2.fill(rect);
}
#Override
public void actionPerformed(ActionEvent ae) {
x += xv;
repaint();
}
#Override
public void keyPressed(KeyEvent ev) {
if (ev.getKeyCode() == KeyEvent.VK_LEFT) {
left = !left;
if (left == true) {
xv = -2;
} else if (ev.getKeyCode() == KeyEvent.VK_RIGHT) {
right = !right;
if (right == true) {
xv = 2;
}
}
}
}
#Override
public void keyReleased(KeyEvent ev) {
}
#Override
public void keyTyped(KeyEvent ke) {
}
}
Im kinda new to programming and this is my very first game to program. Im trying to get the power bar to move with the left and right key but there is no response from these keys when pressed. The power bar is drawn in a separate class called DrawMain which uses the paintComponent method.
A JPanel doesn't listen to Action, it is a container rather than a controller. Hence, it doesn't have a addActionListener(actionListener) function.
For listening to Key event- presse, release etc, the target component must have focus. However, you can invoke requestFocusInWindow() on target component to gain focus if you wish to.
It is preferable not to implement a listener to class which doesn't listen to such listener, in your context it is ActionListener. Make use of inline anonymous class or declare another class implementing the ActionListener
As #AndrewThompson and other swing gigs of stackoverflow will suggest, It is preferable using Key binding using key input map and action input map to a component which is rather higher level implementation. Try avoiding make use of lower level AWT implementation KeyListener as much as possible.
Check the official tutorial page:
How to use Key Bingings
How to Write a Key Listener
How to Write an Action Listener
1) Your ActionListener doesn't attached to JPanel and components on it, because of that it doesn't work.
2) Don't use KeyListener instead of use Key Bindings:
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0), "doSomething1");
getActionMap().put("doSomething1", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
left = !left;
if (left == true) {
xv = -2;
}
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0), "doSomething2");
getActionMap().put("doSomething2", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
right = !right;
if (right == true) {
xv = 2;
}
}
});