Firstly I'm not sure if this is the right place to post this question, so if I am wrong, please, move it. Thanks.
I had an assignment to compare same algorithm performance in Java and C#. The algorithm is supposed to be A* search, but I think I made it more like flooding, but it works well and I'm not here to fix it. Firstly I'll post the code I was using in Java and C# and then explain what I got.
As body of question is limited to 30000 characters and I entered more, I had to delete functions readFile() from Java and C# to make it fit.
UPDATED
After Jim Mischel has pointed out I updated hash function in C# version to be same as in Java which resulted in better performance.
Also thanks to Matt Timmermans I realized that all this time I was running C# in debug (Result of not thinking it through) and changing to release increased performance even more.
C# version:
File: Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
namespace A_Star_Compare
{
class Program
{
static int StartX = 0;
static int StartY = 0;
static int TargetX = 0;
static int TargetY = 0;
static int Width = 0;
static int Height = 0;
static TimeSpan TotalTime = TimeSpan.Zero;
static double[] TrialTimes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static Dictionary<int, List<int>> Obstacles = new Dictionary<int, List<int>>();
static void Main(string[] args)
{
for (int z = 0; z < 10; z++)
{
int Index = 0;
Console.WriteLine("z: " + z);
for (int x = 0; x < 14; x++)
{
if (x < 10)
Index += 10;
else
Index += 100;
string Line = string.Empty;
string FileName = "Maps-" + Index + ".txt";
TotalTime = TimeSpan.Zero;
readFile(FileName);
TrialTimes[x] += (double)TotalTime.TotalSeconds / 100;
}
}
int Index0 = 0;
for (int i = 0; i < 14; i++)
{
if (i < 10)
Index0 += 10;
else
Index0 += 100;
string FileName = "Maps-" + Index0 + ".txt";
Console.WriteLine("{0} Map size: {1}*{2}. On average map solved in: {3}", FileName, Index0, Index0, (double)TrialTimes[i] / 10);
}
}
static void measureTime()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Algorithm Solve = new Algorithm(StartX, StartY, TargetX, TargetY, Width, Height, Obstacles);
Solve.FullSolve();
stopwatch.Stop();
TotalTime += stopwatch.Elapsed;
}
}
}
File: Algorithm.cs
using System.Collections.Generic;
namespace A_Star_Compare
{
public class Node
{
public int X { get; set; }
public int Y { get; set; }
public int G { get; set; }
public int F { get; set; }
public int H { get; set; }
public Node PointsTo { get; set; }
public Node(int x, int y, int g, int f, int h, Node point)
{
this.X = x;
this.Y = y;
this.G = g;
this.F = f;
this.H = h;
this.PointsTo = point;
}
public override bool Equals(object obj)
{
Node rhs = obj as Node;
return rhs.X == this.X && rhs.Y == this.Y;
}
public override int GetHashCode()
{
int hash = 7;
hash = 83 * hash + this.X;
hash = 83 * hash + this.Y;
return hash;
}
}
class Algorithm
{
private Dictionary<int, List<int>> Obstacles { get; set; }
public HashSet<Node> OpenList { get; set; }
public HashSet<Node> ClosedList { get; set; }
private Node Parent { get; set; }
private Node LowestCost { get; set; }
private int StartX { get; set; }
private int StartY { get; set; }
private int TargetX { get; set; }
private int TargetY { get; set; }
private int Width { get; set; }
private int Height { get; set; }
private bool FirstIter = true;
public Algorithm(int stX, int stY, int tgX, int tgY, int wid, int hei, Dictionary<int, List<int>> obs)
{
this.StartX = stX;
this.StartY = stY;
this.TargetX = tgX;
this.TargetY = tgY;
this.Width = wid - 1;
this.Height = hei - 1;
this.Obstacles = new Dictionary<int, List<int>>(obs);
this.Parent = new Node(StartX, StartY, 0, 0, 0, null);
this.LowestCost = new Node(int.MaxValue, int.MaxValue, 0, int.MaxValue, 0, null);
this.ClosedList = new HashSet<Node>();
this.OpenList = new HashSet<Node>();
}
private bool IsBlockObstacle(int X, int Y)
{
if (Obstacles.ContainsKey(X) == false || (Obstacles.ContainsKey(X) == true && Obstacles[X].Contains(Y) == false))
return false;
return true;
}
private void Calculate(ref int H, int G, ref int F, int MovedX, int MovedY, Node AddToList)
{
int H1 = 0;
H = (TargetX - MovedX) * 10;
if (H < 0)
H *= -1;
H1 = (TargetY - MovedY) * 10;
if (H1 < 0)
H1 *= -1;
H += H1;
F = G + H;
AddToList.F = F;
AddToList.H = H;
AddToList.PointsTo = Parent;
}
private Node GetNodeFromOpen(Node Find)
{
Node Ret = null;
foreach (Node Nfo in OpenList)
{
if (Nfo.Equals(Find))
{
Ret = Nfo;
break;
}
}
return Ret;
}
private bool CheckNode(Node AddToList, int G)
{
if (!OpenList.Contains(AddToList))
{
OpenList.Add(AddToList);
return true;
}
else
{
Node Check = GetNodeFromOpen(AddToList);
if (Parent.G + G < Check.G)
{
int Offset = Check.G - Parent.G - G;
Check.G -= Offset;
Check.F -= Offset;
Check.PointsTo = Parent;
}
}
return false;
}
private void ChooseNode()
{
foreach (Node Nfo in OpenList)
{
if (Nfo.X == TargetX && Nfo.Y == TargetY)
{
LowestCost = Nfo;
break;
}
if (Nfo.F < LowestCost.F)
LowestCost = Nfo;
}
}
private void CountCost()
{
int[] Directions = { 1, -1 };
int[] Diagnoly = { 1, 1, -1, 1, 1, -1, -1, -1 };
int ParentX = Parent.X;
int ParentY = Parent.Y;
int MovedX = 0;
int MovedY = 0;
int H = 0;
int F = 0;
Node AddToList = null;
//Left and right
for (int i = 0; i < 2; i++)
{
//Check if it is possible to move right or left
if (ParentX + Directions[i] <= Width && ParentX + Directions[i] >= 0)
{
//Check if blocks to the right and left of parent aren't obstacles
if (!IsBlockObstacle(ParentX + Directions[i], ParentY))
{
AddToList = new Node(ParentX + Directions[i], ParentY, Parent.G + 10, 0, 0, null);
//Check if it is not on closed list
if (!ClosedList.Contains(AddToList))
{
MovedX = AddToList.X;
MovedY = AddToList.Y;
Calculate(ref H, AddToList.G, ref F, MovedX, MovedY, AddToList);
CheckNode(AddToList, 10);
}
}
}
}
//Up and down
for (int i = 0; i < 2; i++)
{
//Check if possible to move up or down
if (ParentY + Directions[i] <= Height && ParentY + Directions[i] >= 0)
{
//Check if higher and lower block of parent aren't obstacles
if (!IsBlockObstacle(ParentX, ParentY + Directions[i]))
{
AddToList = new Node(ParentX, ParentY + Directions[i], Parent.G + 10, 0, 0, null);
if (!ClosedList.Contains(AddToList))
{
MovedX = ParentX;
MovedY = ParentY + Directions[i];
Calculate(ref H, AddToList.G, ref F, MovedX, MovedY, AddToList);
CheckNode(AddToList, 10);
}
}
}
}
//Diagnoly
for (int i = 0; i < 8; i += 2)
{
if (ParentX + Diagnoly[i] <= Width && ParentX + Diagnoly[i] >= 0 && ParentY + Diagnoly[i + 1] <= Height && ParentY + Diagnoly[i + 1] >= 0)
{
if (!IsBlockObstacle(ParentX + Diagnoly[i], ParentY + Diagnoly[i + 1]))
{
AddToList = new Node(ParentX + Diagnoly[i], ParentY + Diagnoly[i + 1], Parent.G + 14, 0, 0, null);
if (!ClosedList.Contains(AddToList))
{
MovedX = ParentX + Diagnoly[i];
MovedY = ParentY + Diagnoly[i + 1];
Calculate(ref H, AddToList.G, ref F, MovedX, MovedY, AddToList);
CheckNode(AddToList, 14);
}
}
}
}
}
public void FullSolve()
{
Node Final = null;
if (FirstIter)
{
CountCost();
ChooseNode();
OpenList.Remove(Parent);
ClosedList.Add(Parent);
Parent = LowestCost;
OpenList.Remove(Parent);
ClosedList.Add(Parent);
FirstIter = false;
FullSolve();
}
else
{
while (true)
{
if (OpenList.Count == 0)
break;
CountCost();
HashSet<Node> Copy = new HashSet<Node>(OpenList);
foreach (Node Nfo in Copy)
{
Parent = Nfo;
CountCost();
ClosedList.Add(Parent);
OpenList.Remove(Parent);
if (Parent.X == TargetX && Parent.Y == TargetY)
{
Final = Parent;
break;
}
}
ChooseNode();
OpenList.Remove(Parent);
ClosedList.Add(Parent);
Parent = LowestCost;
LowestCost.F = int.MaxValue;
if (Parent.X == TargetX && Parent.Y == TargetY)
{
Final = Parent;
break;
}
}
}
}
}
}
Java version:
File: AStar_Compare.java
package a.star_compare;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang3.time.StopWatch;
public class AStar_Compare {
static double totalTime;
static double[] trialTimes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int startX;
static int startY;
static int targetX;
static int targetY;
static int width;
static int heigth;
static HashMap<Integer, List<Integer>> obstacles = new HashMap<>();
static NumberFormat formatter = new DecimalFormat("#0.000000000");
public static void main(String[] args) throws FileNotFoundException, IOException {
for (int z = 0; z < 10; z++) {
int Index = 0;
System.out.println("z: " + z);
for (int x = 0; x < 5; x++) {
if (x < 10) {
Index += 10;
} else {
Index += 100;
}
String fileName = "Maps-" + Index + ".txt";
totalTime = 0;
readFile(fileName);
trialTimes[x] += totalTime / 1E9 / 100;
}
}
int index0 = 0;
for (int i = 0; i < 14; i++) {
if (i < 10) {
index0 += 10;
} else {
index0 += 100;
}
trialTimes[i] /= 10;
String fileName = "Maps-" + index0 + ".txt";
System.out.println(fileName + " Map size: " + index0 + "*" + index0 + ". On average map solved in: " + formatter.format(trialTimes[i]));
}
}
static void measureTime() {
StopWatch time = new StopWatch();
time.start();
Algorithm solve = new Algorithm(obstacles, startX, startY, targetX, targetY, width, heigth);
solve.FullSolve();
time.stop();
totalTime += time.getNanoTime();
}
}
File: Node.java
package a.star_compare;
public class Node {
public int x;
public int y;
public int g;
public int h;
public int f;
public Node pointsTo;
public Node(int gx, int gy, int gg, int gh, int gf, Node point){
this.x = gx;
this.y = gy;
this.g = gg;
this.h = gh;
this.f = gf;
this.pointsTo = point;
}
#Override
public boolean equals(Object other){
if(other == null) return false;
if(other == this) return true;
if(!(other instanceof Node)) return false;
Node rhs = (Node)other;
return this.x == rhs.x && this.y == rhs.y;
}
#Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + this.x;
hash = 83 * hash + this.y;
return hash;
}
}
File: Algorithm.java
package a.star_compare;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class Algorithm {
private final HashMap<Integer, List<Integer>> obstacles;
private final HashSet<Node> closedList;
private final HashSet<Node> openList;
private Node parent;
private Node lowestCost;
private final int startX;
private final int startY;
private final int targetX;
private final int targetY;
private final int width;
private final int height;
private boolean firstIter = true;
public Algorithm(HashMap<Integer, List<Integer>> obs, int stX, int stY, int tgX, int tgY, int wid, int hei) {
this.obstacles = new HashMap(obs);
this.startX = stX;
this.startY = stY;
this.targetX = tgX;
this.targetY = tgY;
this.width = wid - 1;
this.height = hei - 1;
this.parent = new Node(startX, startY, 0, 0, 0, null);
this.lowestCost = new Node(Integer.MAX_VALUE, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 0, null);
this.closedList = new HashSet<>();
this.openList = new HashSet<>();
}
private boolean isBlockObstacle(Integer x, Integer y) {
if (obstacles.containsKey(x) == false || (obstacles.containsKey(x) == true && obstacles.get(x).contains(y) == false)) {
return false;
}
return true;
}
private void calculate(int h, int g, int f, int movedX, int movedY, Node addToList) {
int h1 = 0;
h = (targetX - movedX) * 10;
if (h < 0) {
h *= -1;
}
h1 = (targetY - movedY) * 10;
if (h1 < 0) {
h1 *= -1;
}
h += h1;
f = g + h;
addToList.f = f;
addToList.h = h;
addToList.pointsTo = parent;
}
private Node getNodeFromOpen(Node find) {
Node ret = null;
for (Node nfo : openList) {
if (nfo.equals(find)) {
ret = nfo;
break;
}
}
return ret;
}
private boolean checkNode(Node addToList, int g) {
if (!openList.contains(addToList)) {
openList.add(addToList);
return true;
} else {
Node check = getNodeFromOpen(addToList);
if (parent.g + g < check.g) {
int offset = check.g - parent.g - g;
check.g -= offset;
check.f -= offset;
check.pointsTo = parent;
}
}
return false;
}
private void chooseNode() {
for (Node nfo : openList) {
if (nfo.x == targetX && nfo.y == targetY) {
lowestCost = nfo;
break;
}
if (nfo.f < lowestCost.f) {
lowestCost = nfo;
}
}
}
private void countCost() {
int[] directions = {1, -1};
int[] diagnoly = {1, 1, -1, 1, 1, -1, -1, -1};
int parentX = parent.x;
int parentY = parent.y;
int movedX = 0;
int movedY = 0;
int h = 0;
int f = 0;
Node addToList = null;
//Left and right
for (int i = 0; i < 2; i++) {
//Check if it is possible to move right or left
if (parentX + directions[i] <= width && parentX + directions[i] >= 0) {
//Check if blocks to the right and left of parent aren't obstacles
if (!isBlockObstacle(parentX + directions[i], parentY)) {
addToList = new Node(parentX + directions[i], parentY, parent.g + 10, 0, 0, null);
//Check if it is not on closed list
if (!closedList.contains(addToList)) {
movedX = addToList.x;
movedY = addToList.y;
calculate(h, addToList.g, f, movedX, movedY, addToList);
checkNode(addToList, 10);
}
}
}
}
//Up and down
for (int i = 0; i < 2; i++) {
//Check if possible to move up or down
if (parentY + directions[i] <= height && parentY + directions[i] >= 0) {
//Check if higher and lower block of parent aren't obstacles
if (!isBlockObstacle(parentX, parentY + directions[i])) {
addToList = new Node(parentX, parentY + directions[i], parent.g + 10, 0, 0, null);
if (!closedList.contains(addToList)) {
movedX = parentX;
movedY = parentY + directions[i];
calculate(h, addToList.g, f, movedX, movedY, addToList);
checkNode(addToList, 10);
}
}
}
}
//diagnoly
for (int i = 0; i < 8; i += 2) {
if (parentX + diagnoly[i] <= width && parentX + diagnoly[i] >= 0 && parentY + diagnoly[i + 1] <= height && parentY + diagnoly[i + 1] >= 0) {
if (!isBlockObstacle(parentX + diagnoly[i], parentY + diagnoly[i + 1])) {
addToList = new Node(parentX + diagnoly[i], parentY + diagnoly[i + 1], parent.g + 14, 0, 0, null);
if (!closedList.contains(addToList)) {
movedX = parentX + diagnoly[i];
movedY = parentY + diagnoly[i + 1];
calculate(h, addToList.g, f, movedX, movedY, addToList);
checkNode(addToList, 14);
}
}
}
}
}
public void FullSolve() {
Node finalPath = null;
if (firstIter) {
countCost();
chooseNode();
openList.remove(parent);
closedList.add(parent);
parent = lowestCost;
openList.remove(parent);
closedList.add(parent);
firstIter = false;
FullSolve();
} else {
while (true) {
if (openList.isEmpty()) {
break;
}
countCost();
HashSet<Node> copy = new HashSet<>(openList);
for (Node nfo : copy) {
parent = nfo;
countCost();
closedList.add(parent);
openList.remove(parent);
if (parent.x == targetX && parent.y == targetY) {
finalPath = parent;
break;
}
}
chooseNode();
openList.remove(parent);
closedList.add(parent);
parent = lowestCost;
lowestCost.f = Integer.MAX_VALUE;
if (parent.x == targetX && parent.y == targetY) {
finalPath = parent;
break;
}
}
}
}
}
The testing was done with pregenerated map files. I have 14 map files each of them contains a 100 maps with specific size. With lowest one being map by 10 * 10 and highest being by 500 * 500.
Also note that if each map has 100 examples it means that algorithm was tested 100 times to work with one specific size, furthermore I wanted to increase accuracy even more so I repeat whole process 10 times. Which gives me 1000 test with one map. I of course average those times.
I'm not really familiar with high accuracy time measuring methods so I used StopWatch() in both Java and C# (To use it in Java I downloaded it from apache commons). What I did was after reading one map information I called function measureTime() and started StopWatch() then call Algorithm class and make it solve puzzle after that I'd stop StopWatch() and take time.
Here are the results I got:
I'm posting image because I'm not sure how to make a table here. Times are in second, how much it took to solve one map in average.
Note after "-" symbol there is map size. (Maps-20.txt means map by 20 * 20 and so on)
Also a graph:
These results really surprised me, I was expecting one language having a bit of an advantage, but not like this. After update C# graph looks similar to Java graph, but has steeper growth rate. First I thought that I made some mistake while copying algorithm to Java (Firstly I wrote in C#), but I couldn't find any. So assuming that I didn't make some silly mistake.
How can I improve C# performance even more?
Also one thing I thought about getting these results that in Dictionary<int, List<int>> instead of using List<int> I could use HashSet<int> since I only need to confirm if element exists or not. But as I am not dealing with thousands of elements I don't think that it could be major factor.
Thanks.
I have a list of point objects that need to be sorted by both X and Y coordinates, but when I pass them to a comparator object only one coordinate gets sorted (the first one called). Any ideas to why this might be happening?
static public List<Point> convertToThreeByThreeGrid(String points) {
String[] ptsArray;
List<Point> ptsList = new ArrayList<>();
String stripString = points.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(" ", ",").trim();
ptsArray = stripString.split(",");
for(int i = 0; i < ptsArray.length; i += 2) {
int x = Integer.parseInt(ptsArray[i]);
int y = Integer.parseInt(ptsArray[i + 1]);
System.out.println("X: " + x);
System.out.println("Y: " + y);
ptsList.add(new Point(x, y));
}
Collections.sort(ptsList, new Comparator<Point>() {
public int compare(Point a, Point b) {
int result = Integer.compare((int) a.getX(), (int) b.getX());
if (result == 0 ) {
result = Integer.compare((int) a.getY(), (int) b.getY());
}
return result;
}
});
// subtract each coordinate by smallest x and y coordinate values
List<Point> convertedPtList = new ArrayList<>();
int smallestX = (int) ptsList.get(0).getX();
int smallestY = (int) ptsList.get(0).getY();
for (int i = 1; i < ptsList.size(); i++) {
int x = ((int) ptsList.get(i).getX() - smallestX);
int y = ((int) ptsList.get(i).getY() - smallestY);
convertedPtList.add(new Point(x, y));
}
return convertedPtList;
}
}
Output:
[java.awt.Point[x=10,y=26], java.awt.Point[x=10,y=26], java.awt.Point[x=10,y=28], java.awt.Point[x=12,y=26]]
[java.awt.Point[x=13,y=26], java.awt.Point[x=13,y=28], java.awt.Point[x=13,y=28], java.awt.Point[x=14,y=27], java.awt.Point[x=14,y=27], java.awt.Point[x=15,y=26], java.awt.Point[x=15,y=28], java.awt.Point[x=15,y=28]]
[java.awt.Point[x=16,y=26], java.awt.Point[x=16,y=28], java.awt.Point[x=16,y=28], java.awt.Point[x=18,y=26], java.awt.Point[x=18,y=26], java.awt.Point[x=18,y=28]]
for(int i = 0; i < ptsArray.length; i += 2) {
int x = Integer.parseInt(ptsArray[i]);
int y = Integer.parseInt(ptsArray[i+1]);
ptsList.add(new Point(x, y));
}
Collections.sort( ptsList, new Comparator<Point>() {
public int compare(Point x1, Point x2) {
int result = Double.compare(x1.getX(), x2.getX());
if ( result == 0 ) {
// both X are equal -> compare Y too
result = Double.compare(x1.getY(), x2.getY());
}
return result;
}
});
// ptsList is now sorted by both X and Y!
Edit:
To just find the lowest X and the lowest Y you can also go the 'classic' way without any (double-)sorting:
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
for ( Point p : ptsList ) {
final int x = (int)p.getX();
final int y = (int)p.getY();
if ( x < minX ) {
minX = x;
}
if ( y < minY ) {
minY = y;
}
}
I have a set of methods in my platformer in progress to detect and resolve collisions between entities and the tilemap, but they are doing a bad job of it.
The bottom method is the one being called, like this:
player.velocity = player.velocity.add(getFinalCollisionVector());
where player.velocity is a Vec2D.
private List<Rectangle2D> getCollidingTiles(){
List<Rectangle2D> collidingTiles = new ArrayList<Rectangle2D>();
for(int x = (int) (this.getX()/Tile.SIZE); x <= (int) (this.getX()/Tile.SIZE) + this.getRect2D().getWidth()/Tile.SIZE; x++){
for(int y = (int) (this.getX()/Tile.SIZE); y <= (int) (this.getX()/Tile.SIZE) + this.getRect2D().getHeight()/Tile.SIZE; y++){
if(map.getTileAt(x, y).getAttribute(Attribute.SOLID))
if(map.getCollisionBoxAt(x,y).isColliding(this.collisionBox))
collidingTiles.add(new Rectangle2D.Double(x, y, Tile.SIZE, Tile.SIZE));
}
}
return collidingTiles;
}
private List<Vec2D> getAllTileCollisionVectors(){
List<Rectangle2D> collidingTiles = getCollidingTiles();
List<Vec2D> collisionVectors = new ArrayList<Vec2D>();
for(Rectangle2D rec : collidingTiles){
collisionVectors.add(getCorrectionVector(rec));
}
return collisionVectors;
}
private Vec2D getCorrectionVector(Rectangle2D target)
{
Vec2D ret = new Vec2D();
double x1 = (this.getX() + this.getSize().x) - target.getX();
double x2 = this.getX() - (target.getX() + target.getWidth());
double y1 = (this.getY() + this.getSize().y) - target.getY();
double y2 = this.getY() - (target.getY() + target.getHeight());
// calculate displacement along X-axis
if (x1 < x2)
{
ret.x = x1;
}
else if (x1 > x2)
{
ret.x = x2;
}
// calculate displacement along Y-axis
if (y1 < y2)
{
ret.y = y1;
}
else if (y1 > y2)
{
ret.y = y2;
}
return ret;
}
protected Vec2D getFinalCollisionVector(){
List<Vec2D> collisionVectors = getAllTileCollisionVectors();
if(collisionVectors.size() < 1)
return new Vec2D(0,0);
Vec2D finalVector = new Vec2D();
for(Vec2D vec : collisionVectors){
finalVector = finalVector.add(vec);
}
return finalVector;
}
What am I doing wrong in my code? This is the behavior that the player shows, where he falls (due to gravity) to that point, and then he freezes.
OP here:
Since there are no other answers, I've posted my own.
I've abandonded the old implementation,started from the ground up, and it works now. This is the new implementation:
public Corners getCornersAreSolid(double x, double y) {
int leftTile = (int)(x / Tile.SIZE);
int rightTile = (int)((x + moveData.collisionBox.getWidth()) / Tile.SIZE);
int topTile = (int)(y / Tile.SIZE);
int bottomTile = (int)((y + moveData.collisionBox.getHeight()) / Tile.SIZE);
boolean topLeft = hasAttribute(map, Attribute.SOLID, topTile, leftTile);
boolean topRight = hasAttribute(map, Attribute.SOLID, topTile, rightTile);
boolean bottomLeft = hasAttribute(map, Attribute.SOLID, bottomTile, leftTile);
boolean bottomRight = hasAttribute(map, Attribute.SOLID, bottomTile, rightTile);
Corners solidCorners = new Corners();
solidCorners.topLeft = topLeft;
solidCorners.topRight = topRight;
solidCorners.bottomRight = bottomRight;
solidCorners.bottomLeft = bottomLeft;
return solidCorners;
}
private boolean hasAttribute(GameMap map, Attribute attribute, int tileY, int tileX) {
boolean result = false;
if (tileX >= 0 && tileX < map.getWidthInTiles() && tileY >= 0 && tileY < map.getHeightInTiles()) {
result = map.getTileAt(tileX, tileY).getAttribute(attribute);
}
return result;
}
public Vec2D getNextPosition() {
int currCol = (int) (getX() / Tile.SIZE);
int currRow = (int) (getY() / Tile.SIZE);
double destX = getX() + moveData.velocity.x;
double destY = getY() + moveData.velocity.y;
double tempX = getX();
double tempY = getY();
Corners solidCorners = getCornersAreSolid(getX(), destY);
boolean topLeft = solidCorners.topLeft;
boolean topRight = solidCorners.topRight;
boolean bottomLeft = solidCorners.bottomLeft;
boolean bottomRight = solidCorners.bottomRight;
this.framesSinceLastCollision += 1;
if(moveData.velocity.y < 0) {
if(topLeft || topRight) {
moveData.velocity.y = 0;
tempY = currRow * Tile.SIZE;
this.framesSinceLastCollision = 0;
}
else {
tempY += moveData.velocity.y;
}
}
else if(moveData.velocity.y > 0) {
if(bottomLeft || bottomRight) {
moveData.velocity.y = 0;
tempY = (currRow + 1) * Tile.SIZE - moveData.collisionBox.getHeight() % Tile.SIZE - 1 ;
this.framesSinceLastCollision = 0;
}
else {
tempY += moveData.velocity.y;
}
}
solidCorners = getCornersAreSolid(destX, getY());
topLeft = solidCorners.topLeft;
topRight = solidCorners.topRight;
bottomLeft = solidCorners.bottomLeft;
bottomRight = solidCorners.bottomRight;
if(moveData.velocity.x < 0) {
if(topLeft || bottomLeft) {
moveData.velocity.x = 0;
tempX = currCol * Tile.SIZE;
this.framesSinceLastCollision = 0;
}
else {
tempX += moveData.velocity.x;
}
}
if(moveData.velocity.x > 0) {
if(topRight || bottomRight) {
moveData.velocity.x = 0;
tempX = (currCol + 1) * Tile.SIZE - moveData.collisionBox.getWidth() % Tile.SIZE -1 ;
this.framesSinceLastCollision = 0;
}
else {
tempX += moveData.velocity.x;
}
}
return new Vec2D(tempX, tempY);
}
private static class Corners{
public boolean topLeft, topRight;
public boolean bottomLeft, bottomRight;
public Corners(){
topLeft = false;
topRight = false;
bottomLeft = false;
bottomRight = false;
}
}