Player and Blocks collision (Terraria) [closed] - java

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am creating a game like Terraria. I already have a player and I can place blocks and delete them by clicking on an existing block. But I canĀ“t figure out how to make player and block collision. I want to create physics like in terraria. Can you help me somehow?
Here are my 3 classes (I have 4 but the 4. class is only JFrame class):
Here is my Main Class (gameloop):
package Package;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Game extends JPanel implements ActionListener, MouseListener, KeyListener {
Timer gameLoop = new Timer(5, this);
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1500;
public static final int HEIGHT = 900;
public static final Dimension windowSize = new Dimension(WIDTH, HEIGHT);
Player player = new Player();
public static ArrayList<Block> blocks = new ArrayList<Block>();
private int xDistance;
private int yDistance;
public Game() {
setPreferredSize(windowSize);
setFocusable(true);
addMouseListener(this);
addKeyListener(this);
setBackground(Color.WHITE);
for (int i = 0; i < Game.WIDTH; i += 50) {
for (int j = Game.HEIGHT - 150; j < Game.HEIGHT; j += 50) {
blocks.add(new Block(i, j));
}
}
start();
}
public void start() {
gameLoop.start();
}
public void stop() {
gameLoop.stop();
}
public void paint(Graphics g) {
for (Block b : blocks) {
b.render(g);
}
player.render(g);
}
public void actionPerformed(ActionEvent e) {
player.move();
player.offScreen();
repaint();
}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
boolean foundBlock = false;
xDistance = mouseX % 50;
yDistance = mouseY % 50;
for (Block b : blocks) {
if (b.x == mouseX - xDistance && b.y == mouseY - yDistance) {
if (mouseX >= player.x - 150 && mouseX <= player.x + 200 && mouseY >= player.y - 150 && mouseY <= player.y + 250) {
blocks.remove(b);
foundBlock = true;
break;
}
}
}
if (foundBlock == false) {
if (mouseX >= player.x - 150 && mouseX <= player.x + 200 && mouseY >= player.y - 150 && mouseY <= player.y + 250) {
blocks.add(new Block(mouseX - xDistance, mouseY - yDistance));
}
}
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
int arrows = e.getKeyCode();
if (arrows == KeyEvent.VK_D) {
player.xSpeed = 2;
}
if (arrows == KeyEvent.VK_A) {
player.xSpeed = -2;
}
}
public void keyReleased(KeyEvent e) {
int arrows = e.getKeyCode();
if (arrows == KeyEvent.VK_D) {
player.xSpeed = 0;
}
if (arrows == KeyEvent.VK_A) {
player.xSpeed = 0;
}
}
}
Here is my Player class:
package Package;
import java.awt.Color;
import java.awt.Graphics;
public class Player {
public int x = 14 * 50;
public int y = Game.HEIGHT - (5 * 50);
public int xSpeed = 0;
public int ySpeed = 0;
public boolean jump = false;
public int ticks = 6;
public void render(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(x, y, 50, 100);
}
public void move() {
x += xSpeed;
y += ySpeed;
}
public void offScreen() {
if (x <= 0) {
xSpeed = 0;
} else if (x + 50 >= Game.WIDTH) {
xSpeed = 0;
}
}
}
Here is my Block Class:
package Package;
import java.awt.Color;
import java.awt.Graphics;
public class Block {
public int x;
public int y;
public Block(int x, int y) {
this.x = x;
this.y = y;
}
public void render(Graphics g) {
g.setColor(Color.GRAY);
g.fillRect(x, y, 50, 50);
g.setColor(Color.BLACK);
g.drawRect(x, y, 50, 50);
}
}

