How do I keep an object moving using arrow keys? - java

I am making a snake game, and I want my snake to be moving continuously once a key is pressed. So, I press the down key, and it keeps moving even if the key is released. Right now, it just moves while the key is being held down.
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
mySegment[0].moveSouth();
repaint();
}
else if (e.getKeyCode() == KeyEvent.VK_UP) {
mySegment[0].moveNorth();
repaint();
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT){
mySegment[0].moveWest();
repaint();
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT){
mySegment[0].moveEast();
repaint();
}
for (int a = 0; a < 10; a++) {
if (myFruit[a].distance (mySegment[0].getX(), mySegment[0].getY())
<= 20) {
myFruit[a].hide();
}
}
The "mySegment [0]" is the snake, and the "moveSouth" or whatever direction just moves it 5 pixels in that directin

Use a "game loop" to drive the animation. Since this looks to be possibly a Swing or AWT GUI, then your best bet is to use a Swing Timer -- please check out the tutorial. The gist is that within the Timer's ActionListener you increment the position of the snake, changing its direction depending on the state of your key press.
I would use an enum to indicate Direction {UP, DOWN, LEFT, RIGHT}:
public enum Direction {
UP,
DOWN,
LEFT,
RIGHT
}
AND then a Map<Direction, Boolean> to indicate which direction to head:
private Map<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
Elsewhere you would initialize the Map to hold false in all values:
// initialize the map to all false
for (Direction dir : Direction.values()) {
dirMap.put(dir, false);
}
Then change the state of the items in the Map within your code for listening to key presses and releases -- call map.put(Direction.UP, true) for instance when the up key is pressed, and likewise call map.put(Direction.UP, false) when it has been released, same for the other keys. Note that if yours is a Swing application, I'd use Key Bindings and not a KeyListener to do this. In the listener, I'd then call repaint() on the GUI.
Within the Swing Timer, iterate through the Map, setting the direction based on the state of the Map.
class fields:
private int spriteX = 0; // location of sprite
private int spriteY = 0;
private int directionX = 0; // direction sprite is heading
private int directionY = 0;
Within the ActionListener
// within the Swing Timer's ActionListener
if (dirMap.get(Direction.UP)) {
directionY -= 1;
}
if (dirMap.get(Direction.DOWN)) {
directionY += 1;
}
if (dirMap.get(Direction.RIGHT)) {
directionX += 1;
}
if (dirMap.get(Direction.LEFT)) {
directionY -= 1;
}
// here multiply directionX and directionY by some scale factor and use to place new snake head
// then call repaint();
For example (not a snake but a ball -- I'll leave the Snake to you)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.EnumMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.*;
#SuppressWarnings("serial")
public class DirTest extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 40;
private static final Color SPRITE_COLOR = Color.RED;
private static final int SPRITE_W = 20;
private static final Color BG = Color.BLACK;
public static final int SCALE = 1;
private Map<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
private int spriteX = 0;
private int spriteY = 0;
private int directionX = 0;
private int directionY = 0;
private Timer gameLoopTimer = new Timer(TIMER_DELAY, new TimerListener());
public DirTest() {
setKeyBindings();
setBackground(BG);
// initialize map to all 0;
for (Direction dir : Direction.values()) {
dirMap.put(dir, false);
}
gameLoopTimer.start();
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW; // bind to keys if component in active window
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
setKeyBinding(inputMap, actionMap, KeyEvent.VK_UP, Direction.UP);
setKeyBinding(inputMap, actionMap, KeyEvent.VK_DOWN, Direction.DOWN);
setKeyBinding(inputMap, actionMap, KeyEvent.VK_LEFT, Direction.LEFT);
setKeyBinding(inputMap, actionMap, KeyEvent.VK_RIGHT, Direction.RIGHT);
}
private void setKeyBinding(InputMap inputMap, ActionMap actionMap, int keyCode, Direction dir) {
KeyStroke press = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke released = KeyStroke.getKeyStroke(keyCode, 0, true);
Action pressAction = new PressedAction(dir, true);
Action releasedAction = new PressedAction(dir, false);
inputMap.put(press, press.toString());
inputMap.put(released, released.toString());
actionMap.put(press.toString(), pressAction);
actionMap.put(released.toString(), releasedAction);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(SPRITE_COLOR);
g2.fillOval(spriteX, spriteY, SPRITE_W, SPRITE_W);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class PressedAction extends AbstractAction {
private boolean pressed;
private Direction dir;
public PressedAction(Direction dir, boolean pressed) {
this.dir = dir;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
dirMap.put(dir, pressed);
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (Entry<Direction, Boolean> entry : dirMap.entrySet()) {
if (entry.getValue()) {
directionX += entry.getKey().getX();
directionY += entry.getKey().getY();
}
}
spriteX += SCALE * directionX;
spriteY += SCALE * directionY;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DirTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DirTest());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
enum Direction {
UP(0, -1),
DOWN(0, 1),
LEFT(-1, 0),
RIGHT(1, 0);
private int x;
private int y;
private Direction(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}

If you want to keep the snake moving you need some kind of game-loop (as mentioned before). The easiest way is having a single field containing the current direction, and set it based on the input. So here are a few methods/classes you could use to set the correct direction/position
The Direction Enum
public enum Direction
{
NORTH, EAST, SOUTH, WEST;
public Direction oposite()
{
switch(this)
{
case NORTH: return SOUTH;
case SOUTH: return NORTH;
case EAST: return WEST;
case WEST: return EAST;
}
}
}
The method to set the current direction. (This assumes there is a field named 'currentDirection' which contains an enum (Direction) representing the current direction)
public void setDirection(Direction newDirection)
{
if(currentDirection != newDirection.oposite())
currentDirection = newDirection;
}
This is somewhere in your game loop (either an timer, or a while loop including a 'sleep' call to prevent CPU hugging)
switch(currentDirection)
{
case NORTH: mySegment[0].moveNorth(); break;
case EAST: mySegment[0].moveEast(); break;
case SOUTH: mySegment[0].moveSouth(); break;
case WEST: mySegment[0].moveWest(); break;
}
repaint();
And of course instead of calling 'mySegment[0].moveNorth();' or someting equalivant in the actionHandlers for the keyEvents, you should only call 'setDirection();' to make the snake move.
I hope this helped you out.

Related

How to dissapear rectangle like point in Packman

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

Java Draw not moving snake

I am trying to create a snake clone just as a practice. ive drawn the snake and added the movement patterns but the snake eats on it self when I press any key to move. but its not moving. the array retracts the reactacles on the starting point and does nothing.
here is my snake class I have removed my comments as they where more than the code and the posting system was not allowing me to post
Edit
If you need anything from the other classes please let me know. but I think my error is somewhere in here
EDIT 2
Added the entire code, you can just copy paste in inside a new project and you will reproduce my error.
public class Snake {
List<Point> sPoints;
int xDir,yDir;
boolean isMoving,addTail;
final int sSize = 20, startX = 150 , startY = 150;
public Snake(){
sPoints = new ArrayList<Point>();
xDir = 0;
yDir = 0;
isMoving = false;
addTail = false;
sPoints.add(new Point(startX,startY));
for(int i=1; i<sSize; i++) {
sPoints.add(new Point(startX - i * 4,startY));
}
}
public void draw(Graphics g){
g.setColor(Color.white);
for(Point p : sPoints) {
g.fillRect(p.getX(),p.getY(),4,4);
}
}
public void move(){
if (isMoving) {
Point temp = sPoints.get(0);
Point last = sPoints.get(sPoints.size() - 1);
Point newstart = new Point(temp.getX() + xDir * 4, temp.getY() + yDir * 4);
for (int i = sPoints.size() - 1; i >= 1; i--) {
sPoints.set(i, sPoints.get(i - 1));
}
sPoints.set(0, newstart);
}
}
public int getxDir() {
return xDir;
}
public void setxDir(int x) {
this.xDir = xDir;
}
public int getyDir() {
return yDir;
}
public void setyDir(int y) {
this.yDir = yDir;
}
public int getX(){
return sPoints.get(0).getX();
}
public int getY(){
return sPoints.get(0).getY();
}
public boolean isMoving() {
return isMoving;
}
public void setIsMoving(boolean b) {
isMoving = b;
}
}
The following is the point class. just some getters setters for the points ,for those i used the IntelliJ to auto generate them.. (again i removed comments )
public class Point {
private int x,y;
public Point() {
x = 0;
y = 0;
}
public Point(int x, int y) {
this.x =x;
this.y =y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
and finally my main class called game.
in here what I do is create my applet give it background color. create my threat for the runnable. and also add the movement patterns for up/right/down/left...
and use several classes to update my drawing patterns so it can simulate movement by updating each of state of my rect list.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Game extends Applet implements Runnable, KeyListener {
//setting up double buffering.
Graphics graphics;
Image img;
Thread thread;
Snake snake;
public void init() {
//setting the size of our Applet
this.resize(400,400);
//we gonna create the image just the same size as our applet.
img = createImage(400,400);
//this represents our offscreen image that we will draw
graphics = img.getGraphics();
this.addKeyListener(this);
snake = new Snake();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
//Setting the background of our applet to black
graphics.setColor(Color.black);
//Fill rectangle 0 , 0 (starts from) for top left corner and then 400,400 to fill our entire background to black
graphics.fillRect(0,0,400,400);
snake.draw(graphics);
//painting the entire image
g.drawImage(img,0,0,null);
}
//Update will call on Paint(g)
public void update(Graphics g){
paint(g);
}
//Repaint will call on Paint(g)
public void repaint(Graphics g){
paint(g);
}
public void run() {
//infinite loop
for(;;) {
snake.move();
//drawing snake
this.repaint();
//Creating a time delay
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void keyTyped(KeyEvent keyEvent) {
}
public void keyPressed(KeyEvent keyEvent) {
if(!snake.isMoving()){ //this will allow the snake to start moving, but will disable LEFT for just the 1st move
if(keyEvent.getKeyCode() == KeyEvent.VK_UP || keyEvent.getKeyCode() == KeyEvent.VK_RIGHT ||
keyEvent.getKeyCode() == KeyEvent.VK_DOWN ) {
snake.setIsMoving(true);
}
}
//setting up Key mapping so when the user presses UP,RIGHT,DOWN,LEFT. the Snake will move accordingly
if(keyEvent.getKeyCode() == KeyEvent.VK_UP) {
if (snake.getyDir() != 1) {
snake.setyDir(-1);
snake.setxDir(0);
}
}
if(keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
if (snake.getxDir() != -1) {
snake.setxDir(1);
snake.setyDir(0);
}
}
if(keyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
if (snake.getyDir() != -1) {
snake.setyDir(1);
snake.setxDir(0);
}
}
if(keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
if (snake.getxDir() != 1) {
snake.setxDir(-1);
snake.setyDir(0);
}
}
}
public void keyReleased(KeyEvent keyEvent) {
}
}
Here is some opinion I have reading your code.
The reason your snake won't move is because your snake.setyDir() and
snake.setxDir() didn't take the input to overwrite xDir and yDir. They are assigning to itself.
There is a Point2D class ready for you in JDK
When moving the snake, you just need to remove the tail and add one
more block before the head. You can keep the body tight (according
to my common knowledge to snake).
Consider a L shape snake on the left, the bottom end is the head and it is currently heading right. To move the snake, remove the tail (green block) and add one more to the head according to its direction (red block). It final state become the snake on the right. LinkedList suit the needs.
If using two int (xDir and yDir) to control the snake direction
is confusing, you can help your self by creating a enum. Those -1,
0, 1 with x and y may confuse you.
Declare constant instead of magic number. e.g. the width of block 4,
image size 400
Is Snake.addTail unnecessary?
Attribute should has accessibility modifier
End result:
Game.java
import java.applet.Applet;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Arrays;
public class Game extends Applet implements Runnable, KeyListener {
private final int GAMEBOARD_WIDTH = 400;
// setting up double buffering.
private Graphics graphics;
private Image img;
private Thread thread;
private Snake snake;
public void init() {
// setting the size of our Applet
this.resize(GAMEBOARD_WIDTH, GAMEBOARD_WIDTH);
// we gonna create the image just the same size as our applet.
img = createImage(GAMEBOARD_WIDTH, GAMEBOARD_WIDTH);
// this represents our offscreen image that we will draw
graphics = img.getGraphics();
this.addKeyListener(this);
snake = new Snake();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
// Setting the background of our applet to black
graphics.setColor(Color.BLACK);
// Fill rectangle 0 , 0 (starts from) for top left corner and then 400,400 to
// fill our entire background to black
graphics.fillRect(0, 0, GAMEBOARD_WIDTH, GAMEBOARD_WIDTH);
snake.draw(graphics);
// painting the entire image
g.drawImage(img, 0, 0, null);
}
// Update will call on Paint(g)
public void update(Graphics g) {
paint(g);
}
// Repaint will call on Paint(g)
public void repaint(Graphics g) {
paint(g);
}
public void run() {
// infinite loop
for (;;) {
snake.move();
// drawing snake
this.repaint();
// Creating a time delay
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void keyTyped(KeyEvent keyEvent) {
}
public void keyPressed(KeyEvent keyEvent) {
int keyCode = keyEvent.getKeyCode();
if (!snake.isMoving()) {
// this will allow the snake to start moving, but will disable LEFT for just the
// 1st move
if (matchKey(keyCode, KeyEvent.VK_UP, KeyEvent.VK_RIGHT, KeyEvent.VK_DOWN)) {
snake.setIsMoving(true);
}
}
// setting up Key mapping so when the user presses UP,RIGHT,DOWN,LEFT. the Snake
// will move accordingly
if (matchKey(keyCode, KeyEvent.VK_UP)) {
snake.setDirection(Direction.UP);
}
if (matchKey(keyCode, KeyEvent.VK_RIGHT)) {
snake.setDirection(Direction.RIGHT);
}
if (matchKey(keyCode, KeyEvent.VK_DOWN)) {
snake.setDirection(Direction.DOWN);
}
if (matchKey(keyCode, KeyEvent.VK_LEFT)) {
snake.setDirection(Direction.LEFT);
}
}
// return true if targetKey contains the provided keyCode
private boolean matchKey(int keyCode, int... targetKey) {
return Arrays.stream(targetKey).anyMatch(i -> i == keyCode);
}
public void keyReleased(KeyEvent keyEvent) {
}
}
Snake.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.util.LinkedList;
public class Snake {
private final int sSize = 20, startX = 150, startY = 150;
private final int BLOCK_WIDTH = 4;
private LinkedList<Point2D.Float> sPoints;
private boolean isMoving;
private Direction direction;
public Snake() {
sPoints = new LinkedList<Point2D.Float>();
isMoving = false;
sPoints.add(new Point2D.Float(startX, startY));
for (int i = 1; i < sSize; i++) {
sPoints.add(new Point2D.Float(startX - i * BLOCK_WIDTH, startY));
}
}
public void draw(Graphics g) {
g.setColor(Color.white);
for (Point2D p : sPoints) {
g.fillRect((int) p.getX(), (int) p.getY(), BLOCK_WIDTH, BLOCK_WIDTH);
}
}
public void move() {
if (isMoving) {
sPoints.removeLast();
steer(sPoints.getFirst());
}
}
private void steer(Point2D head) {
Point2D.Float newHead = new Point2D.Float();
switch (this.getDirection()) {
case UP:
newHead.setLocation(head.getX(), head.getY() - BLOCK_WIDTH);
break;
case DOWN:
newHead.setLocation(head.getX(), head.getY() + BLOCK_WIDTH);
break;
case LEFT:
newHead.setLocation(head.getX() - BLOCK_WIDTH, head.getY());
break;
case RIGHT:
newHead.setLocation(head.getX() + BLOCK_WIDTH, head.getY());
break;
}
this.sPoints.addFirst(newHead);
}
public int getX() {
return (int) sPoints.get(0).getX();
}
public int getY() {
return (int) sPoints.get(0).getY();
}
public boolean isMoving() {
return isMoving;
}
public void setIsMoving(boolean b) {
isMoving = b;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction d) {
if (this.getDirection() == null) {
this.direction = d;
} else if (!this.getDirection().isOpposite(d)) {
this.direction = d;
}
}
}
Direction.java
public enum Direction {
UP(-1), DOWN(1), LEFT(-2), RIGHT(2);
int vector;
Direction(int i) {
this.vector = i;
}
public boolean isOpposite(Direction d) {
return this.vector + d.vector == 0;
}
}
Snack.java
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Snake extends JFrame {
public Snake() {
initUI();
}
private void initUI() {
add(new Board());
setResizable(false);
pack();
setTitle("Snake");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame ex = new Snake();
ex.setVisible(true);
});
}
}
Board.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Board extends JPanel implements ActionListener {
private final int B_WIDTH = 300;
private final int B_HEIGHT = 300;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 900;
private final int RAND_POS = 29;
private final int DELAY = 140;
private final int x\[\] = new int\[ALL_DOTS\];
private final int y\[\] = new int\[ALL_DOTS\];
private int dots;
private int apple_x;
private int apple_y;
private boolean leftDirection = false;
private boolean rightDirection = true;
private boolean upDirection = false;
private boolean downDirection = false;
private boolean inGame = true;
private Timer timer;
private Image ball;
private Image apple;
private Image head;
public Board() {
initBoard();
}
private void initBoard() {
addKeyListener(new TAdapter());
setBackground(Color.black);
setFocusable(true);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
loadImages();
initGame();
}
private void loadImages() {
ImageIcon iid = new ImageIcon("src/resources/dot.png");
ball = iid.getImage();
ImageIcon iia = new ImageIcon("src/resources/apple.png");
apple = iia.getImage();
ImageIcon iih = new ImageIcon("src/resources/head.png");
head = iih.getImage();
}
private void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x\[z\] = 50 - z * 10;
y\[z\] = 50;
}
locateApple();
timer = new Timer(DELAY, this);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
if (inGame) {
g.drawImage(apple, apple_x, apple_y, this);
for (int z = 0; z < dots; z++) {
if (z == 0) {
g.drawImage(head, x\[z\], y\[z\], this);
} else {
g.drawImage(ball, x\[z\], y\[z\], this);
}
}
Toolkit.getDefaultToolkit().sync();
} else {
gameOver(g);
}
}
private void gameOver(Graphics g) {
String msg = "Game Over";
Font small = new Font("Helvetica", Font.BOLD, 14);
FontMetrics metr = getFontMetrics(small);
g.setColor(Color.white);
g.setFont(small);
g.drawString(msg, (B_WIDTH - metr.stringWidth(msg)) / 2, B_HEIGHT / 2);
}
private void checkApple() {
if ((x\[0\] == apple_x) && (y\[0\] == apple_y)) {
dots++;
locateApple();
}
}
private void move() {
for (int z = dots; z > 0; z--) {
x\[z\] = x\[(z - 1)\];
y\[z\] = y\[(z - 1)\];
}
if (leftDirection) {
x\[0\] -= DOT_SIZE;
}
if (rightDirection) {
x\[0\] += DOT_SIZE;
}
if (upDirection) {
y\[0\] -= DOT_SIZE;
}
if (downDirection) {
y\[0\] += DOT_SIZE;
}
}
private void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x\[0\] == x\[z\]) && (y\[0\] == y\[z\])) {
inGame = false;
}
}
if (y\[0\] >= B_HEIGHT) {
inGame = false;
}
if (y\[0\] < 0) {
inGame = false;
}
if (x\[0\] >= B_WIDTH) {
inGame = false;
}
if (x\[0\] < 0) {
inGame = false;
}
if (!inGame) {
timer.stop();
}
}
private void locateApple() {
int r = (int) (Math.random() * RAND_POS);
apple_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
apple_y = ((r * DOT_SIZE));
}
#Override
public void actionPerformed(ActionEvent e) {
if (inGame) {
checkApple();
checkCollision();
move();
}
repaint();
}
private class TAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) {
leftDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) {
rightDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_UP) && (!downDirection)) {
upDirection = true;
rightDirection = false;
leftDirection = false;
}
if ((key == KeyEvent.VK_DOWN) && (!upDirection)) {
downDirection = true;
rightDirection = false;
leftDirection = false;
}
}
}
}

