I'm working on a game in Java using the Slick2D library. I've just started building a new mechanic which will involve a laser that I will implement the ability to redirect later. But for now I'm having trouble rendering a basic laser in the first place.
Below is the code for the class that handles the laser shooting object. The "updateLaser" method is used in the update loop of the basicGame extension, and the "render" method in the render loop.
package assets;
import java.util.ArrayList;
import levelTEST.BoxesTest;
import levelTEST.WallsTest;
import org.newdawn.slick.Color;
import org.newdawn.slick.ShapeFill;
import org.newdawn.slick.fills.GradientFill;
import org.newdawn.slick.geom.Point;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.ShapeRenderer;
public class LaserFire {
Polygon hitbox;
ArrayList<Point> laser = new ArrayList<Point>();
String directL;
float xL;
float yL;
boolean isOn;
WallsTest wT;
float startX;
float startY;
boolean laserNew;
float pointX;
float pointY;
BoxesTest bT;
// the wallstest and boxestest variables are just for colision, just ignore
// them
public LaserFire(float x, float y, WallsTest wt, BoxesTest bt, String direct) {
hitbox = new Polygon();
directL = direct;
xL = x;
yL = y;
// this part of the code creates the hitbox of the laser shooter and
// determines where the laser will start
if (direct.equals("top")) {
hitbox.addPoint(x, y + 50);
hitbox.addPoint(x + 50, y + 50);
hitbox.addPoint(x + 25, y);
startX = xL + 25;
startY = yL;
} else if (direct.equals("bottom")) {
hitbox.addPoint(x, y);
hitbox.addPoint(x + 50, y);
hitbox.addPoint(x + 25, y + 50);
startX = xL + 25;
startY = yL + 50;
} else if (direct.equals("left")) {
hitbox.addPoint(x + 50, y + 50);
hitbox.addPoint(x + 50, y);
hitbox.addPoint(x, y + 25);
startX = xL;
startY = yL + 25;
} else if (direct.equals("right")) {
hitbox.addPoint(x, y);
hitbox.addPoint(x, y + 50);
hitbox.addPoint(x + 50, y + 25);
startX = xL + 50;
startY = yL + 25;
}
isOn = true;
wT = wt;
laserNew = true;
pointX = 0;
pointY = 0;
bT = bt;
}
public void updateLaser(int delta) {
// first I wanted the laser to not be instant in how fast it went, so I
// limit the amount of time it takes before a new point in the laser can
// be added
int time = 0;
int timeToBlock = 2;
// second I made sure to start the laser in the correct spot and to have
// a point to start the laser (so there is a point to render at first)
if (laserNew == true) {
laser.add(new Point(startX, startY));
pointX = startX;
pointY = startY;
laserNew = false;
}
// if the laser is on...
if (isOn) {
// this is part of the time limiter
if (time > 0 && time < timeToBlock) {
time += delta;
} else if (time >= timeToBlock) {
time = 0;
}
// here's where each new point of the laser is added, it first
// checks if it would colide with the wall, if so, no new points are
// added
if (bT.allColide(new Point(pointX, pointY)) != true
&& wT.checkColi(new Point(pointX, pointY)) != true
&& time == 0) {
// then it adds a point if there is space for it
laser.add(new Point(pointX, pointY));
// then it checks what direction the laser is going, and plans
// where the next point will be
if (directL.equals("top")) {
pointY -= 1;
} else if (directL.equals("bottom")) {
pointY++;
} else if (directL.equals("left")) {
pointX -= 1;
} else if (directL.equals("right")) {
pointX++;
}
// this part sets of the timer
time += delta;
}
} else if (isOn != true) {
// this resets the laser if it is off
laser.clear();
laserNew = true;
}
}
public void render() {
// first I need the colors for the laser and the ShapeFill to fill it in
Color c = new Color(10, 210, 210);
Color c2 = new Color(10, 210, 210);
Color cL = new Color(255, 10, 10);
ShapeFill fillbox;
// if the laser is on it fills one color for the shooter, if not,
// another
if (isOn) {
fillbox = new GradientFill(0, 0, c, 960, 540, c);
} else {
fillbox = new GradientFill(0, 0, c2, 960, 540, c2);
}
// this renders the shooter
ShapeFill fillLaser = new GradientFill(0, 0, cL, 960, 540, cL);
ShapeRenderer.fill(hitbox, fillbox);
// if the laser is on, this renders each part of the laser beam
if (isOn) {
for (int i = 0; i < laser.size(); i++) {
ShapeRenderer.fill(laser.get(i), fillLaser);
}
}
}
// this part is for character colision, and works fine.
public boolean colision(Shape X) {
boolean check = false;
if (hitbox.intersects(X)) {
check = true;
} else if (hitbox.intersects(X) != true) {
check = false;
}
return check;
}
}
Every time I run the code in Eclipse I get a NullPointerException at the following line (at least according to Eclipse):
&& wT.checkColi(new Point(pointX, pointY)) != true
I've tested the class with the laser off, and it works just fine, but now with me testing the laser on I keep getting this exception.
I assume this has to do with how I'm adding on to the ArrayList "laser" within the if statement, but I don't really know. Just in case it has to do with the collision detection for the wallsTest object, here's the "checkColi" method from that object:
public boolean checkColi(Shape jupiter){
boolean colide = false;
if (top.intersects(jupiter)){
colide = true;
}if (bottom.intersects(jupiter)){
colide = true;
}if (left.intersects(jupiter)){
colide = true;
}if (right.intersects(jupiter)){
colide = true;
}if (midtop.intersects(jupiter)){
colide = true;
}if (midbottom.intersects(jupiter)){
colide = true;
}if (spire.intersects(jupiter)){
colide = true;
}
return colide;
}
Here is the initialization of the "WallsTest" object that is used in the laser class:
This is the wT being passed in the constructor
package levelTEST;
public class levelTest{
public WallsTest W;
public void render(){
W = new WallsTest();
W.render();
}
}
This class (levelTEST) is going to be used to manage all the other classes that take care of various objects, so far, though, it only takes care of the walls object.
This class itself is initialized in the following class Level (this class is going to manage all the levels, but again, hasn't gotten that far):
package assets;
import levelTEST.levelTest;
public class Level{
public levelTest lT = new levelTest();
public void main(){
lT.render();
}
}
And at the very end it is all initialized in the main game class like so:
Level lT = new Level();
Below is how the object wT gets passed on:
LaserTest laT = new LaserTest(lT.lT.W , bT);
lT.lT.W is being passed in the constructor, it is the "wallstest" object within the "leveltest" object within the "level" object.
This line is from the main BasicGame extended class that initializes the LaserTest class (a class that itself initializes the LaserFire class) the "lT.lT.W" leads to the WallsTest "W" that is used in the LaserFire object.
Below is the LaserTest class, it shows how wT is passed into the constructor for the "LaserFire" class:
package levelTEST;
import org.newdawn.slick.geom.Shape;
import assets.LaserFire;
public class LaserTest {
LaserFire[] lasers;
public LaserTest(WallsTest wT, BoxesTest bT) {
//this is how the wT variable is passed into the LaserFire object
LaserFire L1 = new LaserFire(300, 300, wT, bT, "top");
lasers = new LaserFire[] { L1 };
}
public void render() {
for (int i = 0; i < lasers.length; i++) {
lasers[i].render();
}
}
public void update(int delta) {
for (int i = 0; i < lasers.length; i++) {
lasers[i].updateLaser(delta);
}
}
public boolean colide(Shape X) {
boolean coli = false;
for (int i = 0; i < lasers.length; i++) {
if (lasers[i].colision(X)) {
coli = true;
}
}
return coli;
}
}