I have no clue how to change this into p5.js. Just trying to work on conversions. Please help me get this code to work in p5.js.
I know that this is old code and p5.js have different rules. Maybe the array isn't working. I have no clue.
Myself myself;
ArrayList<Enemy> enemies;
`enter code here`ArrayList<Enemy> enemies;
ArrayList<Bullet> myBullets;
ArrayList<Bullet> eneBullets;
void setup(){
size(640, 640);
rectMode(CENTER);
myself = new Myself();
enemies = new ArrayList<Enemy>();
myBullets = new ArrayList<Bullet>();
eneBullets = new ArrayList<Bullet>();
}
void draw(){
background(0);
myself.display();
for(Enemy enemy: enemies){
enemy.display();
}
for(Bullet bullet: myBullets){
bullet.display();
}
for(Bullet bullet: eneBullets){
bullet.display();
}
myself.update();
ArrayList<Enemy> nextEnemies = new ArrayList<Enemy>();
for(Enemy enemy: enemies){
enemy.update();
if(!enemy.isDead){
nextEnemies.add(enemy);
}
}
enemies = nextEnemies;
ArrayList<Bullet> nextMyBullets = new ArrayList<Bullet>();
for(Bullet bullet: myBullets){
bullet.update();
if(!bullet.isDead){
nextMyBullets.add(bullet);
}
}
myBullets = nextMyBullets;
ArrayList<Bullet> nextEneBullets = new ArrayList<Bullet>();
for(Bullet bullet: eneBullets){
bullet.update();
if(!bullet.isDead){
nextEneBullets.add(bullet);
}
}
eneBullets = nextEneBullets;
if(random(1) < 0.02){
enemies.add(new Enemy());
}
}
class Myself{
PVector loc;
float size;
int coolingTime;
boolean isDead;
Myself(){
size = 25;
loc = new PVector(width / 2, height - size / 2 - 10);
coolingTime = 0;
isDead = false;
}
void display(){
if(isDead){
fill(255, 255, 0);
stroke(0, 255, 0);
} else {
noFill();
stroke(0, 255, 0);
}
rect(loc.x, loc.y, size, size);
}
void update(){
isDead = false;
float dmx = mouseX - loc.x;
dmx = constrain(dmx, -5, 5);
loc.x += dmx;
coolingTime++;
if(mousePressed && coolingTime >= 10){
myBullets.add(new Bullet());
coolingTime = 0;
}
for(Bullet b: eneBullets){
if((loc.x - size / 2 <= b.loc.x && b.loc.x <= loc.x + size / 2)
&& (loc.y - size / 2 <= b.loc.y && b.loc.y <= loc.y + size / 2)){
isDead = true;
b.isDead = true;
break;
}
}
for(Enemy e: enemies){
if(abs(loc.x - e.loc.x) < size / 2 + e.size / 2 && abs(loc.y - e.loc.y) < size / 2 + e.size / 2){
isDead = true;
e.isDead = true;
break;
}
}
}
}
class Bullet{
PVector loc;
float vel;
boolean isMine;
boolean isDead;
Bullet(){
loc = new PVector(myself.loc.x, myself.loc.y);
vel = -10;
isMine = true;
}
Bullet(Enemy enemy){
loc = new PVector(enemy.loc.x, enemy.loc.y);
vel = 5;
isMine = false;
}
void display(){
if(isMine){
stroke(0, 255, 0);
} else {
stroke(255, 0, 0);
}
line(loc.x, loc.y, loc.x, loc.y + vel);
}
void update(){
loc.y += vel;
if((vel > 0 && loc.y > height) || (vel < 0 && loc.y < 0)){
isDead = true;
}
}
}
class Enemy{
PVector loc;
float vel;
float size;
int coolingTime;
boolean isDead;
Enemy(){
size = 25;
loc = new PVector(random(size / 2, width - size / 2), -size / 2);
vel = 3;
coolingTime = int(random(60));
isDead = false;
}
void display(){
noFill();
stroke(255, 0, 0);
rect(loc.x, loc.y, size, size);
}
void update(){
loc.y += vel;
if(loc.y > height){
isDead = true;
}
coolingTime++;
if(coolingTime >= 60){
eneBullets.add(new Bullet(this));
coolingTime = 0;
}
for(Bullet b: myBullets){
if((loc.x - size / 2 <= b.loc.x && b.loc.x <= loc.x + size / 2)
&& (loc.y - size / 2 <= b.loc.y && b.loc.y <= loc.y + size / 2)){
isDead = true;
b.isDead = true;
break;
}
}
}
}
If you could just tell me what parts i need to change or fix it it would help me a lot thankyou.
You shouldn't try to translate code line-by-line. Instead, you need to take a step back and understand what the program does, and then you implement that in the target language.
They syntax for Processing (which is based on Java) is very different from the syntax for p5.js (which is based on JavaScript).
Instead of trying to "convert" the code, you need to think in terms of what the code actually does. And then start with a new program in p5.js and do that using p5.js. It's not always going to be a line-by-line conversion.
here's your code:
class myself{
constructor()
{
this.x = 0;
this.y = height-35;
this.size = 25;
this.health = 25;
this.cooldown = 0;
this.plrBullets = [];
}update(){this.cooldown++;
if (mouseIsPressed && this.cooldown>10)
{
this.cooldown = 0;
this.plrBullets.push(new Bullet(true,this.x,this.y));
}
let nextPlrBullets = [];
for (let i = 0; i<this.plrBullets.length; i++)
{
this.plrBullets[i].update();
if (this.plrBullets[i].alive)
{
nextPlrBullets.push(this.plrBullets[i]);
}
}
this.plrBullets = nextPlrBullets;
this.x += constrain(mouseX - this.x, -10, 10);
for (let i = 0; i<eneBullets.length; i++)
{
let c = eneBullets[i];
if (c.x > this.x && c.x < this.x + 25 && c.y > this.y && c.y < this.y + 25)
{
this.health--;
c.alive = false;
if (this.health<=0)
{
print("you lost")
}
}
}
noFill();
stroke(0,255,0);
rect(this.x,this.y,this.size,this.size);
}
}
class enemy{
constructor()
{
this.x = random(15,width-15);
this.y = random(-100,0);
this.size = 25;
this.cooldown = 0;
this.alive = true;
}
update()
{
this.y+=2.5;
this.cooldown++;
if (this.cooldown>20)
{
this.cooldown = 0;
eneBullets.push(new Bullet(false,this.x,this.y));
}
let nextEneBullets = [];
for (let i = 0; i<eneBullets.length; i++)
{
eneBullets[i].update();
if (eneBullets[i].alive)
{
nextEneBullets.push(eneBullets[i]);
}
}
eneBullets = nextEneBullets;
for (let i = 0; i<my_self.plrBullets.length; i++)
{
let c = my_self.plrBullets[i];
if (c.x > this.x && c.x < this.x + 25 && c.y > this.y && c.y < this.y + 25)
{
this.alive = false
c.alive = false;
}
}
if (this.y>height+35)
{
this.y = random(-100,0);
}
noFill();
stroke(255,0,0);
rect(this.x,this.y,this.size,this.size);
}
}
class Bullet{
constructor(dir,x,y)
{
this.x = x;
this.speed = 5;
this.dir = dir;
this.alive = true;
this.y = y;
}
update()
{
if (this.dir)
{
this.y -= this.speed;
}else{
this.y += this.speed;
}
if (this.y<-20)
{
this.alive = false;
}//this.y = -25;
if(this.dir)
{
stroke(0,255,0);
}else{
stroke(255,0,0);
}
line(this.x,this.y,this.x,this.y+25);
}
}
let my_self;
let eneBullets = [];
let enemies = [];
function setup()
{
createCanvas(640,640);
rectMode(CENTER);
my_self = new myself();
for (let i = 0; i<10; i++)
{
enemies.push(new enemy());
}
}
function draw()
{
background(0);
if(random(1) < 0.02 && enemies.length<15){
enemies.push(new enemy());
}
my_self.update();
let newEnimies = [];
for (let i = 0; i<enemies.length; i++)
{
enemies[i].update();
if (enemies[i].alive)
{
newEnimies.push(enemies[i]);
}
s}
enemies = newEnimies;
if (enemies.length == 0)
{
print("you win");
}
}
Sorry that the collisions are a bit wacky but when converting processing to p5 here are some tips:
1: don’t use array lists use arrays with push instead of add
2: no data types only LET
3: print not println
4: class constructors are declared as “constructor” instead of class name
5: use “function” before function declaration instead of void
6: pvector is vector
7: google is your friend
8: the p5 documentation
I have this method:
public boolean moveTowardsPoint(Point p, double stepSize, double minDistance, ArrayList<GameBodyObject> objects, boolean shouldStepInto, AStar astar) {
ArrayList<GameBodyObject> others = (ArrayList<GameBodyObject>) objects.clone();
others.remove(this);
if(aStarPath == null || lastPointToMove == null || !lastPointToMove.isAlmostTheSame(p, 0.01)) {
try {
astar.getGrid().unsetObstacleForObject(this);
aStarPath = astar.process(this, p, objects);
Collections.reverse(aStarPath);
currentIndex = 0;
current = null;
astar.getGrid().reset();
} catch(Exception e) {
e.printStackTrace();
}
}
lastPointToMove = p.clone();
double dx = p.getX() - getPosition().getX();
double dy = p.getY() - getPosition().getY();
Point lastPosition = getPosition().clone();
boolean isClose = false;
double dist = Math.sqrt(dx * dx + dy * dy);
if (dist < Math.max(minDistance, stepSize)) {
isClose = true;
} else {
if(aStarPath.indexOf(current) >= aStarPath.size() - 1) {
if(shouldStepInto && dist <= stepSize) {
setPosition(p.clone());
}
} else if(aStarPath != null && aStarPath.size() > 0) {
if (current == null) {
currentIndex = 0;
current = aStarPath.get((int)currentIndex);
}
double mss = stepSize / current.getWidth();
Spot p2 = aStarPath.get(Math.min((int)Math.ceil(currentIndex + mss), aStarPath.size() - 1));
Spot p1 = current;
if(!p1.equals(p2)) {
Vector v = new Vector(p2.getX() - p1.getX(), p2.getY() - p1.getY()).normalize();
getPosition().moveByVector(v.mult(stepSize));
setRotation(v.getAngle());
}
currentIndex = Math.min(currentIndex + mss, aStarPath.size() - 1);
current = aStarPath.get((int)currentIndex);
}
}
return isClose;
}
It's a GameBodyObject's method.
It should calculate A* path and move towards a point on that path however it does move slower than a current(currentIndex) moves forward through the path. I have there some distance checking at the beginning but that's not important. I move currentIndex by the amount of stepSize divided by the grid cell size. In my case it's 0.6. Then I have a moveByVector method which simply moves a Point by the vector.
How to make it synchronous so that it moves as fast as current(currentIndex)? Is it even possible?
EDIT
Here are my variables definitions:
private List<Spot> aStarPath = null;
private Spot current = null;
private double currentIndex = -1;
private Point lastPointToMove;
There was a one mistake in that solution. On diagonals the currentIndex growed faster than the player moved. So you have to take in account the angle in which the player is moving along that path. Concretely this:
double coef = Math.max(Math.abs(Math.cos(angle)),Math.abs(Math.sin(angle)));
double mss = stepSize / (current.getWidth() / coef);
The full method now looks like this and works like a charm:
public boolean moveTowardsPoint(Point p, double stepSize, double minDistance, ArrayList<GameBodyObject> objects, boolean shouldStepInto, AStar astar) {
ArrayList<GameBodyObject> others = (ArrayList<GameBodyObject>) objects.clone();
others.remove(this);
if(aStarPath == null || lastPointToMove == null || !lastPointToMove.isAlmostTheSame(p, 0.01)) {
try {
astar.getGrid().unsetObstacleForObject(this);
aStarPath = astar.process(this, p, objects);
Collections.reverse(aStarPath);
currentIndex = 0;
current = null;
astar.getGrid().reset();
} catch(Exception e) {
e.printStackTrace();
}
}
lastPointToMove = p.clone();
double dx = p.getX() - getPosition().getX();
double dy = p.getY() - getPosition().getY();
Point lastPosition = getPosition().clone();
boolean isClose = false;
double dist = Math.sqrt(dx * dx + dy * dy);
if (dist < Math.max(minDistance, stepSize)) {
isClose = true;
} else {
if(aStarPath.indexOf(current) >= aStarPath.size() - 1) {
if(shouldStepInto && dist <= stepSize) {
setPosition(p.clone());
}
} else if(aStarPath != null && aStarPath.size() > 0) {
if (current == null) {
currentIndex = 0;
current = aStarPath.get((int)currentIndex);
}
Spot c1 = aStarPath.get((int)currentIndex);
Spot c2 = aStarPath.get(Math.min((int)currentIndex + 1, aStarPath.size() - 1));
double angle = new Vector(c2.getX() - c1.getX(),c2.getY() - c1.getY()).getAngle();
double coef = Math.max(Math.abs(Math.cos(angle)),Math.abs(Math.sin(angle)));
double mss = stepSize / (current.getWidth() / coef);
Spot p2 = aStarPath.get(Math.min((int)Math.ceil(currentIndex + mss), aStarPath.size() - 1));
Spot p1 = current;
if(!p1.equals(p2)) {
Vector v = new Vector(p2.getX() - p1.getX(), p2.getY() - p1.getY()).normalize();
getPosition().moveByVector(v.mult(stepSize));
setRotation(v.getAngle());
}
currentIndex = Math.min(currentIndex + mss, aStarPath.size() - 1);
current = aStarPath.get((int)currentIndex);
}
}
return isClose;
}
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.
Am having issues trying to figure out how to translate or 'animate' my player's position to a new tile, this is what am doing :
if (input.right){
x += 1;
Right now am listening for key.inputs and then x++/x-- or y++/y-- on my players position that makes him move pixel by pixel, but i want my player to move exactly to the next tile(32 pixels) with one hit of the key with like a linear transition from the player's tile position to the next tile over time?
Something like (pseudo code i think..)
if input && walking false
walking = true
increment 1 by 1 32 ints to X over time?
after completed walking = false
I still cant even figure out the logic behind something like that.
An example is the movement in a game called Tibia.
Now Some bits of my code (player related)..
GAMECLASS >
public Game()
player = new Player(playerSpawn.x(), playerSpawn.y(), key);
player.init(level);
public void run()
....
render();
frames++;
....
public void update()
key.update();
player.update();
level.update();
public void render()
.....
int xScroll = ( player.x + 32) - screen.width / 2;
int yScroll = ( player.y + 32) - screen.height / 2;
level.render(xScroll, yScroll, screen);
player.render(screen);
for (int i =0; i < pixels.length; i++){
pixels[i] = screen.pixels[i];
}
SCREENCLASS >
......
public int[] pixels;
public int[] tiles = new int[VIEW_SIZE * VIEW_SIZE];
.....
public void renderTile(int xp, int yp, Tile tile){
xp -= xOffset;
yp -= yOffset;
for (int y = 0; y < tile.sprite.SIZE; y++){
int ya = y + yp;
for (int x = 0; x < tile.sprite.SIZE; x++){
int xa = x + xp;
if (xa < -tile.sprite.SIZE || xa >= width || ya < 0 || ya >= height) break;
if (xa < 0) xa = 0;
pixels[xa + ya * width] = tile.sprite.pixels[x + y * tile.sprite.SIZE];
}
}
}
//THIS IS THE METHOD CALLED TO RENDER THE PLAYER > SEE BELLOW AT THE PLAYER CLASS FOR THE CALL
public void renderMob(int xp, int yp, Mob mob){
xp -= xOffset;
yp -= yOffset;
for (int y = 0; y < 64; y++){
int ya = y + yp;
int ys = y;
for (int x = 0; x < 64; x++){
int xa = x + xp;
int xs = x;
if (xa < -64 || xa >= width || ya < 0 || ya >= height) break;
if (xa < 0) xa = 0;
int col = mob.getSprite().pixels[xs + ys * 64];
if (mob instanceof Chazer && col == 0xFF9b0000) col = 0xff54ff00;
if (col != 0xFFFF00FF) pixels[xa + ya * width] = col;
}
}
}
PLAYERCLASS >
public Player(int x , int y, Keyboard input){
this.x = x;
this.y = y;
this.input = input;
}
//PLAYER UPDATE
public void update(){
if (anim < 7500) anim++;
else anim = 0;
if (input.down) ya = 1;
if (input.up) ya = -1;
if (input.left) xa = -1;
if (input.right) xa = 1;
//CHECK BELLOW TO THIS MOVE METHOD
if (xa != 0){
move(xa, 0);
} else if(ya != 0){
move(0, ya);
}
}
clear();
}
//HERE ANIMATION AND CHOOSE WHAT SPRITE = WHERE PLAYER IS LOOKING AT
public void render(Screen screen){
if (dir == 0) {
sprite = Sprite.player_n;
if (walking) {
if (anim % 20 > 10){
sprite = sprite.player_n1;
} else {
sprite = sprite.player_n2;
}
}
}
if (dir == 1) {
sprite = Sprite.player_e;
if (walking) {
if (anim % 20 > 10){
sprite = sprite.player_e1;
} else {
sprite = sprite.player_e2;
}
}
}
if (dir == 2) {
sprite = Sprite.player_s;
if (walking) {
if (anim % 20 > 10){
sprite = sprite.player_s1;
} else {
sprite = sprite.player_s2;
}
}
}
if (dir == 3) {
sprite = Sprite.player_w;
if (walking) {
if (anim % 20 > 10){
sprite = sprite.player_w1;
} else {
sprite = sprite.player_w2;
}
}
}
// ADDING OFFSET CUZ THE PLAYER IS DOUBLE THE SIZE OF THE TILE
int xx = x - 42;
int yy = y - 42;
screen.renderMob(xx, yy, sprite);
}
//THIS IS HOW I MOVE THE PLAYER
public void move(int xa, int ya){
if (xa != 0 && ya != 0){
move(xa, 0);
move(0, ya);
return;
}
if (xa > 0) dir = 1;
if (xa < 0) dir = 3;
if (ya > 0) dir = 2;
if (ya < 0) dir = 0;
if(!collision(xa, 0)){
x += xa;
}
if(!collision(0, ya)){
y += ya;
}
}
Thanks alooot!
**Run method!
public void run() {
long xlastTime = System.nanoTime();
long timer = System.currentTimeMillis();
final double xns = 1000000000.0 / 60.0;
double delta = 0;
int frames = 0;
requestFocus();
while(running){
long xnow = System.nanoTime();
delta += (xnow-xlastTime) / xns;
xlastTime = xnow;
while (delta >= 1) {
update();
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000){
timer += 1000;
frame.setTitle(title + " | " + frames + " fps");
frames = 0;
}
}
stop();
}
What I would do is declare two int fields in the player class:
private float xToMove = 0;
private float yToMove = 0;
Then, under your input event:
if (input.down && yToMove == 0)
yToMove = -32;
if (input.up && yToMove == 0)
yToMove = 32;
if (input.left && xToMove == 0)
xToMove = -32;
if (input.right && xToMove == 0)
xToMove = 32;
And finally, in your Player class's update method:
public void update()
{
if (xToMove > 0)
{
xToMove--;
x++;
}
if (xToMove < 0)
{
xToMove++;
x--;
}
if (yToMove > 0)
{
yToMove--;
y++;
}
if (yToMove < 0)
{
yToMove++;
y--;
}
}
Of course this is simplified a bit but the concept is there
EDIT: to change the speed. Note that xToMove and yToMove have been changed to floats.
You can use a float to represent the amount of time 1 move takes
float period = 1000; //The time one move takes in milliseconds
Somewhere you should calculate the number of pixels to be moved each frame. You could make a calculateSpeed() method or just throw it into the constructor or something. Depends on if you want speed to change during the game.
float speed = 32f / (fps * (period / 1000f)); //fps should be obtained dynamically and should be a float
Then when you update you should do this:
if (xToMove > 0)
{
xToMove -= speed;
x += speed;
if (xToMove <= 0)
{
//Set these guys to nice even numbers to prevent problems
xToMove = 0;
x = (float) Math.round(x);
}
}
Also make sure that x and y are floats.
EDIT 2: fps
int frames = 0;
int fps = 60;
requestFocus();
while(running){
long xnow = System.nanoTime();
delta += (xnow-xlastTime) / xns;
xlastTime = xnow;
while (delta >= 1) {
update();
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000){
timer += 1000;
fps = frames;
frame.setTitle(title + " | " + fps + " fps");
frames = 0;
}
}
stop();
I'm looking for help in finding the source of jitter when using collision detection.
I've implemented a java game (using eclipse and slick2d) and have been loosely following this guide:
http://katyscode.wordpress.com/2013/01/18/2d-platform-games-collision-detection-for-dummies
but of course changing bits where necessary to suit my game and java not cpp.
From the research I have done I think the root cause of my jitter is coming from rounding errors.
Despite that being my main suspect, I still haven't been able to identify where it's occurring.
Sorry if the indentation isn't quite right there, had a little trouble using the code block recognition.
Basically I'm creating variables in the class.
In init() I set up most of the resources.
In render() all the drawing takes place. Note the graphics translation so the camera follows the player.
In update I'm of course updating the position of the player according to user input, gravity and friction.
I also call my collision detection from this method.
Collision detection is working on a penetration resolution method.
(Yes I know I'm exhaustively comparing with every single world object. I will be improving my efficiency with AABB's when I have sorted more fundamental problems out. Like jitter!)
My method first calculates how much the player expects to move in each axis, then for each world object it checks for intersection with the players bounding points (floating point values that represent coordinates around the player). It checks this in each direction and uses the result to determine in which axis the collision occurred, if any, so that the appropriate action can be taken.
Sorry it's a put load of code, but it's collision detection after all, which isn't a small thing.
Here is my Play class where all the updating goes on for the game:
package GameFiles;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.newdawn.slick.*;
import org.newdawn.slick.state.*;
import java.awt.Rectangle;
//import java.awt.geom.Rectangle2D;
public class Play extends BasicGameState{
static int tileSize = 32;
File levelMap;
SpriteSheet spriteSheet;
int[][] currentMap;
Image[] groundTiles = new Image[4];
//List<Rectangle2D> levelBounds = new ArrayList<Rectangle2D>();
List<Rectangle> levelBounds = new ArrayList<Rectangle>();
Player player;
float playerX, playerY;
int dir;
float acc, mov, friction, gravity;
float runSpeed;
float maxAcc;
boolean inAir, jumping, keyDown;
boolean exitFlag;
int mapHeight, mapWidth;
float mapX, mapY;
float speedX, speedY;
int worldObjectCount;
int iterations;
public Play(int state){
}
public void init(GameContainer gc, StateBasedGame sbg) throws SlickException{
playerX = Game.scrWidth/2;
playerY = Game.scrHeight - tileSize - tileSize;
player = new Player(playerX, playerY);
levelMap = new File("maps/lvl1.txt");
spriteSheet = new SpriteSheet("res/tiles/tilesets/DungeonCrawlTilesetBW.png", tileSize, tileSize);
try
{
currentMap = readMap(levelMap);
}
catch (IOException e)
{
System.out.println("Array size mismatch when copying arrays.");
e.printStackTrace();
}
levelBounds.clear();
for(int x = 0; x < mapWidth; x++)
{
for(int y = 0; y < mapHeight; y++){
if(currentMap[x][y] == 1){
levelBounds.add(new Rectangle(x*tileSize, Game.scrHeight - mapHeight*tileSize + y*tileSize, tileSize, tileSize));
//levelBounds.add(new Rectangle2D.Float(x*tileSize, Game.scrHeight - mapHeight*tileSize + y*tileSize, tileSize, tileSize));
System.out.println("Added new bounding box: " + (x*tileSize) + ", " + (Game.scrHeight - mapHeight*tileSize + y*tileSize) + ", " + tileSize);
}
}
}
worldObjectCount = levelBounds.size();
System.out.println("World object count: " + worldObjectCount);
groundTiles[0] = spriteSheet.getSubImage(4, 16);
groundTiles[1] = spriteSheet.getSubImage(13, 19);
dir = 1;
acc = 0.0f;
mov = 0.0f;
friction = 4f;
gravity = 4f;
runSpeed = 0.6f;
maxAcc = -1f;
inAir = false;
jumping = false;
keyDown = false;
exitFlag = false;
speedX = 0.0f;
speedY = 0.0f;
iterations = 3;
}
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException{
//determine cameraX and cameraY
float cameraX, cameraY;
cameraX = player.getX() - Game.scrWidth/2;
cameraY = player.getY() - (Game.scrHeight/2 - tileSize - tileSize);
g.translate(-cameraX, -cameraY);
player.render(g);
for(int x = 0; x < mapWidth; x++)
{
for(int y = 0; y < mapHeight; y++){
if(currentMap[x][y] == 1){
groundTiles[0].draw(x*tileSize, Game.scrHeight - mapHeight*tileSize + y*tileSize);
}
}
}
g.translate(cameraX, cameraY);
}
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException{
Input input = gc.getInput();
float secondsElapsed = delta/1000.0f;
checkCollisions(secondsElapsed);
player.setX((player.getX() + speedX));
player.setY((player.getY() - speedY));
//check inputs
checkKeyEvents(input);
//slow down / friction
if(!keyDown){
if(mov < 0)
mov += friction * secondsElapsed;
else
mov -= friction * secondsElapsed;
}
speedX = mov;
if (speedX > 0 && speedX < friction * secondsElapsed) speedX = 0;
if (speedX < 0 && speedX > -friction * secondsElapsed) speedX = 0;
//jump or fall
acc -= gravity * secondsElapsed;
if (acc < maxAcc){
acc = maxAcc;
}
speedY = acc;
//exit when exitFlag true
if(exitFlag){
gc.exit();
}
}
public void checkCollisions(float secondsElapsed){
boolean contactX = true, contactYbottom = true, contactYtop = true;
// Keep iterating the contact solver until the maximum number of iterations is reached
// or no collisions are detected
for (int iteration = 0; iteration < iterations && (contactX || contactYbottom || contactYtop); iteration++)
{
float nextMoveX = speedX * secondsElapsed;
float nextMoveY = speedY * secondsElapsed;
contactX = contactYbottom = contactYtop = false;
float projectedMoveX, projectedMoveY, originalMoveX, originalMoveY;
originalMoveX = nextMoveX;
originalMoveY = nextMoveY;
for (int o = 0; o < worldObjectCount && !contactX && !contactYbottom && !contactYtop; o++)
{
for (int dir = 0; dir < 6; dir++)
{
//top, bottom, left, left, right, right.
if (dir == 0 && nextMoveY < 0) continue;
if (dir == 1 && nextMoveY > 0) continue;
if (dir == 2 && nextMoveX > 0) continue;
if (dir == 3 && nextMoveX > 0) continue;
if (dir == 4 && nextMoveX < 0) continue;
if (dir == 5 && nextMoveX < 0) continue;
projectedMoveX = (dir >= 2? nextMoveX : 0);
projectedMoveY = (dir < 2? nextMoveY : 0);
float[][] collisionPoint = player.getBounds();
Rectangle curRect = new Rectangle(levelBounds.get(o).x, levelBounds.get(o).y, levelBounds.get(o).width, levelBounds.get(o).height);
//Rectangle2D curRect = levelBounds.get(o).getBounds2D();
while (curRect.contains(collisionPoint[dir][0] + projectedMoveX, collisionPoint[dir][1] + projectedMoveY)
|| curRect.intersects(collisionPoint[dir][0] + projectedMoveX, collisionPoint[dir][1] + projectedMoveY, 1, 1))
{
if (dir == 0) projectedMoveY += 0.05f; //top collision
if (dir == 1) projectedMoveY -= 0.05f; //bottom collision
if (dir == 2) projectedMoveX += 0.05f; //left collision
if (dir == 3) projectedMoveX += 0.05f;
if (dir == 4) projectedMoveX -= 0.05f; //right collision
if (dir == 5) projectedMoveX -= 0.05f;
}
if (dir >= 2 && dir <= 5)
nextMoveX = projectedMoveX;
if (dir >= 0 && dir <= 1)
nextMoveY = projectedMoveY;
}
if (nextMoveY > originalMoveY && originalMoveY != 0)
{
contactYtop = true;
}
if (nextMoveY < originalMoveY && originalMoveY != 0)
{
contactYbottom = true;
}
if (Math.abs(nextMoveX - originalMoveX) > 0.01f)
{
contactX = true;
}
if (contactX && contactYtop && speedY > 0)
speedY = nextMoveY = 0;
}
if (contactYbottom || contactYtop)
{
player.setY(player.getY() + nextMoveY);
speedY = 0;
acc = 0;
if (contactYbottom)
jumping = false;
}
if (contactX)
{
player.setX(player.getX() + nextMoveX);
speedX = 0;
mov = 0;
}
}//end collisions
}
public int[][] readMap(File level) throws IOException, SlickException{
BufferedReader br = new BufferedReader(new FileReader(level));
mapWidth = Integer.parseInt(br.readLine());
mapHeight = Integer.parseInt(br.readLine());
int[][] map = new int[mapWidth][mapHeight];
for(int row = 0; row < mapHeight; row++)
{
String line = br.readLine();
if(line == null || line.isEmpty())
{
System.out.println("Line is empty or null");
}
else
{
String[] tileValues = line.split(",");
for(int col = 0; col < mapWidth; col++)
{
map[col][row] = Integer.parseInt(tileValues[col]);
}
}
}
br.close();
return map;
}
public void checkKeyEvents(Input input){
//key input events
if(input.isKeyPressed(Input.KEY_DOWN)){
}
if(input.isKeyPressed(Input.KEY_UP)){
if(!jumping){
acc = 1f;
}
jumping = true;
}
if(input.isKeyDown(Input.KEY_LEFT) && !input.isKeyDown(Input.KEY_RIGHT)){
keyDown = false;
mov -= 0.006f;
if (mov < -runSpeed){
mov = -runSpeed;
}
}
if(input.isKeyDown(Input.KEY_RIGHT) && !input.isKeyDown(Input.KEY_LEFT)){
keyDown = false;
mov += 0.006f;
if (mov > runSpeed){
mov = runSpeed;
}
}
if(input.isKeyPressed(Input.KEY_ESCAPE)){
exitFlag = true;
}
}
public int getID(){
return 1;
}
}
Since I can't predict what more info a potential helper might need, I'll leave it at that for now, but of course I can provide any more info when/where needed.
Thanks,
J.