Collision detection only working on top side of wall - Java

I have an assignment to create a 2D game, this game must use an abstract class Shape which is used to draw shapes. I decided to make a 2D platformer, but it's my first time doing something like this. I am trying to implement collision detection, and decided to create an offset of my player object to see if it collides with my rectangle object, and stop movement if it does. This is only working for the top side, however on the right, left and botton the player will move through the rectangle and I don't understand why. I expected the collision detection to work for all sides. I would like to know how I can change my collision detection to work for all sides of the rectangle.
Here's a gif showing what happens:
My App class:
package A2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.TreeSet;
import static java.awt.event.KeyEvent.*;
public class App extends JFrame {
private static final int GRAVITY = 10;
private static final int MAX_FALL_SPEED = 30;
public App() {
final Player player = new Player(200, 300);
final Rectangle rectangle = new Rectangle(100, 400);
JPanel mainPanel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
player.draw(g);
rectangle.draw(g);
}
};
mainPanel.addKeyListener(new controlHandler(this, player, rectangle));
mainPanel.setFocusable(true);
add(mainPanel);
setLayout(new GridLayout());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(600, 600);
}
class controlHandler implements ActionListener, KeyListener {
int keyCode;
App app;
Player player;
Rectangle rectangle;
TreeSet<Integer> keys = new TreeSet<>();
Timer time = new Timer(5, this);
boolean collision = false;
public controlHandler(App app, Player player, Rectangle rectangle) {
this.app = app;
this.player = player;
this.rectangle = rectangle;
time.start();
}
public void actionPerformed(ActionEvent e) {
player.x += player.xVelocity;
player.y += player.yVelocity;
Rectangle2D offset = player.getOffsetBounds();
if(offset.intersects(this.rectangle.wallObj.getBounds2D())){
collision = true;
player.xVelocity = 0;
player.yVelocity = 0;
}
else collision = false;
repaint();
}
public void keyPressed(KeyEvent e) {
keyCode = e.getKeyCode();
keys.add(keyCode);
switch (keyCode) {
case VK_UP:
moveUp();
break;
case VK_DOWN:
moveDown();
break;
case VK_LEFT:
moveLeft();
break;
case VK_RIGHT:
moveRight();
break;
}
}
public void keyReleased(KeyEvent e) {
released();
}
public void keyTyped(KeyEvent e) { }
public void moveUp() {
player.xVelocity = 0;
player.yVelocity = -2;
}
public void moveDown() {
if(!collision) {
player.xVelocity = 0;
player.yVelocity = 2;
}
}
public void moveLeft() {
player.xVelocity = -2;
player.yVelocity = 0;
}
public void moveRight() {
player.xVelocity = 2;
player.yVelocity = 0;
}
public void released() {
player.xVelocity = 0;
player.yVelocity = 0;
}
}
public static void main(String[] args) {
App app = new App();
app.setVisible(true);
}
}
abstract class Shape {
public double x;
public double y;
public void draw(Graphics g) { }
}
Player class:
package A2;
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class Player extends Shape {
public double xVelocity;
public double yVelocity;
public double length = 60;
public double top;
public double right;
public double left;
public double bottom;
public Rectangle2D playerObj;
public Player(double x, double y) {
this.x = x;
this.y = y;
playerObj = new Rectangle2D.Double(x, y, length, length);
}
public Rectangle2D getOffsetBounds(){
return new Rectangle2D.Double(x + xVelocity , y + yVelocity , length, length);
}
public void draw(Graphics g) {
playerObj = new Rectangle2D.Double(x, y, length, length);
g.setColor(Color.BLACK);
Graphics2D g2 = (Graphics2D)g;
g2.fill(playerObj);
}
}
Rectangle class (will be a platform):
package A2;
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class Rectangle extends Shape {
public double width = 400;
public double height = 30;
public double top;
public double right;
public double left;
public double bottom;
public Rectangle2D wallObj;
public Rectangle(double x, double y) {
this.x = x;
this.y = y;
wallObj = new Rectangle2D.Double(x, y, width, height);
}
public void draw(Graphics g) {
g.setColor(Color.ORANGE);
Graphics2D g2 = (Graphics2D)g;
g2.fill(wallObj);
}
}
So, based on my understanding, your collision detection process is basically to create a "virtual" instance of the object and determine if it will collide.
Your code is actually work, you can tell by the fact that the object "stops" moving once it collides, but what you're not doing, is reseting the objects position after it.
Let's look at the original code...
public void actionPerformed(ActionEvent e) {
player.x += player.xVelocity;
player.y += player.yVelocity;
Rectangle2D offset = player.getOffsetBounds();
if (offset.intersects(this.rectangle.wallObj.getBounds2D())) {
collision = true;
player.xVelocity = 0;
player.yVelocity = 0;
} else {
collision = false;
}
repaint();
}
Update the position of the object
Create a virtual instance of the object, which applies the velocity to the objects current position ... again?
Determine if the object collides
Stop the movement if any
... where do you reset the position of object so it's no longer colliding?!?
Instead, you shouldn't set the position of the object until AFTER you've determine if a collision has occurred or not, for example...
public void actionPerformed(ActionEvent e) {
Rectangle2D offset = player.getOffsetBounds();
if (offset.intersects(this.rectangle.wallObj.getBounds2D())) {
collision = true;
player.xVelocity = 0;
player.yVelocity = 0;
} else {
player.x += player.xVelocity;
player.y += player.yVelocity;
collision = false;
}
repaint();
}
The problem with this approach, though, is if the velocity is large enough, the object will appear not to collide, but stop a little before it, as the amount of change is larger than the remaining gap.
What you need to do, is once you've detected a collision is calculate the difference between the amount you want to move and the space available and move the object only by that amount instead