You don't want to store you blocks this way:
public static ArrayList<Block> blocks = new ArrayList<Block>();
Instead, create a structure that easily allows you to access blocks by their coordinates. Maybe:
public static HashMap<Integer, HashMap<Integer, Block>>()
... where the 1st Integer is the X coordinate and the 2nd is the Y. That way, you can quickly find the blocks that have any chance of colliding with the player, instead of checking every block in the world every iteration.
Also, why is this static? It shouldn't be, I think.
To check for collisions (I assume to prevent players from moving through blocks), just figure out the coordinates of where the player would wind up after the proposed move and see if the players bounding rectangle (the rectangle from the upper left of the player's sprite to the lower right) overlaps the bounding rectangle of any block.
But it's really important: arrange the blocks so you don't have to check them all! Only the blocks in a very narrow coordinate range can possibly be collisions, so make sure you can access blocks by range quickly.

Related

How can I change the vector of a ball in breakout game made using Java?

I'm using Java to make a breakout game. The independent parts are functioning: the paddle, the ball, the bricks.
However, the ball hits a wall and then instead of changing it's vector, the ball just travels up the x-axis in a straight line at the edge of the JFrame window until it hits the top of the window and bounces back down from this corner.
The ball then gets stuck in an infinite back and forth line from the top left corner until it touches the paddle (back and forth) and will never break any of the other bricks.
How can I change my code to fix this problem?
import java.awt.Graphics;
public class Ball extends Sprite {
private int xVelocity = 1, yVelocity = -1;
// Constructor
public Ball() {
setWidth(Settings.BALL_WIDTH);
setHeight(Settings.BALL_HEIGHT);
resetPosition();
public void resetPosition() {
setX(Settings.INITIAL_BALL_X);
setY(Settings.INITIAL_BALL_Y);
}
public void update() {
x += yVelocity;
y += yVelocity;
// Bounce off left side of screen
if(x <= 0) {
x = 0;
setXVelocity(-1);
}
// Bounce off right side of screen
if(x >= Settings.WINDOW_WIDTH - Settings.BALL_WIDTH) {
x =Settings.WINDOW_WIDTH;
setXVelocity(-1);
}
// Bounce off top of screen
if(y <= 0) {
y = 0;
setYVelocity(1);
}
}
public void setXVelocity(int x) {
xVelocity = x;
}
public void setYVelocity(int y) {
yVelocity = y;
}
public int getXVelocity() {
return xVelocity;
}
public int getYVelocity() {
return yVelocity;
}
public void paint(Graphics g) {
g.fillOval(x, y, Settings.BALL_WIDTH, Settings.BALL_HEIGHT);
}
}
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class BreakoutPanel extends JPanel implements ActionListener, KeyListener {
static final long serialVersionUID = 2L;
private boolean gameRunning = true;
private int livesLeft = 3;
private String screenMessage = "";
private Ball ball;
private Paddle paddle;
private Brick bricks[];
public BreakoutPanel(Breakout game) {
addKeyListener(this);
setFocusable(true);
Timer timer = new Timer(5, this);
timer.start();
ball = new Ball();
paddle = new Paddle();
bricks = new Brick[Settings.TOTAL_BRICKS];
createBricks();
}
private void createBricks() {
int counter = 0;
int x_space = 0;
int y_space = 0;
for(int x = 0; x < 4; x++) {
for(int y = 0; y < 5; y++) {
bricks[counter] = new Brick((x * Settings.BRICK_WIDTH) + Settings.BRICK_HORI_PADDING + x_space, (y * Settings.BRICK_HEIGHT) + Settings.BRICK_VERT_PADDING + y_space);
counter++;
y_space++;
}
x_space++;
y_space = 0;
}
}
private void paintBricks(Graphics g) {
for(int x = 0; x < Settings.TOTAL_BRICKS; x++) {
for(int y = 0; y < Settings.TOTAL_BRICKS; y++) {
bricks[y].paint(g);
}
}
}
private void update() {
if(gameRunning) {
// TODO: Update the ball and paddle
ball.update();
paddle.update();
collisions();
repaint();
}
}
private void gameOver() {
stopGame();
screenMessage = "Game Over!";
}
private void gameWon() {
stopGame();
screenMessage = "Congratulations! You have won!";
}
private void stopGame() {
gameRunning = false;
}
private void collisions() {
// Check for loss
if(ball.y > 450) {
// Game over
livesLeft--;
if(livesLeft <= 0) {
gameOver();
return;
} else {
ball.resetPosition();
ball.setYVelocity(-1);
}
}
// Check for win
boolean bricksLeft = false;
for(int i = 0; i < bricks.length; i++) {
// Check if there are any bricks left
if(!bricks[i].isBroken()) {
// Brick was found, close loop
bricksLeft = true;
break;
}
}
if(!bricksLeft) {
gameWon();
return;
}
// Check collisions
if(ball.getRectangle().intersects(paddle.getRectangle())) {
// Simplified touching of paddle
ball.setYVelocity(-1);
}
for(int i = 0; i < bricks.length; i++) {
if (ball.getRectangle().intersects(bricks[i].getRectangle())) {
int ballLeft = (int) ball.getRectangle().getMinX();
int ballHeight = (int) ball.getRectangle().getHeight();
int ballWidth = (int) ball.getRectangle().getWidth();
int ballTop = (int) ball.getRectangle().getMinY();
Point pointRight = new Point(ballLeft + ballWidth + 1, ballTop);
Point pointLeft = new Point(ballLeft - 1, ballTop);
Point pointTop = new Point(ballLeft, ballTop - 1);
Point pointBottom = new Point(ballLeft, ballTop + ballHeight + 1);
if (!bricks[i].isBroken()) {
if (bricks[i].getRectangle().contains(pointRight)) {
ball.setXVelocity(-1);
} else if (bricks[i].getRectangle().contains(pointLeft)) {
ball.setXVelocity(1);
}
if (bricks[i].getRectangle().contains(pointTop)) {
ball.setYVelocity(1);
} else if (bricks[i].getRectangle().contains(pointBottom)) {
ball.setYVelocity(-1);
}
bricks[i].setBroken(true);
}
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ball.paint(g);
paddle.paint(g);
paintBricks(g);
// Draw lives left
// TODO: Draw lives left in the top left hand corner**
if(livesLeft != 0) {
String displayLives = Integer.toString(livesLeft);
g.setFont(new Font("Arial", Font.BOLD, 18));
g.drawString(displayLives, Settings.LIVES_POSITION_X, Settings.LIVES_POSITION_Y);
}
// Draw screen message*
if(screenMessage != null) {
g.setFont(new Font("Arial", Font.BOLD, 18));
int messageWidth = g.getFontMetrics().stringWidth(screenMessage);
g.drawString(screenMessage, (Settings.WINDOW_WIDTH / 2) - (messageWidth / 2),
Settings.MESSAGE_POSITION);
}
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
paddle.setXVelocity(-1);
}
if (key == KeyEvent.VK_RIGHT) {
paddle.setXVelocity(1);
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.setXVelocity(0);
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
#Override
public void actionPerformed(ActionEvent arg0) {
update();
}
}
I made a similar game in python years ago. What I did to solve this problem was allow the player to control the direction of the ball by hitting it with different parts of the paddle.
If the player hit the ball with the absolute center of the paddle, then I would just reverse the vector as if it had hit off a wall. But if the absolute edge of the paddle hit the ball, then I would increase the x velocity in the same direction of the edge that it hit. You can make the effect continuous, so if the the ball hits the paddle halfway between the edge and the center, it increases the x velocity half as much as it would have if it had hit the absolute edge.
This way the vector will continuously change and the ball will bounce off of things in a more interesting way, as well as give the player more control so they don't have to just wait until the ball eventually hits every brick in a predefined pattern.
You can use a coefficient, or more complex function if you don't want the effect of where the ball hits the paddle to effect the x velocity in such a linear manner.
You can use a similar strategy for handling collisions with the bricks. For instance, if the ball hits the corner of a brick, you probably don't want it to react as if it had hit the bottom, or the side, but have a reaction that is somewhere in between.

Java Hit Detection/Collision between objects

Being new to coding, I watched a video on how to create Pong. That went well, and as a result I wanted to try and recreate Brick Breaker using some of the coding techniques that were used for Pong. So far I have the ball, paddle, and the base of the game. However, the ball does not collide properly with the paddle, instead of bouncing of it passes through.
Code for base game:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Breaker extends Applet implements Runnable, KeyListener{
final int WIDTH = 600, HEIGHT= 400;
Thread thread;
Graphics gfx;
Image img;
Ball b1;
Player p1;
public void init() {
this.resize(WIDTH, HEIGHT);
this.addKeyListener(this);
b1 = new Ball();
p1 = new Player(1);
img = createImage(WIDTH,HEIGHT);
gfx = img.getGraphics();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
gfx.setColor(Color.black);
gfx.fillRect(0, 0, WIDTH, HEIGHT);
b1.draw(gfx);
p1.draw(gfx);
g.drawImage(img, 0, 0, this);
}
public void update (Graphics g) {
paint(g);
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT){
p1.setLeftAccel(true);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
p1.setRightAccel(true);
}
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT){
p1.setLeftAccel(false);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
p1.setRightAccel(false);
}
}
#Override
public void run() {
for(;;) {
p1.move();
b1.move();
b1.checkPlayerCollision(p1);
repaint();
getToolkit().sync();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
Ball:
import java.awt.Graphics;
import java.awt.Color;
public class Ball {
int score;
double xVel, yVel, x, y;
public Ball() {
x= 350;
y = 250;
xVel= 0;
yVel = 1;
}
public void draw(Graphics g) {
g.setColor(Color.white);
g.fillOval((int)x - 10, (int)y - 10, 20, 20);
}
public void checkPlayerCollision(Player p1) {
if (x <= 50) {
if(x >= p1.getX() && x <= p1.getX() + 10)
xVel = -xVel;
}
else if (x >= 680) {
xVel = -xVel;
}
}
public void move() {
x += xVel;
y += yVel;
if (y < 10) //Bounce at top side of screen
yVel = -yVel;
if (x < 10) // Bounce at left side of screen
xVel = -xVel;
}
public int getX() {
return (int)x;
}
public int getY() {
return (int)y;
}
}
Player:
import java.awt.Color;
import java.awt.Graphics;
public class Player implements Paddle {
double x, xVel;
boolean leftAccel, rightAccel;
int player, y;
public Player(int player) {
leftAccel = false; rightAccel = false;
x=260; xVel=0;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.white);
g.fillRect((int)x, 380, 80, 10);
}
#Override
public void move() {
if(leftAccel){
xVel -= 2;
}
else if (rightAccel) {
xVel += 2;
}
else if (!leftAccel && !rightAccel) {
xVel = 0;
}
if(xVel >= 5) {
xVel = 5;
}
else if (xVel <= -5) {
xVel = -5;
}
x += xVel;
if(x < 0)
x=0; //Collision with left wall
if(x > 520)
x = 520; //Collision with right wall
}
public void setLeftAccel(boolean input) {
leftAccel = input;
}
public void setRightAccel(boolean input) {
rightAccel = input;
}
#Override
public int getX() {
return (int)x;
}
}
Paddle:
import java.awt.Graphics;
public interface Paddle {
public void draw(Graphics g);
public void move();
public int getX();
}
I don't completely understand hit detection yet, so I just messed with what was used for pong, however, that doesn't work (and I'm sure it might look weird).
Collision detection can be hard to wrap your head around sometimes. What really helps me is to draw on a piece of paper the things I need to check for and write the checks in code one by one. It's also a good idea to keep your axes in mind, it's really easy to mix up an x and a y, but they will have an important effect on gameplay.
My eye was drawn to your checkPlayerCollision function as it seemed a little off, I have changed some checks around and commented what each check/function is doing.
public void checkPlayerCollision(Player p1) {
if (
// Is the ball at or below paddle height?
y <= 50
// AND Is the ball right of the left side of the paddle?
&& x >= p1.getX()
// AND Is the ball left of the right side of the paddle?
&& x <= p1.getX() + 10
) {
// Collision with the paddle!
// (Ball is lower than y=50 and between left and right side of the paddle!)
// As we want the ball to go back up, we will be changing its velocity
// along the Y-axis.
yVel -= yVel;
} else if (x >= 680) {
// This checks whether the ball went too far to the right..?
xVel -= xVel;
}
}
Best tip I can give you: grab a piece of paper and just draw a simple grid or graph and use that to draw out different things you need to check. Be sure to label your (0,0) point so when you work with the different borders, you know which ones are 0 (usually top and left borders) and which ones are equal to width and height of the canvas.

Eclipse Game Lags Too Much

I am making a game and tutorials for how to make it on youtube. Here is the link to the channel. I explain the first part of what I have and why I have it because I know that is helpful for filling you in.
Link to part 1(Then watch the rest of parts. #Chris, this is helpful for solving the problem so don't flag the post).
https://www.youtube.com/watch?v=IRn_ZGhJZ94
I noticed as I was testing out my code for part 4. before recording, the game lagged HORRIBLY. I have alot of code, and any help is appreciated.
Game class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class Game extends JPanel implements ActionListener{
Timer mainTimer;
Paddle paddle;
Ball ball;
int blockCount = 16;
static ArrayList<Block> blocks = new ArrayList<Block>();
public Game() {
setFocusable(true);
paddle = new Paddle(250, 300);
addKeyListener(new KeyAdapt(paddle));
ball = new Ball(275, 280);
mainTimer = new Timer(10, this);
mainTimer.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/background.png");
g2d.drawImage(ic.getImage(), 0, 0, null);
paddle.draw(g2d);
ball.draw(g2d);
for(int i = 0; i < blockCount; i++) {
Block b = blocks.get(i);
b.draw(g2d);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
paddle.update();
ball.update();
for(int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
b.update();
}
repaint();
startGame();
}
public void addBlock(Block b) {
blocks.add(b);
}
public static void removeBlock(Block b) {
blocks.remove(b);
}
public static ArrayList<Block> getBlockList() {
return blocks;
}
public void startGame() {
for(int i = 0; i < blockCount; i++) {
addBlock(new Block(i*60 + 7, 20));
addBlock(new Block(i*60 + 7, 0));
}
}
}
Main class(The frame part):
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Game");
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Game());
frame.setResizable(false);
frame.setVisible(true);
}
}
Key Adapt class:
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class KeyAdapt extends KeyAdapter{
Paddle p;
public KeyAdapt(Paddle paddle) {
p = paddle;
}
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
}
Paddle class:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
public class Paddle {
int velX;
int speed = 3;
static int x1, y1;
public Paddle(int x1, int y1) {
this.x1 = x1;
this.y1 = y1;
}
public void update() {
x1+=velX;
checkCollisions();
}
public void draw(Graphics2D g2d) {
g2d.drawImage(getPaddleImg(), x1, y1, null);
}
public static Image getPaddleImg() {
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/paddle.png");
return ic.getImage();
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key==KeyEvent.VK_D) {
velX = speed;
} else if(key==KeyEvent.VK_A){
velX = -speed;
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(key==KeyEvent.VK_D) {
velX = 0;
} else if(key==KeyEvent.VK_A){
velX = 0;
}
}
public void checkCollisions() {
if(getBounds().getX() + getBounds().getWidth() >= 500) {
x1 = 440;
} else if(getBounds().getX() <= 0) {
x1 = 0;
}
}
public static Rectangle getBounds() {
return new Rectangle(x1, y1 - 1, getPaddleImg().getWidth(null), getPaddleImg().getHeight(null));
}
}
Ball class:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
public class Ball {
int velX;
int velY;
int speed = 3;
int x, y;
public Ball(int x, int y) {
this.x = x;
this.y = y;
}
public void update() {
x+=velX;
y+=velY;
checkCollisions();
}
public void draw(Graphics2D g2d) {
g2d.drawImage(getBallImg(), x, y, null);
}
public Image getBallImg() {
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/ball.png");
return ic.getImage();
}
public void checkCollisions() {
for(int i = 0; i < Game.getBlockList().size(); i++) {
Block b = Game.getBlockList().get(i);
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
Game.removeBlock(b);
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
Game.removeBlock(b);
}
}
if(getBounds().intersects(Paddle.getBounds())) {
velY = -speed;
} else if (getBounds().getY() <= 0 && velX!=speed) {
velY = speed;
velX =- speed;
}else if (getBounds().getY() <= 0 && velX!=-speed) {
velY = speed;
velX = speed;
} else if(getBounds().getY() >= 400) {
JOptionPane.showMessageDialog(null, "You Lost! :( ");
System.exit(0);
}
if(getBounds().getX() <= 0) {
velX = speed;
} else if(getBounds().getX() >= 500 - getBounds().getWidth()) {
velX = -speed;
}
}
public Rectangle getBounds() {
return new Rectangle(x, y, getBallImg().getWidth(null), getBallImg().getHeight(null));
}
}
Block class:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
public class Block {
int x2, y2;
public Block(int x2, int y2) {
this.x2 = x2;
this.y2 = y2;
}
public void update() {
}
public void draw(Graphics2D g2d){
g2d.drawImage(getBlockImg(), x2, y2, null);
}
public static Image getBlockImg() {
ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/block.png");
return ic.getImage();
}
public Rectangle getBounds() {
return new Rectangle(x2, y2, getBlockImg().getWidth(null), getBlockImg().getHeight(null));
}
}
I also have a folder called Eclipse Game on my desktop and I refer to it in my code.
Again, I understand this is alot but any idea with making it lag less is helpful. Also, watching the tutorial (look at the beginning for the link) on making what I have finished so far will help make it less confusing for you to understand how the code works. The game seriously lags so much I cannot play.
There are multiple issues.
The first, as I already mentioned in my comment, is that you're calling startGame() inside your timer action listener:
#Override
public void actionPerformed(ActionEvent arg0) {
paddle.update();
ball.update();
for(int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
b.update();
}
repaint();
startGame();
}
This is adding 3,200 blocks every second to the game, so you don't want that. I think the simplest place to put startGame() is at the end of the game constructor:
public Game() {
setFocusable(true);
paddle = new Paddle(250, 300);
addKeyListener(new KeyAdapt(paddle));
ball = new Ball(275, 280);
mainTimer = new Timer(10, this);
mainTimer.start();
startGame();
}
The other really big problem is that you're constantly reloading the images all the time. For example, look at this snippet:
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
Game.removeBlock(b);
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
Game.removeBlock(b);
}
That is 4 calls to getBounds(), and if we take a look at that:
return new Rectangle(x2, y2, getBlockImg().getWidth(null), getBlockImg().getHeight(null));
You are loading 2 images which in total is 4*2*blockCount images every 10ms, just for this one method. Instead of loading images all the time, do something like this:
class GameResources {
static Image ballImage;
static Image paddleImage;
static Image blockImage;
// call GameResources.loadResources() at the
// beginning of main() or something
static void loadResources() {
// load all 3 images once here and be done
ballImage = ...;
paddleImage = ...;
blockImage = ...;
}
Then finally, you have an issue with removing items from the list while iterating over it, Ball.checkCollisions:
for(int i = 0; i < Game.getBlockList().size(); i++) {
Block b = Game.getBlockList().get(i);
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
// removeBlock changes blocks.size()
Game.removeBlock(b);
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
// removeBlock changes blocks.size()
Game.removeBlock(b);
}
}
Instead you need to do something like this:
Iterator<Block> iter = Game.getBlockList().iterator();
while (it.hasNext()) {
Block b = it.next();
if(getBounds().intersects(b.getBounds()) && velX!=-speed) {
velY=speed;
velX =- speed;
// safely removing
it.remove();
}
else if(getBounds().intersects(b.getBounds())) {
velY=speed;
velX = speed;
// safely removing
it.remove();
}
}
And another possible boundary issue in Game.paint:
// using blockCount after possibly
// removing items from the list
// vvvvvvvvvv
for(int i = 0; i < blockCount; i++) {
Block b = blocks.get(i);
b.draw(g2d);
}
For simple iterations like this, you should use for-each:
for(Block b : blocks) {
b.draw(g2d);
}
After all of that the game runs pretty smoothly, except for some type of issue with the key listener which I didn't have time to figure out. I might look at it again after dinner.
edit:
I noticed a lot of other small things, so here is the program fixed up a bit more with my comments.
Some of the classes aren't public anymore just because I had them all in one source file.
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Dimension;
import java.net.URL;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.io.File;
public class BlockGame {
public static void main(String[] args) {
// Swing program should always begin on the Swing
// thread with a call to invokeLater.
// See https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
// change this to
// .loadImages();
GameResources.loadInternetImages();
} catch (IOException x) {
x.printStackTrace();
return;
}
JFrame frame = new JFrame("Game");
// frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.add(new Game());
// Instead of calling setSize on the JFrame
// directly, set a preferred size on the game
// panel, then call pack() on the JFrame
Game game = new Game();
game.setPreferredSize(new Dimension(500, 400));
frame.add(game);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
// I started the game here instead
// of in the game loop, so the panel
// is visible and stuff beforehand.
game.startGame();
}
});
}
}
class Game extends JPanel implements ActionListener {
Timer mainTimer;
Paddle paddle;
Ball ball;
// I removed this because it's only ever
// used by startGame.
// int blockCount = 16;
// I changed this to an instance variable
// (not static) and passed the game in to
// update so the game objects can access
// it.
ArrayList<Block> blocks = new ArrayList<Block>();
public Game() {
setFocusable(true);
paddle = new Paddle(250, 300);
addKeyListener(new KeyAdapt(paddle));
ball = new Ball(275, 280);
mainTimer = new Timer(10, this);
// I moved this to the startGame() method
// mainTimer.start();
}
// Swing programs should override paintComponent
// instead of paint.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// You should create a copy instead of
// directly using the graphics object which
// the component uses.
// This is so any changes you make to it
// don't affect the Swing paint routines.
Graphics2D g2d = (Graphics2D) g.create();
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/background.png");
// g2d.drawImage(ic.getImage(), 0, 0, null);
// Painting static resource.
g2d.drawImage(GameResources.backgroundImage, 0, 0, null);
paddle.draw(g2d);
ball.draw(g2d);
// This loop will throw an out of bounds
// exception once the first block is removed.
// vvvvvvvvvv
// for(int i = 0; i < blockCount; i++) {
// Block b = blocks.get(i);
// b.draw(g2d);
// }
// using for each
for (Block b : blocks) {
b.draw(g2d);
}
// Dispose the copied graphics when you're done.
g2d.dispose();
}
#Override
public void actionPerformed(ActionEvent arg0) {
paddle.update(this);
ball.update(this);
// for(int i = 0; i < blocks.size(); i++) {
// Block b = blocks.get(i);
// b.update();
// }
for (Block b : blocks) {
b.update(this);
}
repaint();
// I moved this to main
// startGame();
}
public void addBlock(Block b) {
blocks.add(b);
}
public void removeBlock(Block b) {
blocks.remove(b);
}
public ArrayList<Block> getBlockList() {
return blocks;
}
// I added this method so that the
// ball can access the paddle without
// static variables.
public Paddle getPaddle() {
return paddle;
}
public void startGame() {
// So the method won't be called twice
// and put the game in some unexpected
// state.
if (mainTimer.isRunning()) {
throw new IllegalStateException("game already started");
}
int initialBlockCount = 16;
for(int i = 0; i < initialBlockCount; i++) {
addBlock(new Block(i*60 + 7, 20));
addBlock(new Block(i*60 + 7, 0));
}
mainTimer.start();
}
}
// Generally speaking you should use
// Swing key bindings now, instead of
// key listeners.
//
// Key listeners have problems with
// the focus system: Swing components
// only send out key events when they
// have the focus.
//
// Key bindings don't have this issue.
//
// You can set up key bindings so they
// trigger any time the key is pressed
// in the focused window.
//
// https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
//
class KeyAdapt extends KeyAdapter {
Paddle p;
public KeyAdapt(Paddle paddle) {
p = paddle;
}
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
}
class Paddle {
int velX;
int speed = 3;
// I changed these from static
// to instance variables.
int x1, y1;
// I added these variables to
// help with the key listener
// logic.
boolean leftPressed, rightPressed;
public Paddle(int x1, int y1) {
this.x1 = x1;
this.y1 = y1;
}
public void update(Game game) {
x1 += velX;
checkCollisions();
}
public void draw(Graphics2D g2d) {
g2d.drawImage(GameResources.paddleImage, x1, y1, null);
}
// public static Image getPaddleImg() {
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/paddle.png");
// return ic.getImage();
// }
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
// This logic is a little more robust
// because it handles cases where both
// keys are being held at the same time.
// Also see computeVelX().
if (key == KeyEvent.VK_D) {
leftPressed = true;
// velX = speed;
} else if (key == KeyEvent.VK_A) {
rightPressed = true;
// velX = -speed;
}
computeVelX();
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
// This logic is a little more robust
// because it handles cases where both
// keys are being held at the same time.
// Also see computeVelX().
if (key == KeyEvent.VK_D) {
leftPressed = false;
// velX = 0;
} else if (key == KeyEvent.VK_A) {
rightPressed = false;
// velX = 0;
}
computeVelX();
}
public void computeVelX() {
// This way the keys will never
// "stick". If both keys are
// held at the same time, velX
// is just 0 until one of the
// keys is released.
velX = 0;
if (leftPressed) {
velX += speed;
}
if (rightPressed) {
velX -= speed;
}
}
public void checkCollisions() {
// I used a variable instead of calling
// getBounds() repeatedly.
Rectangle bounds = getBounds();
if (bounds.getX() + bounds.getWidth() >= 500) {
x1 = 440;
} else if (bounds.getX() <= 0) {
x1 = 0;
}
}
// I change this from static to an instance method.
public Rectangle getBounds() {
// return new Rectangle(x1, y1 - 1, getPaddleImg().getWidth(null), getPaddleImg().getHeight(null));
int width = GameResources.paddleImage.getWidth(null);
int height = GameResources.paddleImage.getHeight(null);
return new Rectangle(x1, y1 - 1, width, height);
}
}
class Ball {
int velX;
int velY;
int speed = 3;
int x, y;
public Ball(int x, int y) {
this.x = x;
this.y = y;
}
public void update(Game game) {
x += velX;
y += velY;
checkCollisions(game);
}
public void draw(Graphics2D g2d) {
// g2d.drawImage(getBallImg(), x, y, null);
g2d.drawImage(GameResources.ballImage, x, y, null);
}
// public Image getBallImg() {
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/ball.png");
// return ic.getImage();
// }
public void checkCollisions(Game game) {
// Using an iterator instead of looping with size()
// directly, because we want to remove items from
// the list while iterating.
// The problem with removing while iterating with
// size() is that once you remove an element, the
// list shifts all the other elements back by 1,
// so on the next iteration of the loop you end
// up skipping an item.
// (Say you remove the element at index 5. Then
// all the elements shift back, so that e.g. the
// element at index 6 is now at index 5. The variable
// i is incremented, so you end up skipping the element
// that was at index 6 before the removal.
Iterator<Block> iter = game.getBlockList().iterator();
Rectangle bounds = getBounds();
while (iter.hasNext()) {
Block b = iter.next();
Rectangle bBounds = b.getBounds();
if (bounds.intersects(bBounds) && velX != -speed) {
velY = speed;
velX =- speed;
// Game.removeBlock(b);
iter.remove();
} else if (bounds.intersects(bBounds)) {
velY = speed;
velX = speed;
// Game.removeBlock(b);
iter.remove();
}
}
//
Rectangle pBounds = game.getPaddle().getBounds();
if (bounds.intersects(pBounds)) {
velY = -speed;
} else if (bounds.getY() <= 0 && velX != speed) {
velY = speed;
velX =- speed;
} else if (bounds.getY() <= 0 && velX != -speed) {
velY = speed;
velX = speed;
} else if (bounds.getY() >= 400) {
JOptionPane.showMessageDialog(null, "You Lost! :( ");
System.exit(0);
}
if (bounds.getX() <= 0) {
velX = speed;
} else if(bounds.getX() >= 500 - bounds.getWidth()) {
velX = -speed;
}
}
public Rectangle getBounds() {
// return new Rectangle(x, y, getBallImg().getWidth(null), getBallImg().getHeight(null));
int width = GameResources.ballImage.getWidth(null);
int height = GameResources.ballImage.getHeight(null);
return new Rectangle(x, y, width, height);
}
}
class Block {
int x2, y2;
public Block(int x2, int y2) {
this.x2 = x2;
this.y2 = y2;
}
public void update(Game game) {
}
public void draw(Graphics2D g2d){
// g2d.drawImage(getBlockImg(), x2, y2, null);
g2d.drawImage(GameResources.blockImage, x2, y2, null);
}
// public static Image getBlockImg() {
// ImageIcon ic = new ImageIcon("C:/Users/Elliot/Desktop/Eclipse Game/block.png");
// return ic.getImage();
// }
public Rectangle getBounds() {
// return new Rectangle(x2, y2, getBlockImg().getWidth(null), getBlockImg().getHeight(null));
int width = GameResources.blockImage.getWidth(null);
int height = GameResources.blockImage.getHeight(null);
return new Rectangle(x2, y2, width, height);
}
}
class GameResources {
public static Image backgroundImage;
public static Image blockImage;
public static Image ballImage;
public static Image paddleImage;
public static void loadImages() throws IOException {
// Load images once here.
// I didn't test this method since I don't have the images, but it
// should work. ImageIO.read will give better error messages than
// using ImageIcon. ImageIcon.getImage() will just return null if
// there was a problem, which doesn't tell you what the problem
// actually was.
paddleImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/paddle.png"));
ballImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/ball.png"));
blockImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/block.png"));
backgroundImage =
ImageIO.read(new File("C:/Users/Elliot/Desktop/Eclipse Game/background.png"));
}
public static void loadInternetImages() throws IOException {
// These images are from
// http://stackoverflow.com/questions/19209650/example-images-for-code-and-mark-up-qas
paddleImage =
ImageIO.read(new URL("http://i.stack.imgur.com/gYxHm.png"));
ballImage =
ImageIO.read(new URL("http://i.stack.imgur.com/gJmeJ.png"));
blockImage =
ImageIO.read(new URL("http://i.stack.imgur.com/F0JHK.png"));
backgroundImage =
ImageIO.read(new URL("http://i.stack.imgur.com/P59NF.png"));
}
}

