I'm currently recreating a Civilization game in Processing. I'm planning to implement the feature in a which a given unit can see every possible move it can make with a given number of hexes it is allowed to move. All possible endpoints are marked with red circles. However, units cannot move through mountains or bodies of water. I'm trying to approach this by finding out every possible combination of moves I can make without the unit going into a mountain or body of water but I can't figure out how I can determine every combination.
There are 6 directions that any unit can go in, north-east, north, north-west, south-east, south, south-west. The max number of movements I'm assigning to any unit would probably go up to 6. Any higher and I'm afraid processing may become to slow every time I move a unit.
I'm trying to recreate this:
What I'm hoping the result will look like with two possible movements (without the black arrows):
Raw version of that image:
Here is the code I use to draw the hex grid. Immediately after drawing each individual hex, its center's x coords and y coords are stored in xHexes and yHexes respectively. Also, immediately after generating the type of tile (e.g. grass, beach), the type of tile is also stored in an array named hexTypes. Therefore, I can get the x and y coords and type of hex of any hex I want on the map just by referencing its index.
Code used to draw a single hexagon:
beginShape();
for (float a = PI/6; a < TWO_PI; a += TWO_PI/6) {
float vx = x + cos(a) * gs*2;
float vy = y + sin(a) * gs*2;
vertex(vx, vy);
}
x is the x coord for centre of hexagon
y is the y coord for centre of hexagon
gs = radius of hexagon
Code used to tesselate hex over the window creating a hex grid:
void redrawMap() {
float xChange = 1.7;
float yChange = 6;
for (int y = 0; y < ySize/hexSize; y++) {
for (int x = 0; x < xSize/hexSize; x++) {
if (x % 2 == 1) {
// if any part of this hexagon being formed will be visible on the window and not off the window.
if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
drawHex(x*hexSize*xChange, y*hexSize*yChange, hexSize);
}
// only record and allow player to react with it if the entire tile is visible on the window
if (x*hexSize*xChange < width && int(y*hexSize*yChange) < height) {
xHexes.add(int(x*hexSize*xChange));
yHexes.add(int(y*hexSize*yChange));
}
} else {
if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
drawHex(x*hexSize*xChange, y*hexSize*yChange+(hexSize*3), hexSize);
}
if (x*hexSize*xChange < width && int(y*hexSize*yChange+(hexSize*3)) < height) {
xHexes.add(int(x*hexSize*xChange));
yHexes.add(int(y*hexSize*yChange+(hexSize*3)));
}
}
}
}
}
hexSize is a user-specified size for each hexagon, determining the number of hexagons that will be on the screen.
This answer will help you get to this (green is plains, red is hills and blue is water, also please don't flame my terrible grid):
Note that there is no pathfinding in this solution, only some very simple "can I get there" math. I'll include the full code of the sketch at the end so you can reproduce what I did and test it yourself. One last thing: this answer doesn't use any advanced design pattern, but it assume that you're confortable with the basics and Object Oriented Programming. If I did something which you're not sure you understand, you can (and should) ask about it.
Also: this is a proof of concept, not a "copy and paste me" solution. I don't have your code, so it cannot be that second thing anyway, but as your question can be solved in a bazillion manners, this is only one which I deliberately made as simple and visual as possible so you can get the idea and run with it.
First, I strongly suggest that you make your tiles into objects. First because they need to carry a lot of information (what's on each tile, how hard they are to cross, maybe things like resources or yield... I don't know, but there will be a lot of stuff).
The Basics
I organized my global variables like this:
// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it
// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;
ArrayList<Tile> _tiles = new ArrayList<Tile>(); // all the tiles
ArrayList<Tile> _canTravel = new ArrayList<Tile>(); // tiles you can currently travel to
The basics being that I like to be able to change my grid size on the fly, but that's just a detail. What's next is to choose a coordinate system for the grid. I choose the simplest one as I didn't want to bust my brain on something complicated, but you may want to adapt this to another coordinate system. I choose the offset coordinate type of grid: my "every second row" is half a tile offset. So, instead of having this:
I have this:
The rest is just adjusting the spatial coordinates of the tiles so it doesn't look too bad, but their coordinates stays the same:
Notice how I consider that the spatial coordinates and the grid coordinates are two different things. I'll mostly use the spatial coordinates for the proximity checks, but that's because I'm lazy, because you could make a nice algorithm which do the same thing without the spatial coordinates and it would probably be less costly.
What about the travel points? Here's how I decided to work: your unit has a finite amount of "travel points". Here there's no unit, but instead a global variable unitTravelPoints which will do the same thing. I decided to work with this scale: one normal tile is worth 10 travel points. So:
Plains: 10 points
Hills: 15 points
Water: 1000 points (this is impassable terrain but without going into the details)
I'm not going to go into the details of drawing a grid, but that's mostly because your algorithm looks way better than mine on this front. On the other hand, I'll spend some time on explaining how I designed the Tiles.
The Tiles
We're entering OOP: they are Drawable. Drawable is a base class which contains some basic info which every visible thing should have: a position, and an isVisible setting which can be turned off. And a method to draw it, which I call Render() since draw() is already taken by Processing:
class Drawable {
PVector position;
boolean isVisible;
public Drawable() {
position = new PVector(0, 0);
isVisible = true;
}
public void Render() {
// If you forget to overshadow the Render() method you'll see this error message in your console
println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
}
}
The Tile will be more sophisticated. It'll have more basic informations: row, column, is it currently selected (why not), a type like plains or hills or water, a bunch of neighboring tiles, a method to draw itself and a method to know if the unit can travel through it:
class Tile extends Drawable {
int row, column;
boolean selected = false;
TileType type;
ArrayList<Tile> neighbors = new ArrayList<Tile>();
Tile(int row, int column, TileType type) {
super(); // this calls the parent class' constructor
this.row = row;
this.column = column;
this.type = type;
// the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
position.x = (_tileSize * 1.5) * (column + 1);
position.y = (_tileSize * 0.5) * (row + 1);
// this part checks if this is an offset row to adjust the spatial coordinates
if (row % 2 != 0) {
position.x += _tileSize * 0.75;
}
}
// this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
if (travelPoints >= type.travelCost) {
// if the unit has enough travel points, we add the tile to the "the unit can get there" list
if (!_canTravel.contains(this)) {
// well, only if it's not already in the list
_canTravel.add(this);
}
// then we check if the unit can go further
for (Tile t : neighbors) {
if (originalTile) {
t.FillCanTravelArrayList(travelPoints, false);
} else {
t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
}
}
}
}
void Render() {
if (isVisible) {
// the type knows which colors to use, so we're letting the type draw the tile
type.Render(this);
}
}
}
The Tile Types
The TileType is a strange animal: it's a real class, but it's never used anywhere. That's because it's a common root for all tile types, which will inherit it's basics. The "City" tile may need very different variables than, say, the "Desert" tile. But both need to be able to draw themselves, and both need to be owned by the tiles.
class TileType {
// cosmetics
color fill = color(255, 255, 255);
color stroke = color(0);
float strokeWeight = 2;
// every tile has a "travelCost" variable, how much it cost to travel through it
int travelCost = 10;
// while I put this method here, it could have been contained in many other places
// I just though that it made sense here
void Render(Tile tile) {
fill(fill);
if (tile.selected) {
stroke(255);
} else {
stroke(stroke);
}
strokeWeight(strokeWeight);
DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
textAlign(CENTER, CENTER);
fill(255);
text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
}
}
Each tile type can be custom, now, yet each tile is... just a tile, whatever it's content. Here are the TileType I used in this demonstration:
// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
Plains() {
this.fill = color(0, 125, 0);
this.travelCost = 10;
}
}
class Water extends TileType {
// here I'm adding a random variable just to show that you can custom those types with whatever you need
int numberOfFishes = 10;
Water() {
this.fill = color(0, 0, 125);
this.travelCost = 1000;
}
}
class Hill extends TileType {
Hill() {
this.fill = color(125, 50, 50);
this.travelCost = 15;
}
}
Non-class methods
I added a mouseClicked() method so we can select a hex to check how far from it the unit can travel. In your game, you would have to make it so when you select a unit all these things fall into place, but as this is just a proof of concept the unit is imaginary and it's location is wherever you click.
void mouseClicked() {
// clearing the array which contains tiles where the unit can travel as we're changing those
_canTravel.clear();
for (Tile t : _tiles) {
// select the tile we're clicking on (and nothing else)
t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
if (t.selected) {
// if a tile is selected, check how far the imaginary unit can travel
t.FillCanTravelArrayList(unitTravelPoints, true);
}
}
}
At last, I added 2 "helper methods" to make things easier:
// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
// simple math, but with a twist: I'm not using the square root because it's costly
// we don't need to know the distance between the center and the point, so there's nothing lost here
return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}
// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
float angle = TWO_PI / npoints;
beginShape();
for (float a = 0; a < TWO_PI; a += angle) {
float sx = x + cos(a) * radius;
float sy = y + sin(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
How Travel is calculated
Behind the scenes, that's how the program knows where the unit can travel: in this example, the unit has 30 travel points. Plains cost 10, hills cost 15. If the unit has enough points left, the tile is marked as "can travel there". Every time a tile is in travel distance, we also check if the unit can get further from this tile, too.
Now, if you're still following me, you may ask: how do the tiles know which other tile is their neighbor? That's a great question. I suppose that an algorithm checking their coordinates would be the best way to handle this, but as this operation will happen only once when we create the map I decided to take the easy route and check which tiles were the close enough spatially:
void setup() {
// create the grid
for (int i=0; i<_gridWidth; i++) {
for (int j=0; j<_gridHeight; j++) {
int rand = (int)random(100);
if (rand < 20) {
_tiles.add(new Tile(j, i, new Water()));
} else if (rand < 50) {
_tiles.add(new Tile(j, i, new Hill()));
} else {
_tiles.add(new Tile(j, i, new Plains()));
}
}
}
// detect and save neighbor tiles for every Tile
for (Tile currentTile : _tiles) {
for (Tile t : _tiles) {
if (t != currentTile) {
if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
currentTile.neighbors.add(t);
}
}
}
}
}
Complete code for copy-pasting
Here's the whole thing in one place so you can easily copy and paste it into a Processing IDE and play around with it (also, it includes how I draw my awful grid):
// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it
// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;
ArrayList<Tile> _tiles = new ArrayList<Tile>();
ArrayList<Tile> _canTravel = new ArrayList<Tile>();
void settings() {
// this is how to make a window size's dynamic
size((int)(((_gridWidth+1) * 1.5) * _tileSize), (int)(((_gridHeight+1) * 0.5) * _tileSize));
}
void setup() {
// create the grid
for (int i=0; i<_gridWidth; i++) {
for (int j=0; j<_gridHeight; j++) {
int rand = (int)random(100);
if (rand < 20) {
_tiles.add(new Tile(j, i, new Water()));
} else if (rand < 50) {
_tiles.add(new Tile(j, i, new Hill()));
} else {
_tiles.add(new Tile(j, i, new Plains()));
}
}
}
// detect and save neighbor tiles for every Tile
for (Tile currentTile : _tiles) {
for (Tile t : _tiles) {
if (t != currentTile) {
if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
currentTile.neighbors.add(t);
}
}
}
}
}
void draw() {
background(0);
// show the tiles
for (Tile t : _tiles) {
t.Render();
}
// show how far you can go
for (Tile t : _canTravel) {
fill(0, 0, 0, 0);
if (t.selected) {
stroke(255);
} else {
stroke(0, 255, 0);
}
strokeWeight(5);
DrawPolygon(t.position.x, t.position.y, _tileSize/2, 6);
}
}
class Drawable {
PVector position;
boolean isVisible;
public Drawable() {
position = new PVector(0, 0);
isVisible = true;
}
public void Render() {
// If you forget to overshadow the Render() method you'll see this error message in your console
println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
}
}
class Tile extends Drawable {
int row, column;
boolean selected = false;
TileType type;
ArrayList<Tile> neighbors = new ArrayList<Tile>();
Tile(int row, int column, TileType type) {
super(); // this calls the parent class' constructor
this.row = row;
this.column = column;
this.type = type;
// the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
position.x = (_tileSize * 1.5) * (column + 1);
position.y = (_tileSize * 0.5) * (row + 1);
// this part checks if this is an offset row to adjust the spatial coordinates
if (row % 2 != 0) {
position.x += _tileSize * 0.75;
}
}
// this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
if (travelPoints >= type.travelCost) {
// if the unit has enough travel points, we add the tile to the "the unit can get there" list
if (!_canTravel.contains(this)) {
// well, only if it's not already in the list
_canTravel.add(this);
}
// then we check if the unit can go further
for (Tile t : neighbors) {
if (originalTile) {
t.FillCanTravelArrayList(travelPoints, false);
} else {
t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
}
}
}
}
void Render() {
if (isVisible) {
// the type knows which colors to use, so we're letting the type draw the tile
type.Render(this);
}
}
}
class TileType {
// cosmetics
color fill = color(255, 255, 255);
color stroke = color(0);
float strokeWeight = 2;
// every tile has a "travelCost" variable, how much it cost to travel through it
int travelCost = 10;
// while I put this method here, it could have been contained in many other places
// I just though that it made sense here
void Render(Tile tile) {
fill(fill);
if (tile.selected) {
stroke(255);
} else {
stroke(stroke);
}
strokeWeight(strokeWeight);
DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
textAlign(CENTER, CENTER);
fill(255);
text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
}
}
// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
Plains() {
this.fill = color(0, 125, 0);
this.travelCost = 10;
}
}
class Water extends TileType {
// here I'm adding a random variable just to show that you can custom those types with whatever you need
int numberOfFishes = 10;
Water() {
this.fill = color(0, 0, 125);
this.travelCost = 1000;
}
}
class Hill extends TileType {
Hill() {
this.fill = color(125, 50, 50);
this.travelCost = 15;
}
}
void mouseClicked() {
// clearing the array which contains tiles where the unit can travel as we're changing those
_canTravel.clear();
for (Tile t : _tiles) {
// select the tile we're clicking on (and nothing else)
t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
if (t.selected) {
// if a tile is selected, check how far the imaginary unit can travel
t.FillCanTravelArrayList(unitTravelPoints, true);
}
}
}
// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
// simple math, but with a twist: I'm not using the square root because it's costly
// we don't need to know the distance between the center and the point, so there's nothing lost here
return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}
// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
float angle = TWO_PI / npoints;
beginShape();
for (float a = 0; a < TWO_PI; a += angle) {
float sx = x + cos(a) * radius;
float sy = y + sin(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
Hope it'll help. Have fun!
You will have to use similar algorithms we use on pathfinding. you create a stack or queue that will hold a class storing the position of the hex and the number of moves left from that point, initially you insert your starting position with the number of moves you have and mark that hex as done ( to not re-use a position you have already been on ), then you pop an element, and you insert every neighbor of that hex with a number of moves -1. when you insert the hexes with zero moves, those are your endpoints. And before inserting any hex check if it's not already done.
I hope I was clear, your question was a bit vague but I tried to give you an idea of how these solutions are usually done, also I think your question fits more into algorithms rather then processing
Best of luck
Let me describe to you the context of my problem before I outline the issue. I'm currently in the middle of writing a game engine level editor and I'm working on the class that is going to act as the screen that the user interacts with in order to build their levels. I want to make it so the screen is proportional to the size of the editor.
The issue in question occurs when I begin resizing my screen and drawing on it at the same time. I draw from one thread and at the same time I'm editing the size of the raw pixel array that I'm drawing onto, from another thread (the EDT). I know this is a big no-no so naturally, with no safety in place, I get the occasional IndexOutOfBounds Exception on a resize.
My thought was, I could add a synchronize block on both the resizing code and the drawing code. This way, there would be no conflict and the issue should be avoided. However, the synchronization is being ignored completely. I'm still getting the same error and I'm really quite confused on why it isn't working. Below are the two methods of interest:
public void setPixel(int r, int g, int b, int x, int y) {
synchronized (pixels){
System.out.println("Start Draw...");
int color = (r << 16) | (g << 8) | b;
pixels[y * screenWidth + x] = color;
System.out.println("End Draw...");
}
}
#Override
public void componentResized(ComponentEvent e) {
synchronized (pixels) {
System.out.println("Start resize");
int width = e.getComponent().getWidth();
int height = e.getComponent().getHeight();
float aspectRatio = 4 / 3f;
if (width > height) {
width = (int) (height * aspectRatio);
} else if (height > width) {
height = width;
}
if (width < 0 || height < 0) {
width = 1;
height = 1;
}
this.screenWidth = width;
this.screenHeight = height;
image = new BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB);
pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
System.out.println("End Resize");
}
}
I don't know if it matters (it shouldn't right?) but my screen class extends a AWT Canvas. Also it is a listener on its parent component, so when that gets resized, it fires an event that triggers componentResized to be called. Anyway, thank you, any help is appreciated.
EDIT: My drawing code can be found below.
new Thread(new Runnable(){
#Override
public void run() {
while(true) {
for (int y = 0; y < screen.getHeight(); y++) {
for(int x = 0; x < screen.getWidth(); x++){
int r = (int) (Math.random() * 255);
int g = (int) (Math.random() * 255);
int b = (int) (Math.random() * 255);
screen.setPixel(r, g, b, x, y);
}
}
}
}
}).start();
There is something else I can think of. Not only what is in the comments of the question.
Let me modify first the setPixel method:
public void setPixel(int r, int g, int b, int x, int y) {
System.out.println("Called with: " + x + ", " + y); //Just added a print statement.
synchronized (mySyncGuard){
System.out.println("Start Draw...");
int color = (r << 16) | (g << 8) | b;
pixels[y * screenWidth + x] = color;
System.out.println("End Draw...");
}
}
Where mySyncGuard is a final property used as the synchronization guard to both setPixel and componentResized.
And now imagine the following scenario:
There is a loop which calls the setPixel method: this loop calls the method starting from x = 0 and y = 0 up to x < screenWidth and y < screenHeight! Like the following one:
for (int x = 0; x < screenWidth; ++x)
for (int y = 0; y < screenHeight; ++y) {
int r = calculateNextR(),
g = calculateNextG(),
b = calculateNextB();
setPixel(r, g, b, x, y);
}
Where calculateNextR(), calculateNextG() and calculateNextB() are methods that produce the next red, green and blue color components respectively.
Now, for example, let screenWidth be 200 and screenHeight be also 200 and at some point resized to 100x100.
Now the problem is that x and y would be lets say 150 and 150 respectively while the component was about to be resized to 100, 100.
Our custom setPixel method is now called with x==150 and y==150. But before the printing statement we added which shows the x and the y values, the componentResized manages to be called and take the lock of the synchronization guard property mySyncGuard! So componentResized is now doing its job changing the size of the image to 100x100, which, in turn, changes the pixels array to something smaller than the previous data array.
In parallel, setPixel now prints "Called with 150, 150" and waits at the synchronized block to obtain the lock of mySyncGuard, because componentResized has already currently obtained it and changing the image to 100x100 and the pixels array accordingly.
So now the pixels array is smaller, the componentResized finishes resizing and finally setPixel can obtain the lock of mySyncGuard.
And now is the problem: the array pixels is traversed at location 150,150 while it actually has size 100x100. So there you go! IndexOutOfBoundsException...
Conclusion:
We need more of your code to determine what's wrong.
The variables changing (such as screenWidth, screenHeight, image and pixels) need to be synchronized everywhere, not only inside the setPixel and componentResized methods. For example, if your case is like the scenario I just described, then unfortunately the for loops should also be in a synchronized (mySyncGuard) block.
This wouldn't fit in a comment. If you post some more code, then we can probably tell you what went wrong (and I may delete this answer if it is not needed).
I am working on a 2D platformer game for my, last, HS year project.
The game is basically about a player walking back & forward, collecting points and reaching goals... The player can shoot bullets and when bullets hit a block, it is destroyed. Now, I wanted to add an explosion effect using so called "particle" objects. I have written the manager class for it and it seemed to have worked the first time but after shooting a few times, i noticed that the particles stopped getting deleted, they just continue and travel out of screen. The life-time limit is 500ns.
I have also noticed that if i shoot bullets as soon as the game starts, the effect finishes as it is supposed to. but after waiting for a few more seconds and then shooting bullets, the effect particles do not behave as they should.
Here is what it looks like when i shoot bullets as soon as i start the game (What it's supposed to look like):
and here is what it looks like, after waiting a few seconds before shooting the bullets.
ParticleManager.java
public class ParticleManager {
private ArrayList<Particle> particles;
private ArrayList<Particle> removeParticles;
public ParticleManager() {
particles = new ArrayList<Particle>();
removeParticles = new ArrayList<Particle>();
}
public int getListSize() {
return particles.size();
}
/*
Generate particles
*/
public void genParticle(int x, int y, int amount) {
for(int i = 0; i < amount; i++) {
particles.add(new Particle("explosion" , x,y, i));
}
}
public void update() {
// Iterate trough particle objects
// update them & check for lifeTime
for(Particle p: particles) {
// Updating particle object before
// checking for time lapse
p.update();
// Append outdated particles to removeParticles
// if time limit has passed
if(System.nanoTime() - p.timePassed >= Config.particleLife) {
removeParticles.add(p);
}
}
// finally, delete all "remove-marked" objects
particles.removeAll(removeParticles);
}
public void render(Graphics2D g) {
for(Particle p: particles) {
p.render(g);
}
}
}
Particle.java
class Particle {
private double px, py, x, y;
private int radius, angle;
public long timePassed;
String type;
public Particle(String type, double x, double y, int angle) {
this.x = x;
this.y = y;
this.radius = 0;
this.angle = angle;
this.timePassed = 0;
this.type = type; // explosion, tail
}
public void update() {
px = x + radius * Math.cos(angle);
py = y + radius * Math.sin(angle);
radius += 2;
this.timePassed = System.nanoTime();
}
public void render(Graphics2D g) {
g.setColor(Color.WHITE);
g.fillOval((int)px, (int)py, 5, 5);
}
}
I haven't figured out what I am doing wrong here, I've googled about some stuff and at one point i came across an answer mentioning that some references don't get deleted directly for some reason...
and my question is "How can I make these particles vanish after a certain amount of time has passed? - as shown in the first GIF"
I think the problem is that you are constantly overwriting timePassed.
// Updating particle object before
// checking for time lapse
p.update();
// Append outdated particles to removeParticles
// if time limit has passed
if(System.nanoTime() - p.timePassed >= Config.particleLife) {
removeParticles.add(p);
}
p.update() sets timePassed to now and then the if check checks if time passed is far from now (it will never be since it was just set).
I think you do want to set timePassed in the constructor (maybe it would be better named timeCreated).
Additionally, just a heads up, you never clear removeParticles so that list is going to grow forever until it causes the process to run out of memory.
I've got a function that grabs the players X co-ords and then returns them, but its returning null for some reason I can't quite figure out. (Hence why I'm here).
The exact error I get is as follows:
java.lang.NullPointerException
at dev.colors.level.Level.getXOffset(Level.java:78)
All i do is call this, this is line 78:
if(player.getX() <= half_width){
This line come from this method:
public int getXOffset(){
int offset_x = 0;
//the first thing we are going to need is the half-width of the screen, to calculate if the player is in the middle of our screen
int half_width = (int) (Game.WINDOW_WIDTH/Game.SCALE/2);
//next up is the maximum offset, this is the most right side of the map, minus half of the screen offcourse
int maxX = (int) (map.getWidth()*32)-half_width;
//now we have 3 cases here
if(player.getX() <= half_width){
//the player is between the most left side of the map, which is zero and half a screen size which is 0+half_screen
offset_x = 0;
}else if(player.getX() > maxX){
//the player is between the maximum point of scrolling and the maximum width of the map
//the reason why we substract half the screen again is because we need to set our offset to the topleft position of our screen
offset_x = maxX-half_width;
}else{
//the player is in between the 2 spots, so we set the offset to the player, minus the half-width of the screen
offset_x = (int) (player.getX()-half_width);
}
return offset_x;
}
The getX method is here:
public abstract class LevelObject {
protected float x;
public LevelObject(float x, float y) {
System.out.println(x);
this.x = x;
this.y = y;
public float getX() {
System.out.println(this.x);
return x;
}
}
We declare LevelObject by creating a Player object:
player = new Player(128, 64);
Which then hands the two variables delcared there through the Player.java class, and then through the Character.java class:
public class Player extends Character {
public Player(float x, float y) throws SlickException {
super(x, y);
}
Character.java:
public abstract class Character extends LevelObject {
public Character(float x, float y) throws SlickException {
super(x, y);
}
}
Everything goes through properly up until we call getX (in the LevelObject class, even when I use the System.out to print "this.x" from LevelObject before we call getX it returns the correct variable, "128.0" as declared by the Player object.
To make things weirder, if i put some printlines inside the getX method, they don't show up in the console. It's as if it doesn't even run the method.
This doesn't make any sense to me, and I'm very very lost.
Apparently, the player variable is null.
The getXOffset() does not initialize a player variable, so I guess it must be a class field or something, which must be initialized in a different method.
Perhaps you need to add the this keyword to player initialization code (make it look like this.player = new Player(128, 64);) ?
Either way, your player variable is not initialized properly and that is the reason for that exception.
I'm trying to move an object on the click of a mouse while the object remains animated. There are a few similar posts on this website, and I've based my code off this answer:
An efficient algorithm to move ostrichs along a line at a constant speed
But I want to use a thread to keep the object animated. How should I do this? Here's my code:
public void movePlayer(Graphics g, int finalX, int finalY)
{
int length = finalX - xpos;
int height = finalY - ypos;
int oldXpos = xpos;
int oldYpos = ypos;
double speed = 20;
double distanceX = (length)/speed;
double distanceY = (height)/speed;
double distance = (Math.hypot(length,height));
double distanceTraveled = 0;
//This currently doesn't work:
move = new Thread(this);
{
while (distanceTraveled<distance)
{
//move the object by increments
xpos += distanceX;
ypos += distanceY;
distanceTraveled = Math.hypot(xpos-oldXpos, ypos - oldYpos);
drawPlayer(img, g);
for(int x = 0; x < 100000; x ++);
}
}
}
If this is Swing, why not simply use a MouseListener to help you drag the object? If you want to animate separate from the mouse, don't use a while(true) loop unless you want to freeze the event thread. Use a Swing Timer instead. If this isn't Swing, tell us more details (shoot, do this anyway)!