setContentPane covering everything up and JButton not working properly

I am making a game in which a player attempts to navigate a maze, but I have run into two bugs that have absolutely stumped me. My first problem is with using setContentPane(). Whenever I set it to my intended background, it covers up everything else on the screen. I have tried relocating it to every place I thought it could be, but it still covered it up. My second question is about my JButton. Whenever I click it, it changes to the playing speed as intended, but I can't move afterwards. It works when I use the method in my Keylistener, and they have the same code inside of them. I have included all code that I thought was necessary, but if you need any more, let me know. The full source for this project can be found Here.
package game;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
public class Game extends JPanel implements ActionListener, KeyListener {
private boolean upPressed = false;
private boolean downPressed = false;
private boolean rightPressed = false;
private boolean leftPressed = false;
private int tileSize = 200;
private int playerSize = tileSize / 4;
private int playerSpeed = playerSize / 10;
private int mazeX = 51;
private int mazeY = 51;
private int[][] maze = new int[mazeX][mazeY];
private int[][] initX = new int[mazeX][mazeY];
private int[][] initY = new int[mazeX][mazeY];
private boolean[][] visited = new boolean[mazeX][mazeY];
private int currentCellX = 0;
private int currentCellY = 0;
private int deltaX = 0;
private int deltaY = 0;
JComponent background = new JLabel(new ImageIcon(Main.class.getResource("/assets/ui/mainMenu.png")));
JButton adventure = new JButton();
JButton freePlay = new JButton();
JButton exit = new JButton();
JButton pause = new JButton();
JButton resume = new JButton();
private String screen = "menu";
public Main main;
public Game(Main main) {
this.main = main;
setFocusable(true);
addKeyListener(this);
addButtons();
setUpInitialCoordinates();
Timer timer = new Timer(1000 / 120, this);
timer.start();
}
private void tick() {
switch (screen) {
case "playing" :
background.setVisible(false);
if (upPressed) {
deltaY += playerSpeed;
} else if (downPressed) {
deltaY -= playerSpeed;
}
if (rightPressed) {
deltaX -= playerSpeed;
} else if (leftPressed) {
deltaX += playerSpeed;
}
break;
case "paused":
background.setVisible(false);
adventure.setVisible(false);
freePlay.setVisible(false);
pause.setVisible(false);
exit.setVisible(true);
break;
case "menu":
main.setContentPane(new JLabel(new ImageIcon(Main.class.getResource("/assets/ui/mainMenu.png"))));
adventure.setVisible(true);
freePlay.setVisible(true);
pause.setVisible(false);
exit.setVisible(true);
break;
}
repaint();
}
private void addButtons() {
setLayout(null);
add(freePlay);
freePlay.setBounds(10, 100, 180, 80);
freePlay.setSize(180, 80);
freePlay.setAction(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
downPressed = false;
rightPressed = false;
leftPressed = false;
generateMaze();
screen = "playing";
}
});
freePlay.setIcon(new ImageIcon(Main.class.getResource("/assets/ui/buttons/freePlayUnselected.png")));
freePlay.setRolloverIcon(new ImageIcon(Main.class.getResource("/assets/ui/buttons/freePlaySelected.png")));
}
private void setUpInitialCoordinates() {
}
private void generateMaze() {
}
private boolean checkForUnvisitedCells() {
}
private boolean checkForUnvisitedAdjacentCells() {
}
#Override
public void actionPerformed(ActionEvent e) {
tick();
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void paintComponent(Graphics g) {
}
}
setContentPane does just that, it replaces what ever was previously been used as the content with the new component.
Take a closer look at How to Use Root Panes for more details.
I'd suggest that a better solution would be to use a CardLayout, but in this case, a better solution would be to just change the icon property of the current content pane...
background.setIcon(new ImageIcon(Main.class.getResource("/assets/ui/mainMenu.png")));
and simply make the background the frame's content pane...
JLabel background = new JLabel(new ImageIcon(Main.class.getResource("/assets/ui/mainMenu.png")));
//....
public Game(Main main) {
this.main = main;
setContentPane(background);
//...
Then never change the content pane...
You could then use a CardLayout to change what appears on the content pane
My second question is about my JButton. Whenever I click it, it changes to the playing speed as intended, but I can't move afterwards. It works when I use the method in my Keylistener
Welcome to the wonderful world of why you shouldn't use KeyListener, the button has stolen key board focus and the KeyListener will no longer respond to key board input because it relies on the component it is registered to be focusable AND focused. Use key bindings instead. How to Use Key Bindings

How to set a "current" window with JFrames?

I am not sure how to really word this, but I have a game that is arrow key based.
Anyways there is an options menu but after I select options when I try to hit arrow keys and move nothing happens...
I am assuming it is because I am "active" in another JFrame that is now hidden (the options menu) rather than the game screen.
Is there a way I can have the program know that I want the keyboard actions to refer back to the original JFrame when I close the options menu?
And while I am at it, I am trying to figure out how to make the game window full-screened. Right now I have it setUndecorated so no border is up and I tried the code: setExtendedState(JFrame.MAXIMIZED_BOTH); But the game is shifted way off to the bottom right of the screen.
I am an external monitor right now, would that matter?
I also have non-resizable checked (I'm on netbeans), and I have "set" sizes for the Jframe and Jpanels, should I remove those?
I hope that makes sense,
Thanks,
-Austin
*All in netbeans too.
I am assuming that you're using a KeyListener to capture key strokes, and if so, KeyListeners only work if the component being listened to has focus. Your problem is that on swapping your views, your listened to component does not have focus. One way to solve this is to call requestFocusInWindow() on the listened component after the swap.
But there's a bigger issue afoot, and that's in your use of KeyListeners to begin with, something that in general should be avoided with Swing applications. Instead use Key Bindings, a much higher level concept and thus one that should be used in favor of the low level KeyListeners.
Also, to maximize a JFrame, you'll want to call it's setExtendedState(...) method passing in Frame.MAXIMIZED_BOTH as the parameter as it appears you are doing. Are you calling pack()? Also, you're not calling setLocation(...), setBounds(...) or setSize(...) on the JFrame, right?
Edit: I see you have in fact called setSize(...) on the JFrame. Yes, remove this as it makes no sense if you're maximizing the JFrame.
Edit
Code example of what I am suggesting:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class AnimationWithKeyBinding {
#SuppressWarnings("serial")
private static void createAndShowUI() {
final JPanel cardPanel = new JPanel(new CardLayout());
MenuPanel menuPanel = new MenuPanel();
AnimationPanel animationPanel = new AnimationPanel();
cardPanel.add(menuPanel, "Menu");
cardPanel.add(animationPanel, "Animation");
menuPanel.setNextBtnAction(new AbstractAction("Next") {
{
putValue(NAME, "Next");
putValue(MNEMONIC_KEY, KeyEvent.VK_N);
}
#Override
public void actionPerformed(ActionEvent arg0) {
((CardLayout)cardPanel.getLayout()).next(cardPanel);
}
});
JFrame frame = new JFrame("Animation With Key Binding");
frame.getContentPane().add(cardPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class MenuPanel extends JPanel {
private JButton nextBtn = new JButton();
public MenuPanel() {
TitledBorder titledBorder = BorderFactory.createTitledBorder("Menu Panel");
titledBorder.setTitleFont(titledBorder.getTitleFont().deriveFont(Font.BOLD, 24));
setBorder(titledBorder);
setLayout(new GridBagLayout());
add(nextBtn);
}
public void setNextBtnAction(Action action) {
nextBtn.setAction(action);
}
}
#SuppressWarnings("serial")
class AnimationPanel extends JPanel {
public static final int SPRITE_WIDTH = 20;
public static final int PANEL_WIDTH = 400;
public static final int PANEL_HEIGHT = 400;
private static final int MAX_MSTATE = 25;
private static final int SPIN_TIMER_PERIOD = 16;
private static final int SPRITE_STEP = 3;
private int mState = 0;
private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
private int oldMX = mX;
private int oldMY = mY;
private boolean moved = false;
// an array of sprite images that are drawn sequentially
private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
public AnimationPanel() {
// create and start the main animation timer
new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.white);
createSprites(); // create the images
setupKeyBinding();
}
private void setupKeyBinding() {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = getInputMap(condition);
ActionMap actMap = getActionMap();
// this uses an enum of Direction that holds ints for the arrow keys
for (Direction direction : Direction.values()) {
int key = direction.getKey();
String name = direction.name();
// add the key bindings for arrow key and shift-arrow key
inMap.put(KeyStroke.getKeyStroke(key, 0), name);
inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK),
name);
actMap.put(name, new MyKeyAction(this, direction));
}
}
// create a bunch of buffered images and place into an array,
// to be displayed sequentially
private void createSprites() {
for (int i = 0; i < spriteImages.length; i++) {
spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = spriteImages[i].createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
double theta = i * Math.PI / (2 * spriteImages.length);
double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x2, y2, x1);
g2.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(spriteImages[mState], mX, mY, null);
}
public void incrementX(boolean right) {
oldMX = mX;
if (right) {
mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
} else {
mX = Math.max(0, mX - SPRITE_STEP);
}
moved = true;
}
public void incrementY(boolean down) {
oldMY = mY;
if (down) {
mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
} else {
mY = Math.max(0, mY - SPRITE_STEP);
}
moved = true;
}
public void tick() {
mState = (mState + 1) % MAX_MSTATE;
}
private class SpinTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
tick();
int delta = 20;
int width = SPRITE_WIDTH + 2 * delta;
int height = width;
// make sure to erase the old image
if (moved) {
int x = oldMX - delta;
int y = oldMY - delta;
repaint(x, y, width, height);
}
int x = mX - delta;
int y = mY - delta;
// draw the new image
repaint(x, y, width, height);
moved = false;
}
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(
KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private AnimationPanel draw;
private Direction direction;
public MyKeyAction(AnimationPanel draw, Direction direction) {
this.draw = draw;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
draw.incrementY(false);
break;
case DOWN:
draw.incrementY(true);
break;
case LEFT:
draw.incrementX(false);
break;
case RIGHT:
draw.incrementX(true);
break;
default:
break;
}
}
}

Categories

Resources