creating many objects with collision properties. JAVA

I have a simple program with three rectangles: one that can move with the push of the arrow keys, and two that are already moving back and forth on their own.
When the 'player' rectangle and top red collide, the player driven rectangle gets put back to (0,0). When I try to collide the player rectangle with the bottom red rectangle, it does not have those collision properties and I have no idea why.
What am I missing?
import java.awt.*;//needed for graphics
import javax.swing.*;//needed for JFrame window
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class javaapplication23 extends JFrame implements KeyListener, ActionListener {
public static int x = 0;
public static int y = 0;
public static int x2 = 100;
public static int y2 = 100;
public javaapplication23() {//constructor for JPanel
add(new JP());
}//close Jpanel Contructor
public static void main(String[] args) {
javaapplication23 w = new javaapplication23();
w.setTitle("MIKE IS AWESOME");
w.setSize(Toolkit.getDefaultToolkit().getScreenSize());
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.setVisible(true);
w.addKeyListener(w);
}
public class JP extends JPanel {//start JPanel CLass
public JP() {
Container c = getContentPane();
c.setBackground(Color.white);//backgraund color can be changed
}
public void paint(Graphics g) {//opens paint method
super.paint(g);
player(g, x, y);
g.setColor(Color.RED);
enemylevel1(g, x2, y2);
Rectangle enemyblocks = new Rectangle(x2, y2, 25, 25);
Rectangle player = new Rectangle(x, y, 25, 25);
enemyblocks.contains(x2, y2);
player.contains(x, y);
if (player.getBounds().intersects(enemyblocks.getBounds())) {
x = 0;
y = 0;
}
pause(1);
repaint();
}//close paint method
}//close JPanel Class
public static void pause(int time) {
try //opens an exception handling statement
{
Thread.sleep(time);
} catch (InterruptedException e) {
} //captures the exception
}
public void actionPerformed(ActionEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == e.VK_RIGHT) {
x += 20;//global variable controlling right movement
repaint();
}
if (e.getKeyCode() == e.VK_LEFT) {
x -= 20;//global variable controlling left movement
repaint();
}
if (e.getKeyCode() == e.VK_UP) {
y -= 20;//global variable controlling up movement
repaint();
}
if (e.getKeyCode() == e.VK_DOWN) {
y += 20;//global variable controlling down movement
repaint();
}
}
public void player(Graphics g, int x, int y) {
g.fillRect(x, y, 30, 30);
}
public void enemylevel1(Graphics g, int x, int y) {
g.fillRect(x2, y2, 25, 25);
g.fillRect(x2, y2 + 100, 25, 25);
if (x2 < 200 && y2 == 100) {
x2 += 1;
}
if (x2 == 200 && y2 >= 100) {
y2 += 1;
}
if (x2 <= 200 && y2 >= 101) {
x2 -= 1;
}
if (x2 == 100 && y2 <= 101) {
y2 -= 1;
}
pause(10);
repaint();
}
}
Start by having a look at Working with Geometry, this will allow you to reduce much of the code complexity.
Basically, a enemy is just a Rectangle, Graphics2D can paint these without to much of an issue. What you need to do is create an instance which can also update it's position based on your needs
public class Enemy extends Rectangle {
private int xDelta;
public Enemy(int x, int y) {
super(x, y, 20, 20);
if (x == 0) {
xDelta = 1;
} else {
xDelta = -1;
}
}
public void update(Rectangle bounds) {
x += xDelta;
if (x < bounds.x) {
x = bounds.x;
xDelta *= -1;
} else if (x > bounds.x + bounds.width - width) {
x = bounds.x + bounds.width - width;
xDelta *= -1;
}
}
}
So, this creates a single unit of work, which is isolated from everything else and carries it's own logic with it. This makes updating it, painting and generally working with much simpler.
Next, you need to create a List of these
public class Bounce extends JPanel implements KeyListener, ActionListener {
private List<Enemy> enemies;
//...
public Bounce() {
enemies = new ArrayList<>(5);
int y = 100;
for (int index = 0; index < 5; index++) {
int x = (index % 2 == 0) ? 0 : 200;
Enemy enemy = new Enemy(x, y);
enemies.add(enemy);
y += 60;
}
This creates a List of Enemys which are distributed evenly within the container.
Now, we need to paint them....
#Override
protected void paintComponent(Graphics g) {//opens paint method
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.RED);
for (Enemy enemy : enemies) {
g2d.fill(enemy);
}
}//close paint method
nb: General convention suggests that you should override paintComponent when you want to perform custom painting
But they don't move, that kind of sucks. So we need a way to, on a regular bases, update the position of the enemies...
First, we create a simple method which we can call to update the enemies, remember, they are capable of updating themselves, we just need to tell them when
public void updateState() {
Rectangle bounds = new Rectangle(20, 20, 200, 200);
for (Enemy enemy : enemies) {
enemy.update(bounds);
}
}
Remember, the Enemy is self contained, it knows how to update itself based on the constraints you have provided.
And now, we need to call this method on a regular bases...
javax.swing.Timer timer = new javax.swing.Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateState();
repaint();
}
});
timer.start();
Okay, this will schedule a callback every 40 milliseconds which will allow us to call the updateState method and repaint the component. This is neat because it won't block the Event Dispatching Thread (making our program look like it's hung) but which notifies us within the context of the EDT, making it safe to update the UI from within - WIN/WIN :)
Take a look at Concurrency in Swing and How to use Swing Timers for more details.
Okay, but that doesn't solve the collision...
The player is also a Rectangle, so why not use the same concept we have with the enemies...
public class Bounce extends JPanel implements KeyListener, ActionListener {
private List<Enemy> enemies;
private Rectangle player;
//...
public Bounce() {
player = new Rectangle(0, 0, 30, 30);
enemies = new ArrayList<>(5);
//...
}
#Override
protected void paintComponent(Graphics g) {//opens paint method
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawPlayer(g2d);
g2d.setColor(Color.RED);
for (Enemy enemy : enemies) {
g2d.fill(enemy);
if (player.intersects(enemy)) {
player.x = 0;
player.y = 0;
}
}
}//close paint method
public void drawPlayer(Graphics2D g) {
g.fill(player);
}
Which ends up with something like...
This allows you to add/remove enemies as you want and also change the way in which the enemies move, simply and easily
An my "awesome" test code...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Bounce extends JPanel implements KeyListener, ActionListener {
private List<Enemy> enemies;
private Rectangle player;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Bounce());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public Bounce() {
player = new Rectangle(0, 0, 30, 30);
enemies = new ArrayList<>(5);
int y = 100;
for (int index = 0; index < 5; index++) {
int x = (index % 2 == 0) ? 0 : 200;
Enemy enemy = new Enemy(x, y);
enemies.add(enemy);
y += 60;
}
setBackground(Color.white);//backgraund color can be changed
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateState();
repaint();
}
});
timer.start();
setFocusable(true);
requestFocusInWindow();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
requestFocusInWindow();
}
});
addKeyListener(this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(240, 400);
}
#Override
protected void paintComponent(Graphics g) {//opens paint method
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
drawPlayer(g2d);
g2d.setColor(Color.RED);
for (Enemy enemy : enemies) {
g2d.fill(enemy);
if (player.intersects(enemy)) {
player.x = 0;
player.y = 0;
}
}
}//close paint method
public void actionPerformed(ActionEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == e.VK_RIGHT) {
player.x += 20;//global variable controlling right movement
}
if (e.getKeyCode() == e.VK_LEFT) {
player.x -= 20;//global variable controlling left movement
}
if (e.getKeyCode() == e.VK_UP) {
player.y -= 20;//global variable controlling up movement
}
if (e.getKeyCode() == e.VK_DOWN) {
player.y += 20;//global variable controlling down movement
}
}
public void drawPlayer(Graphics2D g) {
g.fill(player);
}
public void updateState() {
Rectangle bounds = new Rectangle(20, 20, 200, 200);
for (Enemy enemy : enemies) {
enemy.update(bounds);
}
}
public class Enemy extends Rectangle {
private int xDelta;
public Enemy(int x, int y) {
super(x, y, 20, 20);
if (x == 0) {
xDelta = 1;
} else {
xDelta = -1;
}
}
public void update(Rectangle bounds) {
x += xDelta;
if (x < bounds.x) {
x = bounds.x;
xDelta *= -1;
} else if (x > bounds.x + bounds.width - width) {
x = bounds.x + bounds.width - width;
xDelta *= -1;
}
}
}
}

