I am attempting to modify the pacman game from the Mason 19 website. What I have to do is make pacman run on its own and traverse any board given as fast as possible. So far I have achieved making pacman running on its own but it gets stuck in a loop at the top of the board. What I want to know is what would be the best way to make it work and not get stuck in a loop.
The program I am using code from comes from Mason19 https://cs.gmu.edu/~eclab/projects/mason/
The code I have modified is inside the Pac.java and the doPolicyStep class.
protected void doPolicyStep(SimState state)
{
if(lastAction == NOTHING)
{
if(isPossibleToDoAction(Pac.E))
{
pacman.actions[0] = Pac.E;
performAction(Pac.E);
}
else if(isPossibleToDoAction(Pac.S))
{
pacman.actions[0] = Pac.S;
performAction(Pac.S);
}
else if(isPossibleToDoAction(Pac.N))
{
pacman.actions[0] = Pac.N;
performAction(Pac.N);
}
else
{
pacman.actions[0] = Pac.W;
performAction(Pac.W);
}
}
else if(lastAction == Pac.E)
{
if(isPossibleToDoAction(Pac.E))
{
pacman.actions[0] = Pac.E;
performAction(Pac.E);
}
else if(isPossibleToDoAction(Pac.S))
{
pacman.actions[0] = Pac.S;
performAction(Pac.S);
}
else if(isPossibleToDoAction(Pac.N))
{
pacman.actions[0] = Pac.N;
performAction(Pac.N);
}
else
{
pacman.actions[0] = Pac.W;
performAction(Pac.W);
}
}
else if(lastAction == Pac.S)
{
if(isPossibleToDoAction(Pac.S))
{
pacman.actions[0] = Pac.S;
performAction(Pac.S);
}
else if(isPossibleToDoAction(Pac.W))
{
pacman.actions[0] = Pac.W;
performAction(Pac.W);
}
else if(isPossibleToDoAction(Pac.E))
{
pacman.actions[0] = Pac.E;
performAction(Pac.E);
}
else
{
pacman.actions[0] = Pac.N;
performAction(Pac.N);
}
}
else if(lastAction == Pac.W)
{
if(isPossibleToDoAction(Pac.W))
{
pacman.actions[0] = Pac.W;
performAction(Pac.W);
}
else if(isPossibleToDoAction(Pac.N))
{
pacman.actions[0] = Pac.N;
performAction(Pac.N);
}
else if(isPossibleToDoAction(Pac.S))
{
pacman.actions[0] = Pac.S;
performAction(Pac.S);
}
else
{
pacman.actions[0] = Pac.E;
performAction(Pac.E);
}
}
else if(lastAction == Pac.N)
{
if(isPossibleToDoAction(Pac.N))
{
pacman.actions[0] = Pac.N;
performAction(Pac.N);
}
else if(isPossibleToDoAction(Pac.W))
{
pacman.actions[0] = Pac.W;
performAction(Pac.W);
}
else if(isPossibleToDoAction(Pac.E))
{
pacman.actions[0] = Pac.E;
performAction(Pac.E);
}
else
{
pacman.actions[0] = Pac.S;
performAction(Pac.S);
}
}
int nextAction = pacman.getNextAction(tag);
/** The Method isPossibleToDoAction() Determines if the agent can move with the given action (N/W/S/E/NOTHING) without bumping into a wall. */
public boolean isPossibleToDoAction(int action)
{
if (action == NOTHING)
{
return false; // no way
}
IntGrid2D maze = pacman.maze;
int[][] field = maze.field;
// the Agents grid is discretized exactly on 1x1 boundaries so we can use floor rather than divide
// the agent can straddle two locations at a time. The basic location is x0, y0, and the straddled location is x1, y1.
// It may be that x0 == y0.
int x0 = (int) location.x;
int y0 = (int) location.y;
int x1 = location.x == x0 ? x0 : x0 + 1;
int y1 = location.y == y0 ? y0 : y0 + 1;
// for some actions we can only do the action if we're not straddling, or if our previous action was NOTHING
if ((x0 == x1 && y0 == y1) || lastAction == NOTHING)
{
switch (action)
{
// we allow toroidal actions
case N:
return (field[maze.stx(x0)][maze.sty(y0 - 1)] == 0);
case E:
return (field[maze.stx(x0 + 1)][maze.sty(y0)] == 0);
case S:
return (field[maze.stx(x0)][maze.sty(y0 + 1)] == 0);
case W:
return (field[maze.stx(x0 - 1)][maze.sty(y0)] == 0);
}
} // for other actions we're continuing to do what we did last time.
// assuming we're straddling, this should always be allowed unless our way is blocked
else if (action == lastAction)
{
switch (action)
{
// we allow toroidal actions
case N: // use y0
return (field[maze.stx(x0)][maze.sty(y0)] == 0);
case E: // use x1
return (field[maze.stx(x1)][maze.sty(y0)] == 0);
case S: // use y1
return (field[maze.stx(x0)][maze.sty(y1)] == 0);
case W: // use x0
return (field[maze.stx(x0)][maze.sty(y0)] == 0);
}
} // last there are reversal actions. Generally these are always allowed as well.
else if ((action == N && lastAction == S) ||
(action == S && lastAction == N) ||
(action == E && lastAction == W) ||
(action == W && lastAction == E))
{
return true;
}
return false;
}
I believe I need to use something like a BFS search to find the shortest path, I just have no idea how to do that.
Related
I have a bunch of points in a 3d environment that I want to solve the traveling salesman problem with, for that I first need to convert the game world to a graph.
The world is around 1km wide, 1.5 km long and 250m high = 375 000 000 possible location. Each location either has an obstacle or is walkable.
There are 150 location that I want to connect in this world. What would be the best approach to solve this?
Here is how the pathfinder looks right now, which is the bidirectional a* that also takes into account telporters
package me.mmaxi.pathfinder;
import me.mmaxi.utils.Location;
import me.mmaxi.utils.TeleportDirection;
import java.util.*;
public class PathFinder {
private final Map<Location, Block> blocks = new HashMap<>();
public List<Location> getPath(Location start, Location goal, IWalkHandler walkHandler, ITeleportHandler teleportHandler) {
// initializing start queue
PriorityQueue<PathFindStep> queueFromStart = new PriorityQueue<>();
Block startBlock = getBlock(start);
startBlock.distanceFromStart = 0;
startBlock.fScore = start.h(goal, teleportHandler, TeleportDirection.FORWARD);
queueFromStart.add(new PathFindStep(startBlock, startBlock.fScore));
// initializing goal queue
PriorityQueue<PathFindStep> queueFromGoal = new PriorityQueue<>();
Block goalBlock = getBlock(goal);
goalBlock.distanceFromGoal = 0;
goalBlock.fScore = goal.h(start, teleportHandler, TeleportDirection.BACKWARD);
queueFromGoal.add(new PathFindStep(goalBlock, goalBlock.fScore));
// here we will store the meeting point
Block meetingPoint = null;
// for debug purposes
int furthestAwayFromStartSoFar = 0;
int furthestAwayFromGoalSoFar = 0;
// working on the queues
while (!queueFromStart.isEmpty() || !queueFromGoal.isEmpty()) {
// coming from start
PathFindStep currentStepFromStart = queueFromStart.poll();
if (currentStepFromStart == null) {
throw new RuntimeException("We can no longer reach any blocks from the start. Maybe it's inside a wall?");
} else {
Block current = currentStepFromStart.toVisit;
if (Double.compare(currentStepFromStart.fScore, current.fScore) != 0) {
// we already found a better fScore in the meantime
continue;
}
if (current.location.equals(goal) || current.lastComingFromGoal != null) {
// we found the goal or meeting point
System.out.println("Breaking condition:\n" +
"* current.location.equals(goal):" + current.location.equals(goal) + "\n" +
"* current.lastComingFromGoal != null:" + (current.lastComingFromGoal != null));
meetingPoint = current;
break;
}
Set<Block> neighbours = getNeighbours(current);
Location teleportTarget = teleportHandler.getDestination(current.location);
if (teleportTarget != null) {
System.out.println("Found teleport target: " + teleportTarget);
neighbours.add(getBlock(teleportTarget));
}
for (Block neighbour : neighbours) {
if (!walkHandler.canWalk(neighbour.location) && !neighbour.location.equals(goal)) {
// we can't go there so ignoring this neighbour but only if it's not the goal
continue;
}
int pendingDistanceFromStart = current.distanceFromStart + 1;
if (pendingDistanceFromStart < neighbour.distanceFromStart) {
neighbour.lastComingFromStart = current;
neighbour.distanceFromStart = pendingDistanceFromStart;
neighbour.fScore = neighbour.location.h(goal, teleportHandler, TeleportDirection.FORWARD) + pendingDistanceFromStart;
queueFromStart.add(new PathFindStep(neighbour, neighbour.fScore));
if (pendingDistanceFromStart > furthestAwayFromStartSoFar) {
furthestAwayFromStartSoFar = pendingDistanceFromStart;
if (pendingDistanceFromStart % 25 == 0) {
System.out.println("Furthest from start: " + pendingDistanceFromStart);
}
}
}
}
}
// coming from goal
PathFindStep currentStepFromGoal = queueFromGoal.poll();
if (currentStepFromGoal == null) {
throw new RuntimeException("We can no longer reach any blocks from the goal. Maybe it's inside a wall?");
} else {
Block current = currentStepFromGoal.toVisit;
if (Double.compare(currentStepFromGoal.fScore, current.fScore) != 0) {
// we already found a better fScore in the meantime
continue;
}
if (current.location.equals(start) || current.lastComingFromStart != null) {
// we found the start or meeting point
System.out.println("Breaking condition:\n" +
"* current.location.equals(start):" + current.location.equals(start) + "\n" +
"* current.lastComingFromStart != null:" + (current.lastComingFromStart != null));
meetingPoint = current;
break;
}
Set<Block> neighbours = getNeighbours(current);
Location teleportStart = teleportHandler.getStart(current.location);
if (teleportStart != null) {
System.out.println("Found teleport start: " + teleportStart);
neighbours.add(getBlock(teleportStart));
}
for (Block neighbour : neighbours) {
if (!walkHandler.canWalk(neighbour.location) && !neighbour.location.equals(start)) {
// we can't go there so ignoring this neighbour but only if it's not the start
continue;
}
int pendingDistanceFromGoal = current.distanceFromGoal + 1;
if (pendingDistanceFromGoal < neighbour.distanceFromGoal) {
neighbour.lastComingFromGoal = current;
neighbour.distanceFromGoal = pendingDistanceFromGoal;
neighbour.fScore = neighbour.location.h(start, teleportHandler, TeleportDirection.BACKWARD) + pendingDistanceFromGoal;
queueFromGoal.add(new PathFindStep(neighbour, neighbour.fScore));
if (pendingDistanceFromGoal > furthestAwayFromGoalSoFar) {
furthestAwayFromGoalSoFar = pendingDistanceFromGoal;
if (pendingDistanceFromGoal % 25 == 0) {
System.out.println("Furthest from goal: " + pendingDistanceFromGoal);
}
}
}
}
}
}
if (meetingPoint == null) {
throw new RuntimeException("Could not find a meeting point.");
}
System.out.println("Found a meeting point! " + meetingPoint.location);
// reversing the one side coming from the goal, so we can then backtrack
System.out.println("Reversing side that started from goal.");
while (meetingPoint.lastComingFromGoal != null) {
meetingPoint.lastComingFromGoal.lastComingFromStart = meetingPoint;
meetingPoint = meetingPoint.lastComingFromGoal;
}
// now we can backtrack
System.out.println("Backtracking.");
Block tmp = goalBlock;
if (tmp.lastComingFromStart == null) {
throw new RuntimeException("Could not find the target block from that start block.");
}
System.out.println("Found goal. Starting to backtrack.");
List<Location> path = new ArrayList<>();
while (tmp != null) {
path.add(tmp.location);
tmp = tmp.lastComingFromStart;
}
Collections.reverse(path);
return path;
}
private Block getBlock(Location location) {
return blocks.computeIfAbsent(location, block -> new Block(location));
}
private Set<Block> getNeighbours(Block block) {
Set<Block> neighbours = new HashSet<>();
for (BlockFace blockFace : BlockFace.values()) {
Location targetLocation = block.location.clone().add(blockFace.vector);
if (targetLocation.getBlockY() > 255 || targetLocation.getBlockY() < 0) {
continue;
}
neighbours.add(getBlock(targetLocation));
}
return neighbours;
}
static class PathFindStep implements Comparable<PathFindStep> {
Block toVisit;
double fScore;
public PathFindStep(Block toVisit, double fScore) {
this.toVisit = toVisit;
this.fScore = fScore;
}
#Override
public int compareTo(PathFindStep o) {
return Double.compare(fScore, o.fScore);
}
}
static class Block {
Location location;
Block lastComingFromStart;
Block lastComingFromGoal;
double fScore;
int distanceFromStart = Integer.MAX_VALUE;
int distanceFromGoal = Integer.MAX_VALUE;
public Block(Location location) {
this.location = location;
}
}
enum BlockFace {
POS_Y(new Location(+0, +1, +0)),
NEG_Y(new Location(+0, -1, +0)),
POS_X(new Location(+1, +0, +0)),
NEG_X(new Location(-1, +0, +0)),
POS_Z(new Location(+0, +0, +1)),
NEG_Z(new Location(+0, +0, -1));
final Location vector;
BlockFace(Location vector) {
this.vector = vector;
}
}
}
public class Location implements Cloneable {
private double x, y, z;
public Location(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
public int getBlockX() {
return (int) x;
}
public int getBlockY() {
return (int) y;
}
public int getBlockZ() {
return (int) z;
}
public Location add(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
public Location add(Location other) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
return this;
}
public double distanceSquared(Location other) {
double dx = x - other.x;
double dy = y - other.y;
double dz = z - other.z;
return dx * dx + dy * dy + dz * dz;
}
public double mhd(Location other) {
double dx = Math.abs(x - other.x);
double dy = Math.abs(y - other.y);
double dz = Math.abs(z - other.z);
return Math.max(dx, Math.max(dy, dz));
}
public double h(Location other, ITeleportHandler teleportHandler, TeleportDirection teleportDirection) {
double h = mhd(other);
if (teleportHandler != null) {
Location[] closestTeleport = teleportHandler.getClosestTeleport(this, teleportDirection);
if (closestTeleport != null) {
if (teleportDirection == TeleportDirection.FORWARD) {
h = Math.min(h, mhd(closestTeleport[0]) + closestTeleport[1].mhd(other));
} else {
h = Math.min(h, mhd(closestTeleport[1]) + closestTeleport[0].mhd(other));
}
}
}
return h;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Location location = (Location) o;
return Double.compare(location.x, x) == 0 && Double.compare(location.y, y) == 0 && Double.compare(location.z, z) == 0;
}
#Override
public int hashCode() {
return Objects.hash(x, y, z);
}
#Override
public String toString() {
String xString = Double.compare(x, (int) x) == 0 ? Integer.toString((int) x) : Double.toString(x);
String yString = Double.compare(y, (int) y) == 0 ? Integer.toString((int) y) : Double.toString(y);
String zString = Double.compare(z, (int) z) == 0 ? Integer.toString((int) z) : Double.toString(z);
return "[" + xString + " " + yString + " " + zString + "]";
}
#Override
public Location clone() {
try {
return (Location) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
I'm writing a dice roller program for a project, and it prints an error when I try to access the Calculations object. Everything else works, from what I can tell (the variables are set correctly, everything prints in order.
Calculations c;
int stage = 0;
int number;
int die;
int mod;
int modnum;
int inc = 0;
int[] num = new int[20];
boolean plusminus;
int spot = 0;
void setup() {
startScreen(inc);
}
void draw() {
}
//initial prompt screen
void startScreen(int stage) {
switch(stage) {
case 0:
println("How many dice? (up to 9)");
break;
case 1:
number = num[spot];
//noLoop();
println("Modifiers? (press + or -)");
break;
case 2:
mod = num[spot];
if (mod == 10) {
plusminus = true;
}
if (mod == 11) {
plusminus = false;
}
//noLoop();
println("how much?");
//loop();
break;
case 3:
modnum = num[spot];
//noLoop();
println("which dice? (1 = d2, 2 = d3, 3 = d4, 4 = d6, 5 = d8, 6 = d10, 7 = d12, 8 = d20, 9= d100");
break;
case 4:
die = num[spot];
c.finalcalc();
break;
}
}
void keyPressed() {
if (keyPressed) {
if (keyCode == ENTER) {
inc++;
loop();
startScreen(inc);
noLoop();
}
if (keyCode != ENTER) {
if (key == '1') {
num[spot] = 1;
println(num[spot]);
} else if (key == '2') {
num[spot] = 2;
println(num[spot]);
} else if (key == '3') {
num[spot] = 3;
println(num[spot]);
} else if (key == '4') {
num[spot] = 4;
println(num[spot]);
} else if (key == '5') {
num[spot] = 5;
println(num[spot]);
} else if (key == '6') {
num[spot] = 6;
println(num[spot]);
} else if (key == '7') {
num[spot] = 7;
println(num[spot]);
} else if (key == '8') {
num[spot] = 8;
println(num[spot]);
} else if (key == '9') {
num[spot] = 9;
println(num[spot]);
} else if (key == '+') {
num[spot] = 10;
println(num[spot]);
} else if (key == '-') {
num[spot] = 11;
println(num[spot]);
}
}
}
}
class Calculations {
int dice() {
int i = 0;
if (die == 1) {
i = round(random(2));
}
if (die == 2) {
i = round(random(3));
}
if (die == 3) {
i = round(random(4));
}
if (die == 4) {
i = round(random(6));
}
if (die == 5) {
i = round(random(8));
}
if (die == 6) {
i = round(random(10));
}
if (die == 7) {
i = round(random(12));
}
if (die == 8) {
i = round(random(20));
}
if (die == 9) {
i = round(random(100));
}
return i;
}
int finalcalc() {
int[] roll = new int[key];
int result = 0;
for (int i = 0; i > roll.length; i++) {
roll[i] = dice();
if (plusminus == true) {
result = dice() + modnum;
}
if (plusminus == false) {
result = dice() - modnum;
}
}
return result;
}
}
I've made sure that my arrays are initialized properly, and I made sure that each of my variables are being referenced correctly, but this hasn't changed anything. I'm not sure what else I need to do.
Here is the error:
java.lang.NullPointerException: Cannot invoke "Final_Project$Calculations.finalcalc()" because "this.c" is null
at Final_Project.startScreen(Final_Project.java:68)
at Final_Project.keyPressed(Final_Project.java:80)
It's missing : Calculations c= new Calculations();
I am making my first 2d game and when i move up and collide with a tile it stops and i can still move left and right but it is difficult to move through a doorway.
the image below shows where the player gets stuck they cant move right or down they also can't move up but that is intended
Sorry if i left something important out this is my first time posting a question.
Edit
to try to clarify
my collision box is set a bit smaller than my player so that the player will overlap the wall tiles a bit so when attempting to walk through a doorway to the right if the top of the player collision Box collides with the bottom of the tiles collision box it does stop the player from moving up but if the player tries to keep moving right the player would be stopped
an image of where the player is getting stuck
CollisionHandler.java (Handles all collisions)
package game.handlers;
import game.GamePanel;
import game.entities.Entity;
import game.items.Item;
import game.items.objects.*;
import game.items.misc.*;
public class CollisionHandler
{
GamePanel gp;
public CollisionHandler(GamePanel gp)
{
this.gp = gp;
}
public void checkTile(Entity entity)
{
int entityLeftWorldX = entity.worldX + entity.collisionBox.x;
int entityRightWorldX = entity.worldX + entity.collisionBox.x + entity.collisionBox.width;
int entityTopWorldY = entity.worldY + entity.collisionBox.y;
int entityBottemWorldY = entity.worldY + entity.collisionBox.y + entity.collisionBox.height;
int entityLeftCol = entityLeftWorldX / gp.tileSize;
int entityRightCol = entityRightWorldX / gp.tileSize;
int entityTopRow = entityTopWorldY / gp.tileSize;
int entityBottemRow = entityBottemWorldY / gp.tileSize;
int tileNum1, tileNum2;
switch(entity.direction)
{
case "up":
entityTopRow = (entityTopWorldY - entity.speed) / gp.tileSize;
tileNum1 = gp.tm.mapTileNum[entityLeftCol] [entityTopRow];
tileNum2 = gp.tm.mapTileNum[entityRightCol] [entityBottemRow];
if(gp.tm.tile[tileNum1].hasCollision || gp.tm.tile[tileNum2].hasCollision)
{
entity.collisionOn = true;
}
break;
case "down":
entityBottemRow = (entityBottemWorldY + entity.speed) / gp.tileSize;
tileNum1 = gp.tm.mapTileNum[entityLeftCol] [entityTopRow];
tileNum2 = gp.tm.mapTileNum[entityRightCol] [entityBottemRow];
if(gp.tm.tile[tileNum1].hasCollision || gp.tm.tile[tileNum2].hasCollision)
{
entity.collisionOn = true;
}
break;
case "left":
entityLeftCol = (entityLeftWorldX - entity.speed) / gp.tileSize;
tileNum1 = gp.tm.mapTileNum[entityLeftCol] [entityTopRow];
tileNum2 = gp.tm.mapTileNum[entityRightCol] [entityBottemRow];
if(gp.tm.tile[tileNum1].hasCollision || gp.tm.tile[tileNum2].hasCollision)
{
entity.collisionOn = true;
}
break;
case "right":
entityRightCol = (entityRightWorldX + entity.speed) / gp.tileSize;
tileNum1 = gp.tm.mapTileNum[entityLeftCol] [entityTopRow];
tileNum2 = gp.tm.mapTileNum[entityRightCol] [entityBottemRow];
if(gp.tm.tile[tileNum1].hasCollision || gp.tm.tile[tileNum2].hasCollision)
{
entity.collisionOn = true;
}
break;
default:
break;
}
}
public int checkItem(Entity entity, boolean player)
{
int index = 999;
for(Item item: gp.items)
{
if(item != null)
{
entity.collisionBox.x = entity.worldX + entity.collisionBox.x;
entity.collisionBox.y = entity.worldY + entity.collisionBox.y;
item.collisionBox.x = item.worldX + item.collisionBox.x;
item.collisionBox.y = item.worldY + item.collisionBox.y;
switch(entity.direction)
{
case "up":
entity.collisionBox.y -= entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
break;
case "down":
entity.collisionBox.y += entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
break;
case "left":
entity.collisionBox.x -= entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
break;
case "right":
entity.collisionBox.x += entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
break;
}
/*switch(entity.direction)
{
case "up":
entity.collisionBox.y -= entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item instanceof SObject)
{
SObject object = (SObject)item;
object.interact(gp);
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
else
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
}
break;
case "down":
entity.collisionBox.y += entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item instanceof SObject)
{
SObject object = (SObject)item;
object.interact(gp);
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
else
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
}
break;
case "left":
entity.collisionBox.x -= entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item instanceof SObject)
{
SObject object = (SObject)item;
object.interact(gp);
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
else
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
}
break;
case "right":
entity.collisionBox.x += entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item instanceof SObject)
{
SObject object = (SObject)item;
object.interact(gp);
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
else
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
if(player)
{
index = gp.items.indexOf(item);
}
}
}
break;
}*/
entity.collisionBox.x = entity.defaultCollisionBoxX;
entity.collisionBox.y = entity.defaultCollisionBoxY;
item.collisionBox.x = item.defaultCollisionBoxX;
item.collisionBox.y = item.defaultCollisionBoxY;
}
}
return index;
}
public void interact(Entity entity, int index)
{
Item item = gp.items.get(index);
if(item != null)
{
entity.collisionBox.x = entity.worldX + entity.collisionBox.x;
entity.collisionBox.y = entity.worldY + entity.collisionBox.y;
item.collisionBox.x = item.worldX + item.collisionBox.x;
item.collisionBox.y = item.worldY + item.collisionBox.y;
switch(entity.direction)
{
case "up":
entity.collisionBox.y -= entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
}
break;
case "down":
entity.collisionBox.y += entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
}
break;
case "left":
entity.collisionBox.x -= entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
}
break;
case "right":
entity.collisionBox.x += entity.speed;
if(entity.collisionBox.intersects(item.collisionBox))
{
if(item.hasCollision)
{
entity.collisionOn = true;
}
}
break;
}
entity.collisionBox.x = entity.defaultCollisionBoxX;
entity.collisionBox.y = entity.defaultCollisionBoxY;
item.collisionBox.x = item.defaultCollisionBoxX;
item.collisionBox.y = item.defaultCollisionBoxY;
}
}
}
Entity.java (the class i extend all other entities from player, enemies, npcs, exc...)
package game.entities;
import game.util.Inventory;
import game.items.Item;
import java.awt.image.*;
import java.awt.Rectangle;
import java.util.ArrayList;
public abstract class Entity
{
public int health;
public int maxHealth;
public int worldX, worldY;
public int speed;
public BufferedImage up1, up2, down1, down2, left1, left2, right1, right2;
public String direction;
public int spriteCounter = 0;
public int spriteNumber = 1;
public Rectangle collisionBox;
public int defaultCollisionBoxX, defaultCollisionBoxY;
public boolean collisionOn = false;
public Inventory inv = new Inventory(this);
public Item equiped;
public ArrayList<String> diialogues = new ArrayList<String>();
public void heal(int amountToHeal)
{
health += amountToHeal;
if(health > maxHealth)
{
health = maxHealth;
}
}
public void damage(int amountToDamage)
{
health -= amountToDamage;
if(health < 0)
{
health = 0;
}
}
}
Player.java (The class containing the player of the game)
package game.entities;
import game.handlers.KeyHandler;
import game.items.Item;
import game.items.objects.SObject;
import game.GamePanel;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
public class Player extends Entity
{
GamePanel gp;
KeyHandler kh;
public final int screenX;
public final int screenY;
public Player(GamePanel gp, KeyHandler kh)
{
this.gp = gp;
this.kh = kh;
screenX = gp.screenWidth/2 - (gp.tileSize/2);
screenY = gp.screenHeight/2 - (gp.tileSize/2);
collisionBox = new Rectangle(8, 16, 32, 32);
defaultCollisionBoxX = collisionBox.x;
defaultCollisionBoxY = collisionBox.y;
worldX = gp.tileSize * 22;
worldY = gp.tileSize * 46; //46
speed = 4;
direction = "down";
health = 50;
maxHealth = 100;
getPlayerImage();
}
public void getPlayerImage()
{
try
{
up1 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_up_1.png"));
up2 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_up_2.png"));
down1 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_down_1.png"));
down2 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_down_2.png"));
left1 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_left_1.png"));
left2 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_left_2.png"));
right1 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_right_1.png"));
right2 = ImageIO.read(getClass().getResourceAsStream("/Resources/Entities/Player/boy_right_2.png"));
}
catch(IOException e)
{
e.printStackTrace();
}
}
public void interactObject(int index)
{
if(gp.kh.ePressed)
{
if(index != 999)
{
SObject object = (SObject)gp.items.get(index);
object.interact(gp);
}
}
}
public void pickUpItem(int index)
{
if(index != 999)
{
if(gp.items.get(index) instanceof SObject)
{
}
else
{
//inv.items.add(gp.items.get(index));
gp.items.set(index, null);
}
}
}
public void interactNPC(int index)
{}
public void update()
{
if(kh.downPressed || kh.upPressed || kh.leftPressed || kh.rightPressed)
{
if(kh.upPressed)
{
direction = "up";
}
if(kh.downPressed)
{
direction = "down";
}
if(kh.leftPressed)
{
direction = "left";
}
if(kh.rightPressed)
{
direction = "right";
}
collisionOn = false;
gp.ch.checkTile(this);
int itemIndex = gp.ch.checkItem(this, true);
pickUpItem(itemIndex);
if(!collisionOn)
{
switch(direction)
{
case "up":
worldY -= speed;
break;
case "down":
worldY += speed;
break;
case "left":
worldX -= speed;
break;
case "right":
worldX += speed;
break;
}
}
//int objectIndex = gp.ch.checkItem(this, true);
//interactObject(objectIndex);
gp.kh.ePressed = false;
spriteCounter++;
if(spriteCounter > 12)
{
if(spriteNumber == 1)
{
spriteNumber = 2;
}
else if(spriteNumber == 2)
{
spriteNumber = 1;
}
spriteCounter = 0;
}
}
}
public void draw(Graphics2D g2d)
{
BufferedImage image = null;
switch(direction)
{
case "up":
if(spriteNumber == 1)
{
image = up1;
}
else if(spriteNumber == 2)
{
image = up2;
}
break;
case "down":
if(spriteNumber == 1)
{
image = down1;
}
else if(spriteNumber == 2)
{
image = down2;
}
break;
case "left":
if(spriteNumber == 1)
{
image = left1;
}
else if(spriteNumber == 2)
{
image = left2;
}
break;
case "right":
if(spriteNumber == 1)
{
image = right1;
}
else if(spriteNumber == 2)
{
image = right2;
}
break;
default:
break;
}
int x = screenX;
int y = screenY;
if(screenX > worldX)
{
x = worldX;
}
if(screenY > worldY)
{
y = worldY;
}
int rightOffset = gp.screenWidth - gp.player.screenX;
if(rightOffset > gp.worldWidth - gp.player.worldX)
{
x = gp.screenWidth - (gp.worldWidth - worldX);
}
int bottemOffset = gp.screenHeight - gp.player.screenY;
if(bottemOffset > gp.worldHeight - gp.player.worldY)
{
y = gp.screenHeight -(gp.worldHeight - worldY);
}
g2d.drawImage(image, x, y, gp.tileSize, gp.tileSize, null);
}
}
KeyHandler.java (handles all my key imputs)
package game.handlers;
import game.GamePanel;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
public class KeyHandler implements KeyListener
{
GamePanel gp;
public boolean upPressed, leftPressed, downPressed, rightPressed;
public boolean ePressed, enterPressed;
private boolean u, u2, d, d2, l, r, l2, r2;
public KeyHandler(GamePanel gp)
{
this.gp = gp;
}
#Override
public void keyTyped(KeyEvent e)
{}
#Override
public void keyPressed(KeyEvent e)
{
int input = e.getKeyCode();
if(gp.gameState == gp.playState)
{
if(input == KeyEvent.VK_W)
{
upPressed = true;
}
if(input == KeyEvent.VK_A)
{
leftPressed = true;
}
if(input == KeyEvent.VK_S)
{
downPressed = true;
}
if(input == KeyEvent.VK_D)
{
rightPressed = true;
}
//interact key
if(input == KeyEvent.VK_E)
{
ePressed = true;
}
//Dev mode
if(input == KeyEvent.VK_UP)
{
if(!u)
{
u = true;
}
else if(u)
{
u2 = true;
}
if(u && u2)
{
if(!gp.devMode)
{
gp.devMode = true;
}
else if(gp.devMode)
{
gp.devMode = false;
}
}
}
if(gp.devMode)
{
if(input == KeyEvent.VK_T)
{
gp.player.heal(5);
}
if(input == KeyEvent.VK_Y)
{
gp.player.damage(5);
}
}
}
if(gp.gameState == gp.playState || gp.gameState == gp.pauseState)
{
//Toggle Pause Menu
if(input == KeyEvent.VK_P)
{
if(gp.gameState == gp.playState)
{
gp.gameState = gp.pauseState;
}
else if(gp.gameState == gp.pauseState)
{
gp.gameState = gp.playState;
}
}
}
if(gp.gameState == gp.textState)
{
if(input == KeyEvent.VK_ENTER)
{
enterPressed = true;
}
}
}
#Override
public void keyReleased(KeyEvent e)
{
int input = e.getKeyCode();
if(input == KeyEvent.VK_W)
{
upPressed = false;
}
if(input == KeyEvent.VK_A)
{
leftPressed = false;
}
if(input == KeyEvent.VK_S)
{
downPressed = false;
}
if(input == KeyEvent.VK_D)
{
rightPressed = false;
}
}
}
I would greatly appreciate any help that can be given along with any suggestions
Answering your question about refactoring: there are many places where your code can be made much cleaner and efficient. For example, consider this part from KeyHandler.java:
if(!u)
{
u = true;
}
else if(u)
{
u2 = true;
}
if(u && u2)
{
if(!gp.devMode)
{
gp.devMode = true;
}
else if(gp.devMode)
{
gp.devMode = false;
}
}
Take a look on the first if-statement. If u is false, you enter the first branch and set it to true. Otherwise u is true and you enter the second branch. In this second branch you have a test if(u). But you can enter this branch only if u is true! It means that you don't need to check it again and it can be simplified to
if (!u) {
u = true;
} else {
u2 = true;
}
Now, you can see that after execution of this if-statement the value of u will always be true (because it was either true from the start, or it was set to true in the first branch). Thus, it can be simplified even more:
if (u) {
u2 = true;
}
u = true;
After that the next if-statement follows. The condition is if (u && u2). However, as we have seen just now the value of u at this point is always true. So the condition can be simplified to just if (u2). What is inside the if-statement? The following code:
if(!gp.devMode)
{
gp.devMode = true;
}
else if(gp.devMode)
{
gp.devMode = false;
}
Here you again perform the same excessive test as before with the u variable: there is no need for the second if(gp.devMode) check, because in order to get in this branch gp.devMode must be already true. The whole meaning of this if-statement is to toggle the value of gp.devMode: if it is true then set it to false and vice versa. You can achieve this with a simple assignment:
gp.devMode = !gp.devMode;
So, the second if-statement can be reduced to just
if (u2) {
gp.devMode = !gp.devMode;
}
And the whole block of code from which we started is reduced to
if (u) {
u2 = true;
}
u = true;
if (u2) {
gp.devMode = !gp.devMode;
}
This is just a simplification of the code, while maintaining the same logic. In many places the logic can be simplified or changed as well.
Also, the code right now is very prone to errors appearing from simple typos. For example, to specify the direction of the motion you use strings. What happens if in one of the places you accidentally type "rigth" instead of "right"? The program will compile and run, but it will not work correctly and it can be very difficult to spot the error. Much better solution in such cases is to use enumerations. So, you create a new file Direction.java:
public enum Direction {
UP,
DOWN,
LEFT,
RIGHT
}
Then in your Entity class you replace the declaration
public String direction;
with
public Direction direction;
When you need to use it you replace a string with the corresponding enumeration value, for example:
switch(direction) {
case Direction.UP:
worldY -= speed;
break;
case Direction.DOWN:
worldY += speed;
break;
case Direction.LEFT:
worldX -= speed;
break;
case Direction.RIGHT:
worldX += speed;
break;
}
This is more efficient and less error-prone. If you make a typo and write Direction.RIGTH, then the code will not compile. The compiler can also warn you now if you forget to specify all the cases in your switch statement, etc.
There are many other places where improvements can be done (e.g. you consistently break encapsulation), but let's return to your main question about collisions. It's hard to tell from the information you provided, but I think the collision box for your player looks something like the red rectangle on the following picture:
Now, consider the player is a bit to the left and up of the door, and the player wants to move to the right:
In your code for collision detection you have the following:
case "right":
entityRightCol = (entityRightWorldX + entity.speed) / gp.tileSize;
tileNum1 = gp.tm.mapTileNum[entityLeftCol] [entityTopRow];
tileNum2 = gp.tm.mapTileNum[entityRightCol] [entityBottemRow];
if(gp.tm.tile[tileNum1].hasCollision || gp.tm.tile[tileNum2].hasCollision)
{
entity.collisionOn = true;
}
break;
It says, that you calculate the position which is a bit to the right of the player's right side, and then check the left-top and the right-bottom tiles, i.e. tiles shown by green color on the previous picture. Do you see the problem now? You should check the right-top and the right-bottom tiles. However, because of your mistake the collision of the right-top corner with the wall is not detected and the player is allowed to move further, inside the wall.
Now you get this situation:
Let's say you press a down key now. The check is the following:
case "down":
entityBottemRow = (entityBottemWorldY + entity.speed) / gp.tileSize;
tileNum1 = gp.tm.mapTileNum[entityLeftCol] [entityTopRow];
tileNum2 = gp.tm.mapTileNum[entityRightCol] [entityBottemRow];
if(gp.tm.tile[tileNum1].hasCollision || gp.tm.tile[tileNum2].hasCollision)
{
entity.collisionOn = true;
}
break;
You again check the left-top and the right-bottom corners. The right-bottom corner is free, but the left-top is stuck in the wall and you can't move. The same is true for all other directions except for movement to the left.
In order to fix this you need to check in each case the two tiles which are lying in the direction of the desired movement:
case UP: check left-top and right-top;
case DOWN: check left-bottom and right-bottom;
case LEFT: check left-top and left-bottom;
case RIGHT: check right-top and right-bottom;
I am making a little game for my university coursework and got stuck at the menu creation. Been trying to solve this for a while. Here goes the code:
Before trying to fiddle around the loop and noLoop() functions and before I moved the arrow placement from void setup to void keypressed my game has worked. Now the program freezes before running draw.
What I want is to only display the menu background before the key, in this case s for start, has been pressed. If anyone could come up with some directions it would be of a great help.
Thanks in advance.
int totalArrows = 6;
arrowclass[] theArrows = new arrowclass[totalArrows];
PImage[] imgList = new PImage[4];
PImage bg;
PImage life;
PImage menu;
int score;
int loose = 3;
void setup() {
size(400, 600);
bg = loadImage("bck.jpg");
life = loadImage("life.png");
menu = loadImage("menu.jpg");
background(menu);
// loading the arrow imgs
for (int i= 0; i<4; i++) {
println(i+".png");
imgList[i] = loadImage(i+".png");
}
noLoop();
}
void draw() {
textSize(32);
text(score,30,100);
// active area
fill(255,31,31);
rect(0,500,width,100);
//lifetrack
if (loose==3){
image(life,10,10);
image(life,45,10);
image(life,80,10);
}
if (loose==2){
image(life,10,10);
image(life,45,10);
}
if (loose==1){
image(life,10,10);
}
// calling class action, movement
for (int i = 0; i<totalArrows; i++) {
theArrows[i].run();
if (theArrows[i].y>475 && theArrows[i].y<600) {
theArrows[i].inactive = true;
}
if(theArrows[i].did == false && theArrows[i].y>598) {
theArrows[i].lost = true;
}
if(theArrows[i].lost && theArrows[i].did == false && theArrows[i].wrongclick == false){
loose--;
theArrows[i].lost = false;
}
}
if (loose == 0){
textSize(32);
text("gameover",width/2,height/2);
for(int i=0; i<totalArrows;i++){
}
}
}
void keyPressed() {
if (key == 'p'){
// placing tha arrows. (i*105 = positioning) THIS PART IS AN ISSUE FOR SURE. SEE ARROW CLASS
for (int i= 0; i<totalArrows; i++) {
theArrows[i] = new arrowclass(-i*105);
}
loop();
}
}
void keyReleased() {
for (int i= 0; i<totalArrows; i++) {
if (theArrows[i].inactive && theArrows[i].did == false) {
if (keyCode == UP && theArrows[i].id == 3){
score++;
theArrows[i].did = true;
}
if (keyCode != UP && theArrows[i].id == 3){
loose--;
theArrows[i].wrongclick = true;
}
if (keyCode == DOWN && theArrows[i].id == 0){
score++;
theArrows[i].did = true;
}
if (keyCode != DOWN && theArrows[i].id == 0){
loose--;
theArrows[i].wrongclick = true;
}
if (keyCode == RIGHT && theArrows[i].id == 2){
score++;
theArrows[i].did = true;
}
if (keyCode != RIGHT && theArrows[i].id == 2){
loose--;
theArrows[i].wrongclick = true;
}
if (keyCode == LEFT && theArrows[i].id == 1){
score++;
theArrows[i].did = true;
}
if (keyCode != LEFT && theArrows[i].id == 1){
loose--;
theArrows[i].wrongclick = true;
}
}
}
}
And the arrow class:
class arrowclass{
int y;
int id;
boolean did;
boolean inactive;
boolean lost;
boolean wrongclick;
// proprieties of the class
arrowclass(int initY){
y = initY;
id = int(random(4));
did = false;
inactive = false;
lost = false;
wrongclick = false;
}
//actions
void run(){
image(imgList[id],width/2,y);
// score goes up, speed goes up
y += 2+score;
// reset arrow to default
if (y>600) {
y = -50;
id = int(random(4));
inactive = false;
did = false;
lost = false;
wrongclick = false;
}
}
}
Im doing a pacman game. The following code is for ghosts movement and it works correctly. But I have to include a another check. The problem is that I always ruin the logic.
Current code:
public void moveGhost(Tiles target) {
if(specialIntersections()){
direction = direction; //keeps going in the same direction
}
else{
int oppDir;
if(direction == UP){
oppDir = DOWN;
}
else if(direction == DOWN){
oppDir = UP;
}
else if(direction == LEFT){
oppDir = RIGHT;
}
else{
oppDir=LEFT;
}
double minDist = 10000.0;
Tiles potentialNext;
for(int i=0; i<4; i++){
if(i!=oppDir){
potentialNext = maze.nextTile(getCurrentPos(), i);
if(!(potentialNext.wall()) && check(potentialNext)){
if(calculateDistance(target, potentialNext) < minDist){
minDist = calculateDistance(target, potentialNext);
futureDirection = i;
}
}
}
}
}
changeDirection();
timer++;
increment();
x += xinc;
y += yinc;
tunnel();
}
Another check I need to include:
//if the door is a wall (closed) the object cannot go through it
if(DoorIsWall()){
if(!(potentialNext.wall()) && !(potentialNext.door()) && check(potentialNext)){
I generally write a new method when my conditions start to get unruly:
if (isTileValid(potentialNext)) {
// do stuff
}
private boolean isTileValid(TileObject someTile) {
if (someTile.wall()) {
return false;
}
if (someTile.door()) {
return false;
}
if (! check(someTile)) {
return false;
}
return true;
}