I am having trouble with collision detection in a 2d java game that I am currently trying to make. For some reason I keep on getting the following error:
Exception in thread "Thread-2" java.lang.NullPointerException
at Objects.Bullet.Collision(Bullet.java:57)
at Objects.Bullet.tick(Bullet.java:39)
at window.Handler.tick(Handler.java:15)
at window.Game.tick(Game.java:96)
at window.Game.run(Game.java:76)
at java.lang.Thread.run(Unknown Source)
Here the code that I currently have for my bullet:
package Objects;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.LinkedList;
import window.Game;
import window.Handler;
import Framework.GameObject;
import Framework.ObjectId;
import Framework.Texture;
public class Bullet extends GameObject{
private float width=32,height=32;
private final float MAX_SPEED = 10;
private Handler handler;
Texture tex= Game.getInstance();
public Bullet(float x,float y, ObjectId id,int velX){
super(x,y,id);
this.velX = velX;
}
public void tick(LinkedList<GameObject> object) {
x+=velX;
y+=velY;
if(velX<0)orientation =-1;
else if(velX>0)orientation=1;
if(velY>MAX_SPEED){
velY = MAX_SPEED;}
Collision(object);
}
public void render(Graphics g){
g.drawImage(tex.bullet[0],(int)x ,(int)y,null);
}
public Rectangle getBoundsRight() {
return new Rectangle((int) ((int)x+width-5),(int)y+5,(int)5, (int)height-10);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)x,(int)y+5,(int)5,(int)height-10);
}
private void Collision(LinkedList<GameObject>object){
for(int i=0; i<handler.object.size();i++){
GameObject tempObject = handler.object.get(i);
if(tempObject.getId() == ObjectId.Block){
if(getBoundsRight().intersects(tempObject.getBounds())){
velX=0;
x = tempObject.getX()- width;
}
if(getBoundsLeft().intersects(tempObject.getBounds())){
velX = 0;
x = tempObject.getX()+ (30);
}
}
}
}
public Rectangle getBounds() {
return null;
}
}
I basically copied the code from my player class in which everything works perfectly but for some reason I am getting errors with my bullet class
The code for my player class is shown below:
package Objects;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.LinkedList;
import window.Animation;
import window.Game;
import window.Handler;
import Framework.GameObject;
import Framework.ObjectId;
import Framework.Texture;
public class Player extends GameObject{
private float width = 64, height = 64;
private float gravity = 0.5f;
private final float MAX_SPEED = 10;
private Handler handler;
Texture tex = Game.getInstance();
//1= right
//-1= left
private Animation playerWalk,playerWalkLeft;
public Player(float x, float y,Handler handler, ObjectId id) {
super(x, y, id);
this.handler = handler;
playerWalk = new Animation(5,tex.player[1],tex.player[2],tex.player[3],tex.player[4],tex.player[5],tex.player[6]);
playerWalkLeft = new Animation(5,tex.player[7],tex.player[8],tex.player[9],tex.player[10],tex.player[11],tex.player[12]);
}
public void tick(LinkedList<GameObject> object) {
x+=velX;
y+=velY;
if(velX<0)orientation =-1;
else if(velX>0)orientation=1;
if(falling||jumping){
velY+=gravity;
if(velY>MAX_SPEED){
velY = MAX_SPEED;
}
}
Collision(object);
playerWalk.runAnimation();
playerWalkLeft.runAnimation();
}
private void Collision(LinkedList<GameObject>object){
for(int i=0; i<handler.object.size();i++){
GameObject tempObject = handler.object.get(i);
if(tempObject.getId() == ObjectId.Block){
if(getBoundsTop().intersects(tempObject.getBounds())){
y= tempObject.getY()+40 ;
velY=0;
}
if(getBounds().intersects(tempObject.getBounds())){
velY=0;
y= tempObject.getY()-height;
falling = false;
jumping = false;
}else
falling = true;
//Right
if(getBoundsRight().intersects(tempObject.getBounds())){
x = tempObject.getX()- width;
}
//Left
if(getBoundsLeft().intersects(tempObject.getBounds())){
x = tempObject.getX()+ (30);
}
}
}
}
public void render(Graphics g) {
g.setColor(Color.BLUE);
if(velX != 0 ){
if(orientation == 1)
playerWalk.drawAnimation(g,(int)x,(int)y,64,64);
else
playerWalkLeft.drawAnimation(g,(int)x,(int)y,64,64);
}else
if(orientation == 1)
g.drawImage(tex.player[1],(int)x,(int)y,64,64,null); //change here to change the size of the ball. does not account for hit-box must change in the width and height global variables above
else if(orientation == -1)
g.drawImage(tex.player[12],(int)x,(int)y,64,64,null);
}
public Rectangle getBounds() {
return new Rectangle((int) ((int)x+(width/2)-((width/2)/2)),(int) ((int)y+(height/2)),(int)width/2,(int)height/2);
}
public Rectangle getBoundsTop() {
return new Rectangle((int) ((int)x+(width/2)-((width/2)/2)),(int)y,(int)width/2,(int)height/2);
}
public Rectangle getBoundsRight() {
return new Rectangle((int) ((int)x+width-5),(int)y+5,(int)5,(int)height-10);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)x,(int)y+5,(int)5,(int)height-10);
}
}
Any form of help/hints.or suggestions will be greatly appreciated
Related
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
I am trying to "teleport" objects to the opposite positions whenever they get out of screen. I've managed to do this with right & bottom sides, but for some reason, which I haven't figured out, the star objects won't go off screen on TOP & LEFT sides UNLESS the lerping speed is high enough.
Preview:
Main.java
package asteroids;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class Main extends Canvas implements KeyListener {
private boolean gameOver;
private BufferStrategy backBuffer;
private Dimension dimension = new Dimension(Config.WINDOW_WH[0], Config.WINDOW_WH[1]);
private List<Star> stars = new ArrayList<Star>();
private HashMap<Integer,Boolean> keyDownMap = new HashMap<Integer, Boolean>();
private Ship ship;
private Image bg;
private float starGoalX, starGoalY;
public Main() {
// Initialize Window
initWindow();
addKeyListener(this);
this.createBufferStrategy(2);
backBuffer = this.getBufferStrategy();
bg = new ImageIcon(getClass().getResource("/bg.png")).getImage();
// init variables
gameOver = false;
// Generating stars
generateStars();
// Init spaceship
ship = new Ship(25,36,"ship.png");
// Init loop
gameLoop();
}
public void initWindow(){
JFrame window = new JFrame("Asteroids");
setPreferredSize(dimension);
window.add(this);
window.pack();
window.setResizable(false);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.requestFocus();
}
public float lerp(float starGoal, float vel, float speed) {
return starGoal + speed * (vel - starGoal);
}
public void update() {
if(keyDownMap.containsKey(KeyEvent.VK_ESCAPE)){
gameOver = false;
System.exit(0);
}
for(Star s: stars) {
s.posx += lerp(this.starGoalX, s.velX, Config.lerpSpeed);
s.velX += this.starGoalX * Config.lerpSpeed;
s.posy += lerp(this.starGoalY, s.velY, Config.lerpSpeed);
s.velY += this.starGoalY * Config.lerpSpeed;
checkPos(s);
s.update();
}
}
public void checkPos(Star s) {
if(s.posx < -s.width)
s.posx = Config.WINDOW_WH[0]-s.width;
else if(s.posx > Config.WINDOW_WH[0])
s.posx = 0;
if(s.posy < -s.height)
s.posy = Config.WINDOW_WH[1]-s.height;
else if(s.posy > Config.WINDOW_WH[1])
s.posy = 0;
}
public void render(){
Graphics2D g = (Graphics2D) backBuffer.getDrawGraphics();
if (!backBuffer.contentsLost()) {
g.drawImage(this.bg,0,0,Config.WINDOW_WH[0], Config.WINDOW_WH[1], null);
// Draw Stars
g.setColor(Color.WHITE);
for(Star s: stars)
g.fillOval(s.posx - (s.width/2), s.posy - (s.height/2), s.width, s.height);
// Draw ship
g.drawImage(
this.ship.getImage(),
this.ship.posx, this.ship.posy,
this.ship.width, this.ship.height, null);
backBuffer.show();
g.dispose();
}
}
public void generateStars() {
for(int i = 0;i < 50;i++) {
int starX = new Random().nextInt(Config.WINDOW_WH[0]+100)+5;
int starY = new Random().nextInt(Config.WINDOW_WH[1]+100)+5;
stars.add(new Star(starX, starY));
}
}
public void gameLoop(){
while(!gameOver){
update();
render();
try{ Thread.sleep(20);}catch(Exception e){};
}
}
public static void main(String[] args) {
new Main();
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT) this.starGoalX = Config.lerpSpeed;
if(e.getKeyCode() == KeyEvent.VK_RIGHT) this.starGoalX = -Config.lerpSpeed;
if(e.getKeyCode() == KeyEvent.VK_UP) this.starGoalY = Config.lerpSpeed;
if(e.getKeyCode() == KeyEvent.VK_DOWN) this.starGoalY = -Config.lerpSpeed;
keyDownMap.put(e.getKeyCode(), true);
}
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT
|| e.getKeyCode() == KeyEvent.VK_RIGHT) this.starGoalX = 0;
if(e.getKeyCode() == KeyEvent.VK_UP
|| e.getKeyCode() == KeyEvent.VK_DOWN) this.starGoalY = 0;
keyDownMap.remove(e.getKeyCode());
}
}
Star.java
package asteroids;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.HashMap;
import java.util.Random;
public class Star {
int width, height;
int posx, posy;
float velX, velY, velGoalX, velGoalY;
/** CONSTRUCTOR **/
public Star(int x, int y) {
int rand = new Random().nextInt(Config.STAR_SIZES.length);
width = Config.STAR_SIZES[rand];
height = Config.STAR_SIZES[rand];
posx = x;
posy = y;
}
public void update() {
// pass
}
}
Ship.java
package asteroids;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import javax.swing.ImageIcon;
public class Ship {
public int posx, posy;
public int width, height;
private Image image;
public Ship(int w, int h, String img) {
this.posx = Config.WINDOW_WH[0]/2;
this.posy = Config.WINDOW_WH[1]/2;
this.width = w;
this.height = h;
this.image = new ImageIcon(getClass().getResource("/"+img)).getImage();
}
public Image getImage() {
return this.image;
}
public void setPosx(int x) {posx = x;}
public void setPosy(int y) {posy = y;}
public void setImg(Image img) {
this.image = img;
}
public void update(HashMap keyDownMap) {
// pass
}
}
Config.java
package asteroids;
import java.awt.Color;
public class Config {
// MAIN CANVAS SETTINGS
public static int[] WINDOW_WH = {500, 500};
public static String WINDOW_BG_IMG = "";
public static Color WINDOW_BG_CLR = new Color(40, 42, 45);
// OBJECT SETTINGS
public static int[] STAR_SIZES = {10,5,8,3,7};
public static float lerpSpeed = 0.8f;
}
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"));
}
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I'm getting an error when I call my refresh() method from another class.
public void refresh() {
repaint();
}
It's a JFrame that the refresh() is in and I'm calling it from a normal class. The error is a Null Pointer, and it is on my level.refresh(), as well as as public void refresh().
What I am trying to do is working completely fine, but I'm getting this error
Player class`package entity.player;
import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import level.Level;
public class Player {
public static Image playerImgLeft = new ImageIcon(
"resources/Player/left/player_anim1_left.png").getImage();
public static Image playerImgRight = new ImageIcon(
"resources/Player/right/player_anim1_right.png").getImage();
public static Level levelp;
public int x = 0;
public int y = 0;
public int dy = 0;
public int dx = 0;
// Direction = right
public int direction = 0;
public Player() {
x = 10;
y = 10;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Image getImageLeft() {
return playerImgLeft;
}
public Image getImageRight() {
return playerImgRight;
}
public void move() {
x = x + dx;
y = y + dy;
}
public void keyPressed(KeyEvent key) {
if (key.getKeyCode() == KeyEvent.VK_A) {
dx = -1;
direction = 1;
System.out.println("left");
levelp.refresh();
}
if (key.getKeyCode() == KeyEvent.VK_D)
dx = 1;
direction = 0;
if (key.getKeyCode() == KeyEvent.VK_W)
dy = -1;
if (key.getKeyCode() == KeyEvent.VK_S)
dy = 1;
}
public void keyReleased(KeyEvent key) {
if (key.getKeyCode() == KeyEvent.VK_A)
dx = 0;
if (key.getKeyCode() == KeyEvent.VK_D)
dx = 0;
if (key.getKeyCode() == KeyEvent.VK_W)
dy = 0;
if (key.getKeyCode() == KeyEvent.VK_S)
dy = 0;
}
}
Level class:
package level;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
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;
import entity.player.Player;
public class Level extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
public static int level = 0;
Player player;
public Timer time;
public Image img;
public Level() {
this.setVisible(true);
setFocusable(true);
player = new Player();
this.addKeyListener(new LevelKeyListener());
ImageIcon i = new ImageIcon("");
img = i.getImage();
time = new Timer(5, this);
time.start();
}
public void actionPerformed(ActionEvent e) {
player.move();
repaint();
revalidate();
}
public void refresh () {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.drawImage(img, 0, 0, null);
if(player.direction == 0) {
g2d.drawImage(player.getImageRight(), player.getX(), player.getY(), null);
} else if(player.direction == 1) {
g2d.drawImage(player.getImageLeft(), player.getX(), player.getY(), null);
}
}
private class LevelKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
player.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
player.keyReleased(e);
}
}
}
`
As was suspected, your levelp variable points to nothing (is null).
When you look at this line you'll see that you're trying to call the refresh() method on a variable that isn't actually referencing anything:
levelp.refresh();
which is just declared as
public static Level levelp;
without any code that actually creates a Level object (ergo: you are trying to call a method on a reference that points to null).
Try creating an immediate object like this:
public static Level levelp = new Level();
I also encourage you to read through this post so you understand how and when NullPointerExceptions occur!
I need to make a system where a obstacle is drawn on the screen, when it scrolls off the screen, another one will be drawn at a random value (up to a max of a certain value) on the screen after that one.
I have some code here:
public int xElapsed = 0;
this is just incremented all the time, it is how much the player has moved.
obstacleHole.paint(g);
if(obstacleHole.getX() <= 0){
obstacleHole.paint(g);
}
This is the paint function. The first obstacle is painted straight away, and the second after that condition is met. This is not working as intended, however.
x = position.nextInt(500 + player.xElapsed * 2);
and this is how the x coordinate of the obstacle is set. "position" is a random value generator.
This code is not working because only one obstacle ever shows up. How can I fix this to work as intended? I can provide extra code if necessary.
Here is the ObstacleHole class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;
public class ObstacleHole {
Player player = new Player();
Random position = new Random();
int x;
int y;
int dx = 1;
int width = 100;
int height = 100;
public ObstacleHole(){
x = position.nextInt(500 + player.xElapsed * 2);
y = 250;
}
public void move(){
x = x - player.playerSpeed;
}
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
public Rectangle bounds(){
return (new Rectangle(x, y, width, height));
}
public int getX() {
return x;
}
}
Screen.java
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Screen extends JPanel implements ActionListener, KeyListener{
Player player = new Player();
ObstacleHole obstacleHole = new ObstacleHole();
public Screen(){
addKeyListener(this);
setDoubleBuffered(true);
setFocusable(true);
Timer tick = new Timer(5, this);
tick.start();
}
public void actionPerformed(ActionEvent e) {
repaint();
player.move();
obstacleHole.move();
System.out.println(player.getXElapsed());
}
public void paint(Graphics g){
super.paint(g);
player.paint(g);
obstacleHole.paint(g);
if(obstacleHole.getX() <= 0){
obstacleHole.paint(g);
}
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(player.jumpReady){
if(key == KeyEvent.VK_UP){
player.dy = -1;
player.jumpReady = false;
}
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
Player.java
import java.awt.Color;
import java.awt.Graphics;
public class Player {
int x;
int y;
int dx;
public int xElapsed = 0;
public int dy;
int width = 64;
int height = 64;
public int playerSpeed = 3;
public boolean isMoving = true;
public boolean hasJumped = false;
public boolean jumpReady = true;
public Player(){
x = 150;
y = 250;
}
public void move(){
x = x + dx;
y = y + dy;
xElapsed++;
if(hasJumped == true){
dy = -1;
}
if(y == 150){
dy = 1;
}
if(y == 250){
dy = 0;
jumpReady = true;
}
}
public void paint(Graphics g){
g.setColor(Color.RED);
g.fillRect(x, y, width, height);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public int getXElapsed(){
return xElapsed;
}
}
In your code you are painting obstacleHole, then when the x value of obstacleHole is less than or equal to 0, you draw it again. All you are doing is sending two calls to the paint() method of the same object.
If you want to paint a second one, you will need to create another object. Or, alternatively, move the original object back onto the screen after it leaves.
It is hard to give you sample code when you have provided so little context, but try something like this:
MyObject obstacleHoleA = new MyObject();
MyObject obstacleHoleB = new MyObject();
obstacleHoleA.paint(g);
if(obstacleHoleA.getX() <= 0){
obstacleHoleB.paint(g);
}
Or this:
obstacleHole.paint(g);
if(obstacleHole.getX() <= 0){
obstacleHole.setX(randomValueUpToAMaxOfCertainValue);
}
Edit: There are a lot of things I would do a lot differently with the above code, but they are outside the scope of the question.
Try this for your ObstacleHole class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;
public class ObstacleHole {
Player player = new Player();
Random position = new Random();
int x;
int y;
int dx = 1;
int width = 100;
int height = 100;
public ObstacleHole(){
x = getNewPosition();
y = 250;
}
public void move(){
x = x - player.playerSpeed;
if(x < 0 - width) {
x = getNewPosition();
}
}
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
public Rectangle bounds(){
return (new Rectangle(x, y, width, height));
}
public int getX() {
return x;
}
private int getNewPosition() {
return 200 + position.nextInt(300);
}
}
Note the change to the constructor and move() method, along with the new method getNewPosition().