Why does it wants the method to be static?

I am building my first game, relying heavily on various tutorials and guides on the Java website, and I have come across a problem.
In my game engine, I want to call the Player.update() method, but it says it has to be static (cannot make static reference to non-static method)
However, the method calling it is not static. Can anyone tell me why it requires it to be static? It doesn't require the only other method in Update to be static.
package Main;
import java.awt.*;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import Graphics.Assets;
import Sprites.Player;
#SuppressWarnings("serial")
public class Game extends JPanel
implements Runnable, KeyListener{
//TEST CODE
private int x = 0;
private int y = 0;
private int dY = 1;
private int dX = 1;
public void moveBall() {
x = x + dX;
y = y + dY;
if(x > WIDTH - 28) {
dX = -1;
}
if(y > HEIGHT) {
dY = -1;
}
if(x < 0) {
dX = 1;
}
if(y < 10) {
dY = 1;
}
}
//dimensions
public static final int WIDTH = 400;
public static final int HEIGHT = 300;
public static final int SCALE = 2;
//game thread
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000 / FPS;
//image
private BufferedImage image;
private Graphics2D g;
//Constructor
public Game () {
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init () {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D)image.getGraphics();
running = true;
Assets.init();
}
public void run() {
init();
long start;
long elapsed;
long wait;
//game loop
while(running){
start = System.nanoTime();
update();
draw(g);
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
if(wait < 0) wait= 5;
try {
Thread.sleep(wait);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
private void update() {
moveBall();
Player.update();
}
private void draw(Graphics g2d) {
super.paint(g2d);
Graphics2D g = (Graphics2D) g2d;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.drawString("Hello", x, y);
}
private void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
g2.dispose();
}
public void keyPressed(KeyEvent e){}
public void keyReleased(KeyEvent e){}
public void keyTyped(KeyEvent e){}
}
That is the main code. Now for the Player class.
package Sprites;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import Main.Game;
import Graphics.*;
public class Player extends Creature implements KeyListener{
private int dir;
public Player(Game game, float x, float y) {
super(game, x, y, Creature.DEFAULT_CREATURE_WIDTH, Creature.DEFAULT_CREATURE_HEIGHT);
dir = 0;
}
public void update() {
getInput();
move();
}
private void getInput() {
xMove = dir;
}
public void render(Graphics g) {
g.drawImage(Assets.player, (int) (x), (int) (y), width, height, null);
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_A)
dir = -1;
if(e.getKeyCode() == KeyEvent.VK_D)
dir = 1;
else dir = 0;
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_A)
dir = 0;
if(e.getKeyCode() == KeyEvent.VK_D)
dir = 0;
}
public void keyTyped(KeyEvent e) {
}
}
Look at your method call:
Player.update();
That's calling it as if it's a static method - on the type, rather than on an instance of the type. Which player do you want to update? You don't seem to have any instances of it... you should almost certainly be creating an instance of Player within Game, and saving a reference to it.
You have called update() method of Player like this -
private void update() {
moveBall();
Player.update();
}
Here you did not create the object of Player. So the compiler expect the update() method is static. To resolve the problem you may do either of -
1. Make update() of player static. Or -
2. Create an instance/object of Player inside your Game class as a field/property before calling update() method of Player class -
Player aPlayer = new Player(.....);
.....
.....
.....
private void update(){
moveBall();
aPlayer.update();
}

Categories

Resources