I have a project I am working on in that I have to create two objects, I would like the big rectangle(BLUE) to move around the frame every time I press the arrow keys on my keyboard while the small rectangle(RED) is moving away, once the big square touches/tags the small rectangle, screen refreshes and I can move the big rectangle again to chase down the small rectangle. Below is my main class, and IT class where I have implemented my two shapes.
The goal is to have the two rectangles, small rectangle runs away in the frame every time the big rectangle comes close until it's tagged. Would also have to add some kind of score panel on the frame to show updated scores and a timer to count down when the player starts playing.
I need help having the two rectangles move differently and not on top of each other. I would like the second rectangle to move away every time the first rectangle comes close to it moving around the Frame
`
My class IT
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class IT extends JPanel implements ActionListener, KeyListener {
Timer shapeTimer = new Timer(5, this);
public double xPos = 0, yPos = 0, movementX = 0, movementY = 0;
public int rectSize = 50;
public int rectSize2 = 35;
public int windowWidth;
int windowHeight;
public int xBound;
public int yBound;
public IT(int w, int h){
shapeTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
windowWidth = w;
windowHeight = h;
xBound = (windowWidth - rectSize);
yBound = (windowHeight - rectSize);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D movableRect = new Rectangle2D.Double(xPos, yPos, rectSize, rectSize);
g2.setColor(Color.BLUE);
g2.draw(movableRect);
g2.fill(movableRect);
Rectangle2D movableRect2 = new Rectangle2D.Double(xPos, yPos, rectSize2, rectSize2);
g2.setColor(Color.RED);
g2.draw(movableRect2);
g2.fill(movableRect2);
}
public void actionPerformed(ActionEvent e){
repaint();
xPos += movementX;
yPos += movementY;
}
public void moveUp(){
if (yPos == 0){
movementY = 0;
movementX = 0;
}
movementY = -0.5;
movementX = 0;
}
public void moveDown(){
if (yPos == yBound){
movementY = 0;
movementX = 0;
}
movementY = 0.5;
movementX = 0;
}
public void moveLeft()
{
if (xPos == 0){
movementY = 0;
movementX = 0;
}
movementX = -0.5;
movementY = 0;
}
public void moveRight(){
if (xPos == xBound)
{
movementY = 0;
movementX = 0;
}
movementX = 0.5;
movementY = 0;
}
public void enlargeSquare(){
rectSize++;
rectSize2++;
}
public void shrinkSquare(){
rectSize--;
rectSize2--;
}
public void keyPressed(KeyEvent e){
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP){
moveUp();
}
if (keyCode == KeyEvent.VK_DOWN){
moveDown();
}
if (keyCode == KeyEvent.VK_RIGHT){
moveRight();
}
if (keyCode == KeyEvent.VK_LEFT){
moveLeft();
}
if (keyCode == KeyEvent.VK_OPEN_BRACKET)
{
shrinkSquare();
}
if (keyCode == KeyEvent.VK_CLOSE_BRACKET)
{
enlargeSquare();
}
}
public void keyTyped(KeyEvent e){
}
public void keyReleased(KeyEvent e){
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP){
movementX = 0;
movementY = 0;
}
if (keyCode == KeyEvent.VK_DOWN){
movementX = 0;
movementY = 0;
}
if (keyCode == KeyEvent.VK_RIGHT){
movementX = 0;
movementY = 0;
}
if (keyCode == KeyEvent.VK_UP){
movementX = 0;
movementY = 0;
}
}
}
`
MainTester class
`
import javax.swing.*;
import javax.swing.JFrame;
public class MainTester {
public static void main(String[] args) {
// TODO Auto-generated method stub
int frameWidth = 850;
int frameHeight = 650;
JFrame frmMain = new JFrame();
frmMain.setSize(frameWidth, frameHeight);
IT it = new IT(frameWidth, frameHeight);
frmMain.add(it);
frmMain.setVisible(true);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmMain.setTitle("Tag Game");
}
}
`
Use key bindings, seriously, this is going to solve a swagger of issues related to KeyListener.
Decouple and seperate your logic. Each "entity" should be a self contained unit of work. In your case, they should contain information about their color, location and size at a minimum.
For simplicity, I started out with something which could just be painted...
public interface Entity {
public void paint(Graphics2D g2d);
}
Now, you could have a lot of different interfaces which reflect this which can be painted, moved, controlled, represents effects or what ever you need - then your classes should implement the interfaces they need. Then, your engine just deals with the "concepts" it wants to - need all "paintable" entities, need all "movable" entities, etc, when it needs to.
Next I created a concept of a "player". A player is a "paintable" entity, but which can be controlled by the player in some way, so it takes the current state of the "input"s and updates itself based on those states (more about that to come)
public static class PlayerEntity implements Entity {
protected static final int DELTA = 2;
private Rectangle bounds = new Rectangle(0, 0, 35, 35);
private Color fillColor;
public PlayerEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void update(Set<PlayerAction> actions, Dimension size) {
Rectangle currentBounds = getBounds();
int x = currentBounds.x;
int y = currentBounds.y;
if (actions.contains(PlayerAction.UP)) {
y -= DELTA;
}
if (actions.contains(PlayerAction.DOWN)) {
y += DELTA;
}
if (actions.contains(PlayerAction.LEFT)) {
x -= DELTA;
}
if (actions.contains(PlayerAction.RIGHT)) {
x += DELTA;
}
if (y < 0) {
y = 0;
}
if (y + currentBounds.height > size.height) {
y = size.height - currentBounds.height;
}
if (x < 0) {
x = 0;
}
if (x + currentBounds.width > size.width) {
x = size.width - currentBounds.width;
}
getBounds().setLocation(x, y);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
For reference, PlayerAction represents all the valid actions which can be performed by the player, for simplicity sake, I've just stuck to movement:
public enum PlayerAction {
UP, DOWN, LEFT, RIGHT;
}
Next I created a "monster" entity, in this case, the "monster" will always try and follow the player, this implementation is loosely based on Java: Move image towards mouse position
public static class MonsterEntity implements Entity {
protected static final int DELTA = 1;
private Rectangle bounds = new Rectangle(0, 0, 15, 15);
private Color fillColor;
public MonsterEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void moveTowards(Point target) {
Rectangle bounds = getBounds();
Point center = getCenter();
int xDelta = target.x < center.x ? -DELTA : DELTA;
int yDelta = target.y < center.y ? -DELTA : DELTA;
getBounds().setLocation(bounds.x + xDelta, bounds.y + yDelta);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
The monster is always trying to move it's center to the target location (which will eventually be the center of the player)
Now, this is where things become complicated.
We need:
A renderable surface onto which we can paint the player and monster(s)
Input bindings
A "game loop" to update the state of the entities and schedule repaints
For simplicity, I started with a JPanel, made use of Swing Timer and the key bindings API.
public class MainPane extends JPanel {
// This represents the "input bindings", these represent
// abstract actions which can be applied to the player
// or game state.
private enum InputKey {
PRESSED_UP, PRESSED_DOWN, PRESSED_LEFT, PRESSED_RIGHT,
RELEASED_UP, RELEASED_DOWN, RELEASED_LEFT, RELEASED_RIGHT;
public KeyStroke getKeyStroke() {
switch (this) {
case PRESSED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false);
case PRESSED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false);
case PRESSED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false);
case PRESSED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false);
case RELEASED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true);
case RELEASED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true);
case RELEASED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true);
case RELEASED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true);
}
return null;
}
}
private PlayerEntity playerEntity;
private MonsterEntity monsterEntity;
private Timer timer;
private Set<PlayerAction> actions = new HashSet<PlayerAction>();
public MainPane() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(InputKey.PRESSED_UP.getKeyStroke(), InputKey.PRESSED_UP);
inputMap.put(InputKey.PRESSED_DOWN.getKeyStroke(), InputKey.PRESSED_DOWN);
inputMap.put(InputKey.PRESSED_LEFT.getKeyStroke(), InputKey.PRESSED_LEFT);
inputMap.put(InputKey.PRESSED_RIGHT.getKeyStroke(), InputKey.PRESSED_RIGHT);
inputMap.put(InputKey.RELEASED_UP.getKeyStroke(), InputKey.RELEASED_UP);
inputMap.put(InputKey.RELEASED_DOWN.getKeyStroke(), InputKey.RELEASED_DOWN);
inputMap.put(InputKey.RELEASED_LEFT.getKeyStroke(), InputKey.RELEASED_LEFT);
inputMap.put(InputKey.RELEASED_RIGHT.getKeyStroke(), InputKey.RELEASED_RIGHT);
actionMap.put(InputKey.PRESSED_UP, new MoveAction(actions, PlayerAction.UP, true));
actionMap.put(InputKey.PRESSED_DOWN, new MoveAction(actions, PlayerAction.DOWN, true));
actionMap.put(InputKey.PRESSED_LEFT, new MoveAction(actions, PlayerAction.LEFT, true));
actionMap.put(InputKey.PRESSED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, true));
actionMap.put(InputKey.RELEASED_UP, new MoveAction(actions, PlayerAction.UP, false));
actionMap.put(InputKey.RELEASED_DOWN, new MoveAction(actions, PlayerAction.DOWN, false));
actionMap.put(InputKey.RELEASED_LEFT, new MoveAction(actions, PlayerAction.LEFT, false));
actionMap.put(InputKey.RELEASED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, false));
Dimension size = getPreferredSize();
Point center = new Point((size.width - 35) / 2, (size.height - 35) / 2);
playerEntity = new PlayerEntity(Color.BLUE, center);
monsterEntity = new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15));
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
performTick();
}
});
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void performTick() {
playerEntity.update(actions, getSize());
monsterEntity.moveTowards(playerEntity.getCenter());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
playerEntity.paint(g2d);
monsterEntity.paint(g2d);
g2d.dispose();
}
}
Movement (or input) is controlled through the key bindings API, this triggers a MoveAction which updates a centralised state repository (which is then passed to the PlayerEntity so it apply the state accordingly).
For simplicity, I've only used a single Action, but you could make a couple, one representing "press/activate" or "release/deactivate"
public class MoveAction extends AbstractAction {
private Set<PlayerAction> actions;
private PlayerAction action;
private boolean activate;
public MoveAction(Set<PlayerAction> directions, PlayerAction direction, boolean activate) {
this.actions = directions;
this.action = direction;
this.activate = activate;
}
#Override
public void actionPerformed(ActionEvent e) {
if (activate) {
actions.add(action);
} else {
actions.remove(action);
}
}
}
See How to Use Actions for more details about actions.
But why follow this workflow?!
It decouples and decentralises a lot of the workflows. In fact, if you really wanted to, you could also seperate out the Timer and "paint" workflows to seperate classes, further decoupling the classes.
Key bindings solve all the issues related to KeyListener weirdness. It also decouples the input - want to add touch controls/buttons, no worries, it's done via Actions. Want to add joystick/controllers, no worries, it's done via Actions.
Want more monsters?
Change:
private MonsterEntity monsterEntity;
to:
private List<MonsterEntity> monsterEntitys = new ArrayList<>(32);
Change:
monsterEntity = new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15));
to:
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15)));
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(0, 0)));
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(size.width - 15, 0)));
monsterEntitys.add(new MonsterEntity(Color.RED, new Point(0, size.height - 15)));
Change:
monsterEntity.paint(g2d);
to:
for (MonsterEntity entity : monsterEntitys) {
entity.paint(g2d);
}
And now you have more monsters! Have fun with that!
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.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 Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum PlayerAction {
UP, DOWN, LEFT, RIGHT;
}
public class MainPane extends JPanel {
private enum InputKey {
PRESSED_UP, PRESSED_DOWN, PRESSED_LEFT, PRESSED_RIGHT,
RELEASED_UP, RELEASED_DOWN, RELEASED_LEFT, RELEASED_RIGHT;
public KeyStroke getKeyStroke() {
switch (this) {
case PRESSED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false);
case PRESSED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false);
case PRESSED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false);
case PRESSED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false);
case RELEASED_UP: return KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true);
case RELEASED_DOWN: return KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true);
case RELEASED_LEFT: return KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true);
case RELEASED_RIGHT: return KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true);
}
return null;
}
}
private PlayerEntity playerEntity;
private MonsterEntity monsterEntity;
private Timer timer;
private Set<PlayerAction> actions = new HashSet<PlayerAction>();
public MainPane() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(InputKey.PRESSED_UP.getKeyStroke(), InputKey.PRESSED_UP);
inputMap.put(InputKey.PRESSED_DOWN.getKeyStroke(), InputKey.PRESSED_DOWN);
inputMap.put(InputKey.PRESSED_LEFT.getKeyStroke(), InputKey.PRESSED_LEFT);
inputMap.put(InputKey.PRESSED_RIGHT.getKeyStroke(), InputKey.PRESSED_RIGHT);
inputMap.put(InputKey.RELEASED_UP.getKeyStroke(), InputKey.RELEASED_UP);
inputMap.put(InputKey.RELEASED_DOWN.getKeyStroke(), InputKey.RELEASED_DOWN);
inputMap.put(InputKey.RELEASED_LEFT.getKeyStroke(), InputKey.RELEASED_LEFT);
inputMap.put(InputKey.RELEASED_RIGHT.getKeyStroke(), InputKey.RELEASED_RIGHT);
actionMap.put(InputKey.PRESSED_UP, new MoveAction(actions, PlayerAction.UP, true));
actionMap.put(InputKey.PRESSED_DOWN, new MoveAction(actions, PlayerAction.DOWN, true));
actionMap.put(InputKey.PRESSED_LEFT, new MoveAction(actions, PlayerAction.LEFT, true));
actionMap.put(InputKey.PRESSED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, true));
actionMap.put(InputKey.RELEASED_UP, new MoveAction(actions, PlayerAction.UP, false));
actionMap.put(InputKey.RELEASED_DOWN, new MoveAction(actions, PlayerAction.DOWN, false));
actionMap.put(InputKey.RELEASED_LEFT, new MoveAction(actions, PlayerAction.LEFT, false));
actionMap.put(InputKey.RELEASED_RIGHT, new MoveAction(actions, PlayerAction.RIGHT, false));
Dimension size = getPreferredSize();
Point center = new Point((size.width - 35) / 2, (size.height - 35) / 2);
playerEntity = new PlayerEntity(Color.BLUE, center);
monsterEntity = new MonsterEntity(Color.RED, new Point(size.width - 15, size.height - 15));
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
performTick();
}
});
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void performTick() {
playerEntity.update(actions, getSize());
monsterEntity.moveTowards(playerEntity.getCenter());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
playerEntity.paint(g2d);
monsterEntity.paint(g2d);
g2d.dispose();
}
}
public class MoveAction extends AbstractAction {
private Set<PlayerAction> actions;
private PlayerAction action;
private boolean activate;
public MoveAction(Set<PlayerAction> directions, PlayerAction direction, boolean activate) {
this.actions = directions;
this.action = direction;
this.activate = activate;
}
#Override
public void actionPerformed(ActionEvent e) {
if (activate) {
actions.add(action);
} else {
actions.remove(action);
}
}
}
public interface Entity {
public void paint(Graphics2D g2d);
}
public static class MonsterEntity implements Entity {
protected static final int DELTA = 1;
private Rectangle bounds = new Rectangle(0, 0, 15, 15);
private Color fillColor;
public MonsterEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void moveTowards(Point target) {
Rectangle bounds = getBounds();
Point center = getCenter();
int xDelta = target.x < center.x ? -DELTA : DELTA;
int yDelta = target.y < center.y ? -DELTA : DELTA;
getBounds().setLocation(bounds.x + xDelta, bounds.y + yDelta);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
public static class PlayerEntity implements Entity {
protected static final int DELTA = 2;
private Rectangle bounds = new Rectangle(0, 0, 35, 35);
private Color fillColor;
public PlayerEntity(Color fillColor, Point location) {
this.fillColor = fillColor;
this.bounds.setLocation(location);
}
public Color getFillColor() {
return fillColor;
}
public Rectangle getBounds() {
return bounds;
}
public Point getCenter() {
return new Point((int)getBounds().getCenterX(), (int)getBounds().getCenterY());
}
public void update(Set<PlayerAction> actions, Dimension size) {
Rectangle currentBounds = getBounds();
int x = currentBounds.x;
int y = currentBounds.y;
if (actions.contains(PlayerAction.UP)) {
y -= DELTA;
}
if (actions.contains(PlayerAction.DOWN)) {
y += DELTA;
}
if (actions.contains(PlayerAction.LEFT)) {
x -= DELTA;
}
if (actions.contains(PlayerAction.RIGHT)) {
x += DELTA;
}
if (y < 0) {
y = 0;
}
if (y + currentBounds.height > size.height) {
y = size.height - currentBounds.height;
}
if (x < 0) {
x = 0;
}
if (x + currentBounds.width > size.width) {
x = size.width - currentBounds.width;
}
getBounds().setLocation(x, y);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getFillColor());
g2d.fill(getBounds());
}
}
}
Other considerations...
Right now the speed/delta is actually really high. I would consider making use of the Shape API, using things like Point2D and Rectangle2D which provide double based properties instead of int, which would give away to reduce the delta values and slow down the entities.
See Working with Geometry for some more details
I would like to have two JLabel (that create two circles) at the same time, and moveable with the keyboard.
However, only the green one Vert appears and not the blue one Bleu.
public class FenJeu extends JPanel{
int xPos;
int yPos;
public FenJeu(int r, int v, int b) {
JLabel image = new JLabel();
add(new Vert());
add(new Bleu());
image.setVisible(true);
image.setEnabled(true);
//image.setIcon(new ImageIcon("D:\\terrain.png"));
//image.setBounds(1,1,1680,1050);
//image.setPreferredSize(new Dimension(1680,1050));
add(image);
}
public class Vert extends JPanel {
public Vert() {
Action leftAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
xPos -= 50;
if (xPos < 0) {
xPos = 0;
}
repaint();
}
};
Action rightAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += 50;
if (xPos + 10 > getWidth()) {
xPos = getWidth() - 10;
}
repaint();
}
};
Action upAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
yPos -= 50;
if (yPos + 10 > getWidth()) {
yPos = getWidth() - 10;
}
repaint();
}
};
Action downAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += 50;
if (yPos < 0) {
yPos = 0;
}
repaint();
}
};
bindKeyStroke(WHEN_IN_FOCUSED_WINDOW, "move.left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), leftAction);
bindKeyStroke(WHEN_IN_FOCUSED_WINDOW, "move.right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), rightAction);
bindKeyStroke(WHEN_IN_FOCUSED_WINDOW, "move.up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), upAction);
bindKeyStroke(WHEN_IN_FOCUSED_WINDOW, "move.down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), downAction);
}
protected void bindKeyStroke(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 Dimension getPreferredSize() {
return new Dimension(1680, 1050);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
//int yPos = (getHeight() - 10) / 2;
//int xPos = (getWidth() -10) / 2;
g2d.setPaint(Color.GREEN);
g2d.fillOval(xPos, yPos, 50, 50);
g2d.dispose();
}
}
public class Bleu extends JPanel {
public Bleu() {
Action lefttAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
xPos -= 50;
if (xPos < 0) {
xPos = 0;
}
repaint();
}
};
Action righttAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += 50;
if (xPos + 10 > getWidth()) {
xPos = getWidth() - 10;
}
repaint();
}
};
Action uppAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
yPos -= 50;
if (yPos + 10 > getWidth()) {
yPos = getWidth() - 10;
}
repaint();
System.out.println("oui");
}
};
Action downnAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += 50;
if (yPos < 0) {
yPos = 0;
}
repaint();
}
};
bindKeyStroke1(WHEN_IN_FOCUSED_WINDOW, "move.leftt", KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0), lefttAction);
bindKeyStroke1(WHEN_IN_FOCUSED_WINDOW, "move.rightt", KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), righttAction);
bindKeyStroke1(WHEN_IN_FOCUSED_WINDOW, "move.upp", KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0), uppAction);
bindKeyStroke1(WHEN_IN_FOCUSED_WINDOW, "move.downn", KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), downnAction);
}
protected void bindKeyStroke1(int conditionn, String namee, KeyStroke keyStrokee, Action actionn) {
InputMap om = getInputMap(conditionn);
ActionMap pm = getActionMap();
om.put(keyStrokee, namee);
pm.put(namee, actionn);
}
protected void paintComponent1(Graphics f) {
super.paintComponent(f);
Graphics2D g3d = (Graphics2D) f.create();
//int yPos = (getHeight() - 10) / 2;
// int xPos = (getWidth() -10) / 2;
g3d.setPaint(Color.BLUE);
g3d.fillOval(xPos, yPos, 50, 50);
g3d.dispose();
}
}
}
Vert panel size is 1680x1050 (see Vert#getPreferredSize()), Bleu has no defined size, so minimal component size will be set.
You override JPanel's paintComponent methods for Vert and Bleu, however method name in Bleu is paintComponent1. If you correct method name blue circle will be drawn, but due to minimal size you will see just small piece of it. Default JPanel layout is FlowLayout, so your Bleu panel will be located on the right side after Vert one.
I am teaching myself 2D graphics in Java right now. I am trying to create a block-stacking game. So far, I am able to get the first row moving and stopping, but my aim is to generate a new row below the first while the first row stays in place. I feel like the fix for this is a simple concept I haven't learned yet. However, any advice will be appreciated. If you want to point me in the right direction to where I can teach myself 2D Graphics in general, I will appreciate that also.
My code for the JFrame is as follows:
public class BlockStacker extends JFrame {
public class AL extends KeyAdapter {
stack2 s2 = new stack2();
public void keyPressed(KeyEvent e){
int keyCode = e.getKeyCode();
}
}
public static void main(String[] args){
stack2 s = new stack2();
JFrame f = new JFrame();
f.add(s);
f.setVisible(true);
f.setSize(1920, 1080);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Block Stacker");
}
}
The code with the graphics and the keylistener is this:
public class stack2 extends JPanel implements ActionListener, KeyListener{
Timer t = new Timer(250, this);
double x, y, velX = 253;
public stack2() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
setBackground(Color.BLACK);
Color blue = new Color(0,0,255);
g2.setColor(blue);
Rectangle2D.Double rectangle = new Rectangle.Double(x + 210, y, 200, 100);
g2.fill(rectangle);
Rectangle2D.Double rectangle2 = new Rectangle.Double(x, y, 200, 100);
g2.fill(rectangle2);
Rectangle2D.Double rectangle3 = new Rectangle.Double(x + 420, y, 200, 100);
g2.fill(rectangle3);
}
public void actionPerformed(ActionEvent e) {
if (x < 0) {
velX = -velX;
}
x += velX;
repaint();
if (x < 10 || x > 1200) {
velX = -velX;
repaint();
}
}
public void space() {
velX = 0;
repaint();
}
public void space2() {
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_SPACE) {
space();
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_SPACE) {
space2();
}
}
}
There are multiple problems...
You attach the KeyListener to JFrame, but a JFrame has a JRootPane, content pane and it's content between it and the user, any of those components could have keyboard focus, preventing the frame from receiving key events
You create a instance of Stack in both the KeyAdapter and then the frame, so the instance of Stack you are trying to change isn't the version that's on the screen
KeyListener only responds to key events when the component that they are registed to are focus able and have focus...
Use key bindings instead? See How to use key bindings
I'm trying to create a brickbreakers game. Obviously i'm still struggling with the beginning of it. I need my "block" to move but if i press some key my code won't even go in the keyPressed()-method. Does anybody know why?
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class VGKernel extends JPanel implements KeyListener {
public Rectangle screen, ball, block; // The screen area and ball location/size.
public Rectangle bounds; // The boundaries of the drawing area.
public JFrame frame; // A JFrame to put the graphics into.
public VGTimerTask vgTask; // The TimerTask that runs the game.
public boolean down, right; // Direction of ball's travel.
public VGKernel(){
super();
screen = new Rectangle(0, 0, 600, 400);
ball = new Rectangle(0, 0, 20, 20);
block = new Rectangle(0, 350, 40, 10);
bounds = new Rectangle(0, 0, 600, 400); // Give some starter values.
frame = new JFrame("VGKernel");
vgTask = new VGTimerTask();
addKeyListener(this);
}
class VGTimerTask extends TimerTask{
public void run(){
moveBall();
frame.repaint();
}
}
// Now the instance methods:
public void paintComponent(Graphics g){
// Get the drawing area bounds for game logic.
bounds = g.getClipBounds();
// Clear the drawing area, then draw the ball.
g.clearRect(screen.x, screen.y, screen.width, screen.height);
g.fillRect(ball.x, ball.y, ball.width, ball.height);
g.fillRect(block.x, block.y, block.width, block.height);
}
public void moveBall(){
// Ball should really be its own class with this as a method.
if (right) ball.x+=1; // If right is true, move ball right,
else ball.x-=1; // otherwise move left.
if (down) ball.y+=1; // Same for up/down.
else ball.y-=1;
if (ball.x > (bounds.width - ball.width)) // Detect edges and bounce.
{ right = false; ball.x = bounds.width - ball.width; }
if (ball.y > (bounds.height - ball.height))
{ down = false; ball.y = bounds.height - ball.height;}
if (ball.x <= 0) { right = true; ball.x = 0; }
if (ball.y <= 0) { down = true; ball.y = 0; }
}
public void keyPressed(KeyEvent evt) {
if(evt.getKeyCode() == KeyEvent.VK_G && block.x > 0) {
block.x -= 10;
}
if(evt.getKeyCode() == KeyEvent.VK_H && block.x < 560) {
block.x += 10;
}
}
public void keyTyped(KeyEvent evt){ }
public void keyReleased(KeyEvent evt){ }
public static void main(String arg[]){
java.util.Timer vgTimer = new java.util.Timer(); // Create a Timer.
VGKernel panel = new VGKernel(); // Create and instance of our kernel.
// Set the intial ball movement direction.
panel.down = true;
panel.right = true;
// Set up our JFRame
panel.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.frame.setSize(panel.screen.width, panel.screen.height);
panel.frame.setContentPane(panel);
panel.frame.setVisible(true);
// Set up a timer to do the vgTask regularly.
vgTimer.schedule(panel.vgTask, 0, 10);
}
}
Put
setFocusable(true);
inside your constructor.
I am having a problem with threading. I am trying to recreate pong where there are 2 Action Listeners for 2 players. The 2 paddles move at the speed that I want them to move at. However, the "ball"(Rectangle object with an oval painted over it) is moving way too fast. I've tried slowing it down using a second thread in the Ball class, but that doesn't seem to work. Any help would be appreciated.
Here's my code:
public class BattleBallz extends JFrame implements Runnable {
AL keyListen1 = new AL();
AL2 keyListen2 = new AL2();
Image dbi;
Graphics dbg;
int x1,x2;
int ballX, ballY, direction;
Ball b1;
public BattleBallz(){
setTitle("Battle Ballz");
setSize(350, 400);
setResizable(false);
setVisible(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
addKeyListener(keyListen1);
addKeyListener(keyListen2);
x1 = 150;
x2 = 150;
int direction = 0 + (int)(Math.random() * ((2 - 0) + 1));
b1 = new Ball(direction);
Thread b = new Thread(b1);
b.start();
}
public void paint(Graphics g){
dbi = createImage(getWidth(), getHeight());
dbg = dbi.getGraphics();
paintComponent(dbg);
g.drawImage(dbi, 0, 0, this);
}
public void paintComponent(Graphics g){
Rectangle p1 = new Rectangle(x1,375,60,10);
g.setColor(Color.blue);
g.fillRect(x1, 375, 60, 10);
Rectangle p2 = new Rectangle(x2,45,60,10);
g.setColor(Color.red);
g.fillRect(x2,45,60,10);
b1.paintComponent(g, p1, p2);
repaint();
}
#Override
public void run() {
try {
while(true){
move();
Thread.sleep(5);
}
} catch (Exception e) {
// TODO: handle exception
}
}
public void move(){
x1 += keyListen1.getXdirection();
x2 += keyListen2.getXdirection();
if (x1<=0){
x1 =0;
}
if (x1>=305){
x1 = 305;
}
if (x2<=0){
x2=0;
}
if (x2>=305){
x2=305;
}
}
}
public class Ball extends JPanel implements Runnable {
boolean up,down,right,left;
int x = 150, y = 150 ;
public Ball(int direction){
if(direction ==1){
down = true;
}
if (direction ==0){
up = true;
}
}
public void paintComponent(Graphics g, Rectangle p1, Rectangle p2){
super.paintComponents(g);
if(down){
y++;
Rectangle ball = new Rectangle(x,y,49,49);
g.fillOval(x, y, 50, 50);
if (y>=385 || ball.intersects(p1)){
down = false;
up = true;
}
}
if (up){
y--;
Rectangle ball = new Rectangle(x,y,49,49);
g.fillOval(x, y, 50, 50);
if (y<=10 || ball.intersects(p2)){
up = false;
down = true;
}
}
}
#Override
public void run() {
// TODO Auto-generated method stub
try {
while (true){
Thread.sleep(30);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
use Swing Timer instead of plain Thread, you have an issue with Concurency in Swing, code posted to block Event Dispatch Thread, locked by Thread.sleep, endless block
use paintComponent but put there JPanel, 1st. code line inside should be super.paintComponent, otherwise paint is cumulated, override getPreferredSize for JPanel
use KeyBindings instead of KeyListener