I'm not sure if I can express this question correctly, but here it goes..
I want to code an example, where small dots have a velocity according to which they move - but also, there is a random motion superimposed to the "proper" motion. Using the Processing code below, I get the following animation:
The right dot is supposed to be going towards the bottom right corner, and I'm OK with how it behaves. The problem is the left dot, which is supposed to be "static" - so it would only show the "random" motion "in place"; however, as the animated .gif shows, it tends to end up veering some distance away from its original location. The random velocity is calculated with:
this.randspeed.set(random(0,1)-0.5, random(0,1)-0.5);
I would have guessed that random(0,1)-0.5 doesn't give me a Gaussian-like "normal distribution" centered around (or converging? to) zero; but then again, even if it was a "proper" Gaussian, I still could have such "luck" so that say, positive values [0:0.5) are returned for a whole day, and then negative values [-0.5:0) are returned the next day, and in the end, it would still be a proper Gaussian.
So, I guess, I'm looking for a way to convert a (pseudo?)-random sequence (as the one generated by random(0,1)-0.5) to a pseudo-random one, but in which the average sum of N samples (say, 10) is 0. I'm not sure how to call this - a random sequence periodically converging to zero, I guess??
Note that I've been trying below with both changing position directly; and saving position with changing finalpos instead - changing the position seems more like a "natural", smoothed motion (especially with the modulo frame operation, so a new random velocity isn't assigned every frame); but then, it also allows that the random noise accumulates, and "pushes" the dot away from its central location. Also, note that it took me a few takes until I could reproduce this on the .gif, running the program "live" seems to cause the dot to diverge from the original location more quickly (I had read something about hardware events like hard-disk writes being used for changing entropy for /dev/random on Linux, but I don't really know if it's related).
Also, I thought of setting some sort of a virtual border around the dot position, and having a collision detection for the random motion going out of the border - but that seems to me like too much work (and CPU cycles for vector operations) for this kind of thing; I would have hoped that the random function can somehow be "tempered" in an easier manner, instead.
So, would there be a recommended way to approach this kind of random motion around a central location in a limited area?
marbles.pde:
import java.util.*; // added for Iterator;
ArrayList<Marble> marbles = new ArrayList<Marble>();
Iterator<Marble> imarb;
color mclr = #0000FF;
int RNDLIMIT = 2;
int framecount = 0;
void setup() {
size(600,400,P2D);
Marble m_moving = new Marble(width/2, height/2, 2, 2);
marbles.add(m_moving);
Marble m_stopped = new Marble(width/2-100, height/2, 0, 0);
marbles.add(m_stopped);
}
void draw() {
background(255);
strokeWeight(1);
stroke(mclr);
fill(mclr);
imarb = marbles.iterator();
while (imarb.hasNext()) {
Marble m = imarb.next();
m.update();
ellipse(m.finalpos.x, m.finalpos.y, m.radius*2, m.radius*2);
}
framecount++;
//~ saveFrame("marbles-######.png");
}
class Marble {
PVector position = new PVector(0,0);
PVector finalpos = new PVector(0,0);
PVector speed = new PVector(0,0);
PVector randspeed = new PVector(0,0);
float radius=4;
public Marble() {
}
public Marble(float inx, float iny, float invx, float invy) {
this.position.set(inx, iny);
this.speed.set(invx, invy);
}
public void update() {
this.position.add(this.speed);
if (framecount % 4 == 0) {
this.randspeed.set(random(0,1)-0.5, random(0,1)-0.5);
this.randspeed.setMag(RNDLIMIT);
}
int algoTry = 1; // 0
switch(algoTry) {
case 0:
this.finalpos.set(PVector.add(this.position, this.randspeed));
break;
case 1:
this.position.set(PVector.add(this.position, this.randspeed));
this.finalpos.set(this.position);
break;
}
}
}
A typical 'random walk' will always meander away, because statistics don't 'balance'. Moving a lot to the left will not be corrected with movement to the right. So quality of the randomness isn't the issue.
If you want the dot to stay around a specific location, you should store that location and make the "proper" motion (as you called it) always move towards that location. Some subtraction of current location from target location should get you the correct "proper" motion. With this solution, the dot will always be inclined to head back to where is started.
Well, I think I got somewhere; thanks to the comment by #Teepeemm, I learned about Ornstein - Uhlenbeck process, and also that Brownian motion: "is described by the Wiener process ... one of the best known Lévy processes". Rereading Ornstein - Uhlenbeck process ("Over time, the process tends to drift towards its long-term mean ... is a prototype of a noisy relaxation process ...the length x(t) of the spring will fluctuate stochastically around the spring rest length x0;"), I realized it is not what I want - it would have caused my dot eventually to settle in the central position, and then I would have had to "ping" it every once in a while.
Just as I realized that it would take me forever to fist understand, and then code, those processes - I found this:
Generation of noise with given PSD - Newsreader - MATLAB Central
I want to generate noise data with especific frequency
characteristics: That is, the power spectral density (PSD) has to be
proportional to f^0, f, f^2 etc.
f^0 -- use randn
f^(-2) -- low-pass filter the f^0 time series, or integrate with cumsum
f^2 -- differentiate, as with diff
... so I thought, maybe I can somehow process the raw random numbers, to get a "distribution" as I want. So I came up with a Processing patch, which you'll find below as rndquest2.pde. Processing makes it easy to use alpha colors for points, and if the background is not erased, they accumulate - so it's easier to see what is the actual distribution of a random output being tweaked. I got this image:
The "choice 0" seems to point out that random() generates a sequence with uniform distribution (white noise). For instance, "choice 1" would cause the dot to tend to stick on the edges; "choice 2" quite obviously shows folding ; and I'd prefer a circle, too. In the end, I got something most resembling a Gauss (most frequent in the center, and slowly diminishing to the edges) on "choice 9", by something like a radial folding, I guess. There's still a visible threshold border on "choice 9", but if it is implemented in the code above in OP, then I get something like this:
... which is, actually, as I wanted it! (not sure why the start came out as it did, though) The trick is that the random vector, once limited/processed, should be interpreted as a position (or rather, should be added to the position, to obtain a new position, used to calculate a new velocity for finalpos); it should not be directly added to the speed/velocity!
So, only these changes need to be added in the OP code:
...
float r1 =0, r2 = 0;
PVector rv = new PVector(r1, r2);
float radius = 10;
float pr1 =0; int pr3 =0;
...
int signum(float f) {
if (f > 0) return 1;
if (f < 0) return -1;
return 0;
}
float getRandom() {
float ret;
ret = random(-radius,radius);
return ret;
}
void getRandomVect() {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
pr1 = rv.mag()-radius/2;
pr3 = int(radius-rv.mag());
pr3 = (pr3 == 0) ? 1 : pr3;
if (pr1>0) {
r1 = rv.x - random(1)*2*signum(rv.x)*pr3;
r2 = rv.y - random(1)*2*signum(rv.y)*pr3;
}
rv.set(r1,r2);
}
...
public void update() {
this.position.add(this.speed);
if (framecount % 4 == 0) {
getRandomVect();
this.randspeed.set(PVector.div(PVector.sub(PVector.add(this.position, rv), this.finalpos), 4));
}
this.finalpos.set(PVector.add(this.finalpos, this.randspeed));
}
...
... to get it working as shown on the gif in this post.
Well, hope this helps someone,
Cheers!
rndquest2.pde
PVector mainpos = new PVector(200.0, 200.0);
float radius = 50;
float x1 =0, y1 = 0;
float r1 =0, r2 = 0;
float pr1 =0, pr2 = 0;
int pr3 =0, pr4 = 0;
PVector rv = new PVector(r1, r2);
color clr = color(0,0,255,30);
int choice = 0;
int framecount = 0;
void setup() {
size(600,400,P2D);
background(255);
textSize(14);
textAlign(LEFT, TOP);
}
void draw() {
try {
strokeWeight(2);
stroke(clr); // #0000FF web colors only
fill(clr);
point(mainpos.x, mainpos.y);
r1 = getRandom();
r2 = getRandom();
switch(choice) {
case 0:
x1 = mainpos.x + r1;
y1 = mainpos.y + r2;
println("0"); // these help trigger the draw(), apparently..
break;
case 1:
rv.set(r1,r2);
if(rv.mag() > radius) {
rv.setMag(radius);
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("1");
break;
case 2:
rv.set(r1,r2);
if(rv.mag() > radius) {
rv.sub(PVector.mult(rv,0.1*(rv.mag()-radius)));
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("2");
break;
case 3:
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("3");
break;
case 4:
pr1 = rv.x;
pr2 = rv.y;
rv.set(r1-pr1,r2-pr2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1-pr1,r2-pr2);
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("4");
break;
case 5:
pr1 = rv.x;
pr2 = rv.y;
rv.set(r1-pr1,r2-pr2);
if(rv.mag() > radius) {
rv.mult(1.0/(rv.mag()-radius));
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("5");
break;
case 6:
pr1 = (pr1 + r1)/2.0;
pr2 = (pr2 + r2)/2.0;
rv.set(pr1,pr2);
if(rv.mag() > radius) {
rv.mult(1.0/(rv.mag()-radius));
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("6");
break;
case 7:
r1 = (pr1 + r1)/2.0;
r2 = (pr2 + r2)/2.0;
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
r1 = (pr1 + r1)/2.0;
r2 = (pr2 + r2)/2.0;
rv.set(r1,r2);
}
pr1 = rv.x;
pr2 = rv.y;
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("7");
break;
case 8:
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
//~ pr1 = abs(rv.x)-radius/2;
//~ pr2 = abs(rv.y)-radius/2;
pr1 = rv.mag()-radius/2;
//~ pr3 = int(radius-abs(rv.x));
//~ pr4 = int(radius-abs(rv.y));
pr3 = int(radius-pr1);
pr3 = (pr3 == 0) ? 1 : pr3;
//~ pr4 = (pr4 == 0) ? 1 : pr4;
if (pr1>0)
r1 = rv.x - random(1)*2*signum(rv.x)*pr1; //framecount ; b2i(int(random(radius)) % pr3 == 0)*
if (pr1>0) //(pr2>0)
r2 = rv.y - random(1)*2*signum(rv.y)*pr1;//pr2;
rv.set(r1,r2);
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("8");
break;
case 9:
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
pr1 = rv.mag()-radius/2;
pr3 = int(radius-rv.mag()); //pr1);
pr3 = (pr3 == 0) ? 1 : pr3;
if (pr1>0) {
r1 = rv.x - random(1)*2*signum(rv.x)*pr3; //framecount ; b2i(int(random(radius)) % pr3 == 0)*
r2 = rv.y - random(1)*2*signum(rv.y)*pr3;//pr2;
//~ r1 = rv.x - 2*signum(rv.x)*pr3; //like an X for pr3 = int(radius-pr1);
//~ r2 = rv.y - 2*signum(rv.y)*pr3;
}
rv.set(r1,r2);
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("9");
break;
}
// note: patch does not draw point(mainpos.x + getRandom(), ..)
point(x1, y1);
fill(255);
stroke(255); //~ stroke(255,0,0);
rect(mainpos.x-radius,100,mainpos.x-radius+100,20);
fill(0,0,255);
stroke(clr);
text(String.format("choice %d (f:%d)", choice, framecount), mainpos.x-radius, 100);
framecount++;
if (framecount % 5000 == 0) {
saveFrame(String.format("rndquest2-%d-%d-######.png", choice, framecount));
}
} catch(Exception e) {
e.printStackTrace();
}
}
int signum(float f) {
if (f > 0) return 1;
if (f < 0) return -1;
return 0;
}
int b2i(boolean inb) {
if (inb) return 1;
else return 0;
}
float getRandom() {
float ret;
ret = random(-radius,radius);
return ret;
}
void mousePressed() {
choice = (choice + 1) % 10;
background(255);
framecount = 0;
}
If you want random movement within a certain distance of an "actual" point, you could try having a fixed, maximum-distance from the "actual" location, and not allowing the ball outside of that radius.
If you don't want a hard limit, you could add some kind of force that attracts the object toward its "actual" location, and make it increase with the distance from that point linearly, quadratically, or by some other function of your choosing. Then the object would be free to move around its "actual" location, but still be kept relatively nearby.
You are simulating a random walk. Generally, a random walk after n steps will be on the order of sqrt(n) from where it started (more specifically, it will obey the Law of the Iterated Logarithm, so that its magnitude after n steps is O(sqrt(n log log n))). Which is a long way of saying that the walk will wander away as time goes on (but because it's two dimensional, it will eventually return to the origin).
To solve this, you want to have a drift back toward the origin. One random process which has this property is the Ornstein - Uhlenbeck process, which has a drift toward the origin that is proportional to its distance from the origin. (And the random part of the random walk would still cause it to wiggle around its origin.)
This could be accomplished in your original code by something along the lines of
double driftScale = .01;
double wiggleScale = 1;
Point origin = new Point(0,0);
...
this.randspeed.set(driftScale*(origin.x-this.position.x)+wiggleScale*(random(0,1)-.5),
driftScale*(origin.y-this.position.y)+wiggleScale*(random(0,1)-.5));
It would be better to replace random(0,1)-.5 with a standard normal Gaussian, but I don't know how noticeable that affect would be. The biggest difference is that with the current code, there is a maximum distance the point can get from its start. With a Gaussian, it could theoretically get arbitrarily far (but it would still drift back to the origin).
I'm also not quite sure how much this matches with your eventual solution. I'm having trouble following your logic (using PVector and 10 cases didn't help).
Related
I'm quite new to programming though following a bunch of tutorials I've ended up with this code to deal with the pathfinding of a small game I'm trying to make.
If works for small and straight paths but not for complex routes (it freezes and closedSet.size() gets larger than 70000 in a grid that is only 54 * 46).
Note that wall is true depending on the height of the colliding tiles, so it may be true coming from a point but false coming from another. Is that the problem?
import java.util.*;
int heuristic(int x,int y,int x_,int y_){
int dstX = abs(x - x_);
int dstY = abs(y - y_);
if(dstX > dstY){
return 14*dstY + 10*(dstX - dstY);
}else{
return 14*dstX + 10*(dstY - dstX);
}
}
boolean wall(int x, int y, int x_, int y_){
Tile tileS = getTile(x, y);
Tile tileCurr = getTile(x_, y_);
if(abs(tileS.altitude - tileCurr.altitude) > 1 || tileS.altitude < 1){
return true;
}else{
return false;
}
}
ArrayList<PVector> findPath(int startx, int starty, int endx, int endy){
Queue<Spot> openSet = new PriorityQueue<Spot>(fComparator);
ArrayList<Spot> closedSet = new ArrayList<Spot>();
Spot start = new Spot(startx, starty);
Spot end = new Spot(endx, endy);
Spot current = start;
openSet.add(start);
while(!openSet.isEmpty()){
current = openSet.poll();
closedSet.add(current);
println(closedSet.size());
if (current.x == end.x && current.y == end.y) {
break;
}
ArrayList<Spot> successors = new ArrayList<Spot>();
for(int i = 0; i < collidingTiles.size(); i++){
JSONObject difference = collidingTiles.getJSONObject(i);
/*JSONArray such as
[{x: -1, y: -1},{x: 0, y: -1},...](not including {x:0, y:0})
*/
int x_ = difference.getInt("x");
int y_ = difference.getInt("y");
int x = x_ + current.x;
int y = y_ + current.y;
if(x >= 0 && x <= map.columns && y >= 0 && y <= map.rows){
Spot s = new Spot(x, y);
successors.add(s);
}
}
for(Spot s: successors){
if (!closedSet.contains(s) && !wall(s.x, s.y, current.x, current.y)) {
int tempG = current.g + heuristic(s.x, s.y, current.x, current.y);
if(tempG < s.g || !openSet.contains(s)){
s.g = tempG;
s.h = heuristic(s.x, s.y, end.x, end.y);
s.f = s.g + s.h;
s.parent = current;
if (!openSet.contains(s)) {
openSet.add(s);
}
}
}
}
successors.clear();
}
ArrayList<PVector> path = new ArrayList<PVector>();
Spot temp = current;
PVector tile = new PVector(temp.x + 0.5, temp.y + 0.5);
path.add(tile);
while (temp.parent != null) {
tile = new PVector(temp.parent.x + 0.5, temp.parent.y + 0.5);
path.add(0, tile);
temp = temp.parent;
}
return path;
}
class Spot{
int x, y;
int f, g, h = 0;
Spot parent;
Spot(int x_, int y_){
x = x_;
y = y_;
}
}
Comparator<Spot> fComparator = new Comparator<Spot>() {
#Override
int compare(Spot s1, Spot s2) {
return s1.f - s2.f;
}
};
Any recommendations or minor changes are also appreciated.
closedSet.size() gets larger than 70000 in a grid that is only 54 * 46
Your code does implement some logic that says
"if a node is closed, don't process it again", and
"if the node is already in the open set, compare G scores"
But in both cases it does not work, because Spot does not implement equals, and therefore contains is comparing for reference equality and it will always be false. So, implement Spot.equals. Specifically, make it compare only x and y, because f/g/h/parent are allowed to be different for nodes that are considered equal for this purpose.
Even when it works, using contains on an ArrayList and a PriorityQueue is not so good for performance. For the closed list, it is easy to use a HashSet (of course, also implement Spot.hashCode, in some way that depends only on x and y). For the open list, getting rid of slow contains is more work. One trick you can use is manually maintaining a binary heap, and additionally have a HashMap which maps an x,y pair to the index in the heap where the corresponding node is located. The reason for manually maintaining a heap is that the HashMap must be updated whenever nodes are moved in the queue, and the normal PriorityQueue does not have such functionality.
The way that finding successors works also worries me from a performance perspective, but I cannot see the details.
Note that wall is true depending on the height of the colliding tiles, so it may be true coming from a point but false coming from another. Is that the problem?
That's fine, A* can tolerate a spot being reachable from one side but not an other. What it cannot natively take into account is if the direction a spot was reached from affects which successors that node has, but that does not happen here.
I am looking for some help with some game code i have inherited from a flight sim. The code below simulates bombs exploding on the ground, it works fine but i am trying to refine it.
At the moment it takes a random value for x and y as a start point and then adds another random value between -20 and 20 to this. It works ok, but doesn't simulate bombs dropping very well as the pattern does not lay along a straight line/
What i would like to achieve though is all x and y points after the first random values, to lay along a straight line, so that the effects called for all appear to lay in a line. It doesn't matter which way the line is orientated.
Thanks for any help
slipper
public static class BombUnit extends CandCGeneric
{
public boolean danger()
{
Point3d point3d = new Point3d();
pos.getAbs(point3d);
Vector3d vector3d = new Vector3d();
Random random = new Random();
Aircraft aircraft = War.GetNearestEnemyAircraft(this, 10000F, 9);
if(counter > 10)
{
counter = 0;
startpoint.set(point3d.x + (double)(random.nextInt(1000) - 500), point3d.y + (double)(random.nextInt(1000) - 500), point3d.z);
}
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
World.MaxVisualDistance = 50000F;
counter++;
String s = "weapon.bomb_std";
startpoint.x += random.nextInt(40) - 20;
startpoint.y += random.nextInt(40) - 20;
Explosions.generate(this, startpoint, 7F, 0, 30F, !Mission.isNet());
startpoint.z = World.land().HQ(startpoint.x, startpoint.y);
MsgExplosion.send(this, s, startpoint, getOwner(), 0.0F, 7F, 0, 30F);
Engine.land();
int i = Landscape.getPixelMapT(Engine.land().WORLD2PIXX(startpoint.x), Engine.land().WORLD2PIXY(startpoint.y));
if(firecounter < 100 && i >= 16 && i < 20)
{
Eff3DActor.New(null, null, new Loc(startpoint.x, startpoint.y, startpoint.z + 5D, 0.0F, 90F, 0.0F), 1.0F, "Effects/Smokes/CityFire3.eff", 300F);
firecounter++;
}
super.setTimer(15);
}
return true;
}
private static Point3d startpoint = new Point3d();
private int counter;
private int firecounter;
public BombUnit()
{
counter = 11;
firecounter = 0;
Timer1 = Timer2 = 0.05F;
}
}
The code in the question is a mess, but ignoring this and trying to focus on the relevant parts: You can generate a random position for the first point, and a random direction, and then walk along this direction in several steps.
(This still raises the question of whether the direction is really not important. Wouldn't it matter if only the first bomb was dropped in the "valid" area, and the remaining ones outside of the screen?)
However, the relevant code could roughly look like this:
class Bombs
{
private final Random random = new Random(0);
int getScreenSizeX() { ... }
int getScreenSizeY() { ... }
// Method to drop a single bomb at the given position
void dropBombAt(double x, double y) { ... }
void dropBombs(int numberOfBombs, double distanceBetweenBombs)
{
// Create a random position in the screen
double currentX = random.nextDouble() * getScreenSizeX();
double currentY = random.nextDouble() * getScreenSizeY();
// Create a random step size
double directionX = random.nextDouble();
double directionY = random.nextDouble();
double invLength = 1.0 / Math.hypot(directionX, directionY);
double stepX = directionX * invLength * distanceBetweenBombs;
double stepY = directionY * invLength * distanceBetweenBombs;
// Drop the bombs
for (int i=0; i<numberOfBombs; i++)
{
dropBombAt(currentX, currentY);
currentX += stepX;
currentY += stepY;
}
}
}
I am assuming your startpoint is a StartPoint class with x,y,z coordinates as integers in it.
I hope I have understood your problem correctly. It looks like you either want to create a vertical explosion or a horizontal explosion. Since an explosion always occurs on ground, the z coordinate will be zero. Now you can vary one of x or y coordinate to give you a random explosion along a straight line. Whether you choose x or y could be fixed or could be randomized itself. A potential randomized solution below:
public boolean danger() {
// stuff
int orientation = Random.nextInt(2);
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
// stuff
startPoint = randomizeStartPoint(orientation, startPoint);
// stuff
}
}
StartPoint randomizeStartPoint(int orientation, StartPoint startPoint) {
if(orientation == 0) {
startPoint.x += random.nextInt(40) - 20;
}
else {
startPoint.y += random.nextInt(40) - 20;
}
return startPoint;
}
In response to the image you uploaded, it seems that the orientation of the explosion need not necessarily be horizontal or vertical. So the code I posted above gives a limited solution to your problem.
Since you want any random straight line, your problem boils down to two sub parts:
1. Generate a random straight line equation.
2. Generate random point along this line.
Now, a straight line equation in coordinate geometry is y = mx + c where m is the slope and c is the constant where the line crosses the y-axis. The problem with c is that it gives rise to irrational coordinates. I am assuming you are looking for integer coordinates only, since this will ensure that your points are accurately plotted. (You could do with rational fractions, but then a fraction like 1/3 will still result in loss of accuracy). The best way to get rid of this irrational problem is to get rid of c. So now your straight line always looks like y = mx. So for step one, you have to generate a random m.
Then for step 2, you can either generate a random x or random y. It doesn't matter which one, since either one will result in random coordinates.
Here is a possible code for the solution:
int generateRandomSlope() {
return Random.nextInt(100); // arbitrarily chose 100.
}
int randomizeStartPoint(int m, StartPoint startPoint) { // takes the slope we generated earlier. without the slope, your points will never be on a straight line!
startPoint.x += random.nextInt(40) - 20;
startPoint.y += x * m; // because a line equation is y = mx
return startPoint;
}
public boolean danger() {
// stuff
int m = generateRandomSlope(); // you may want to generate this elsewhere so that it doesn't change each time danger() is called.
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
// stuff
startPoint = randomizeStartPoint(m, startPoint);
// stuff
}
}
Again, this is not a complete or the best solution.
I just started a new 2D game using Java, LWJGL, and Slick Util but I can't seem to figure out a good way to make collision detection.
If I wanted to, it would be easy to detect collision between 2 entities with the Rectangle intersect method, but it can only check the collision with a certain area you specify.
I have thought that I could make a list of every entity and its coordinates as its created and then run the intersect method through the list, but then it would check for collision with every entity on the entire map for every time the game updated and I think that would be too inefficient.
Does anyone know a more efficient way to create collision detection? If there was some way i could check if there was an entity at every point the character moved that would probably be the best.
If I have not enough information or I made this sound too confusing please tell me and I can try to clarify things. Also as a side question, what are the benefits of using slick util or slick 2D over one another. Thanks for the help!
The usual way to solve this is a scene graph, a hierarchical system of the objects of the game world.
You might want to look at this and this.
Shortened: you logically group your objects under nodes and assign the nodes a bounding rectangle that encompasses all its sub-nodes and leaves(objects). Everything is grouped again under one main node to access the tree. Now you can test a object for collision with a node, usually starting from the main node. If you get a hit you check its sub-nodes and leaves.
This will take some time to implement but can cut down on CPU usage if the tree structure/grouping is done right. It has also the benefit that you can implement local transforms which makes moving objects relative to each other easier.
Because I hate "The usual way", I made an array of all the coordinates and then checked if a single point hit the coordinate.
Here is a slight modification of my code to demonstrate (It is in 3D):
for (CannonBall can : GameServer.ballss){ //Go through all cannonballs
if (can.owner != cl){ //Can.owner is the ship, cl is the player the cannonball is being checked with to see if colliding.
int distancex = (int) (can.x - cl.z);
int distancez = (int) (can.z - cl.x);
final int distancey = (int) (can.y - cl.y);
double xRot = Math.cos(Math.toRadians(cl.rotation)) * (distancex - 0) - Math.sin(Math.toRadians(cl.rotation)) * (distancez - 0) + 0;
double zRot = Math.sin(Math.toRadians(cl.rotation)) * (distancex - 0) - Math.cos(Math.toRadians(cl.rotation)) * (distancez - 0) + 0;
distancex = (int) xRot;
distancez = (int) zRot;
try{
if (true){ //Skip different coordinates for different ships for demonstration purposes
i = GameServer.coords[GameServer.DELTA + distancex][GameServer.DELTA + distancez][GameServer.DELTA + (distancey)];
}
if (i == 1){
if (can.owner != cl){
remcan.add(can);
if (can.type == 0){
double damage = (100 + Math.random()*25);
if (cl.type == 1){
damage/=2;
}
if (cl.type == 2){
damage*=2;
}
cl.damage-=damage;
}
if (can.type == 1){
double damage = (Math.random() * 500);
if (cl.type == 1){
damage/=2;
}
if (cl.type == 2){
damage*=2;
}
cl.damage-=damage;
}else{
double damage = (100 + Math.random()*25);
if (cl.type == 1){
damage/=2;
}
if (cl.type == 2){
damage*=2;
}
cl.damage-=damage;
}
crash = true;
if (cl.damage < 1){
if (!cl.sinking){
cl.sinking = true;
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
GameServer.coords is an int[][][], which is given coordinates like so:
public static int[][][] coords;
public void CollisionSetup(){
try{
File f = new File("res/coords.txt");
String coords = readTextFile(f.getAbsolutePath());
for (int i = 0; i < coords.length();){
int i1 = i;
for (; i1 < coords.length(); i1++){
if (String.valueOf(coords.charAt(i1)).contains(",")){
break;
}
}
String x = coords.substring(i, i1).replace(",", "");
i = i1;
i1 = i + 1;
for (; i1 < coords.length(); i1++){
if (String.valueOf(coords.charAt(i1)).contains(",")){
break;
}
}
String y = coords.substring(i, i1).replace(",", "");;
i = i1;
i1 = i + 1;
for (; i1 < coords.length(); i1++){
if (String.valueOf(coords.charAt(i1)).contains(",")){
break;
}
}
String z = coords.substring(i, i1).replace(",", "");;
i = i1 + 1;
//buildx.append(String.valueOf(coords.charAt(i)));
////System.out.println(x);
////System.out.println(y);
////System.out.println(z);
//x = String.valueOf((int)Double.parseDouble(x));
//y = String.valueOf((int)Double.parseDouble(y));
//z = String.valueOf((int)Double.parseDouble(z));
double sx = Double.valueOf(x);
double sy = Double.valueOf(y);
double sz = Double.valueOf(z);
javax.vecmath.Vector3f cor = new javax.vecmath.Vector3f(Float.parseFloat(x), Float.parseFloat(y), Float.parseFloat(z));
//if (!arr.contains(cor)){
if (cor.y > 0)
arr.add(new javax.vecmath.Vector3f(cor));
if (!ship.contains(new Vector3f((int) sx, (int) sy, (int) sz)))
ship.add(new Vector3f((int) sx, (int) sy, (int) sz));
Float.parseFloat(z)));
}
}
public void setUpPhysics() {
//coords = new int[20][20];
coords = new int[80][80][80];
coords1 = new int[80][80];
//coords[-5 + DELTA][7 + DELTA] = 11;
for (javax.vecmath.Vector3f vec : arr){
coords[DELTA+(int) vec.x][DELTA+(int) vec.z][DELTA + (int) vec.y] = 1; //This is line 124
coords1[DELTA+(int) vec.x][DELTA+(int) vec.z] = 1;
}
}
Though it has limitations on collision interaction, it works for cannonballs colliding with a ship and checking the front of a ship to see if it has hit another ship. Also, it uses barely any CPU.
No idea on the opinions of other programmers on such a method.
I'm Using Cocos2d Android.
I have a scrolling background in Cocos2d, i use this method to make it work :
public void moveBackground(float dt) {
bg1.setPosition(CGPoint.ccp(bg1.getPosition().x, bg1.getPosition().y
- speed));
bg2.setPosition(CGPoint.ccp(bg2.getPosition().x, bg2.getPosition().y
- speed));
if (bg1.getPosition().y < -(bg1.getBoundingBox().size.height)) {
bg1.setPosition(CGPoint.ccp(bg1.getPosition().x,
bg2.getPosition().y + bg2.getBoundingBox().size.height - 1));
}
if (bg2.getPosition().y < -(bg2.getBoundingBox().size.height)) {
bg2.setPosition(CGPoint.ccp(bg2.getPosition().x,
bg1.getPosition().y + bg1.getBoundingBox().size.height - 1));
}
}
speed = 5 or 10 or whatever. It is called thanks to a schedule, every frame.
I want to drop targets with the same speed that my background uses to scroll, to add targets i use this method :
public void addTargetFibre(float dt) {
Random rand = new Random();
CCSprite target = CCSprite.sprite(RessourcesManager.Players.blue_ball);
target.setScale(0.3f);
CGSize winSize = CCDirector.sharedDirector().displaySize();
int actualX = (int) a;
int n = rand.nextInt(4);
switch (n) {
case 0:
actualX = (int) a;
break;
case 1:
actualX = (int) b;
break;
case 2:
actualX = (int) c;
break;
case 3:
actualX = (int) d;
break;
}
target.setPosition(actualX, winSize.height
+ (target.getContentSize().height / 2.0f));
addChild(target, 1);
float d = ((win_size.height+target.getContentSize().height/2)/(60*speed));
d = d+(d*(float)1/60);
CCMoveTo actionMove = CCMoveTo.action(d,
CGPoint.ccp(actualX, -target.getContentSize().height / 2.0f));
CCCallFuncN actionMoveDone = CCCallFuncN.action(this,
"removeSprite");
CCSequence actions = CCSequence.actions(actionMove, actionMoveDone);
target.runAction(actions);
}
What i want is the duration that i need to put in CCMoveTo action.
There are 2 possibilities:
Background movement speed is constant, as you have it right now. Don't use dt in addTargetFibre, because you don't want to call it every frame. If you know the initialPosition, finalPosition and speed, just write a procedure that calculates the distance between 2 points (ccpDistance exists in the iOS version of Cocod2D, I'm sure there's a corespondent for Android)
int d = ccpDistance(intialPosition, finalPosition)/speed;
CCMoveTo actionMove = CCMoveTo.action(d, finalPosition);
Background movement speed is variable (ex: player controls background movement from joystick). If that is the case, you will have to move the sprites every frame, and you will have to have a method to call from your update to fix the position.
public void updateTargetFibres(float dt)
The formula for updating the position every frame is
sprite.position = CGPointMake(sprite.position.x, sprite.position.y - speed*dt)
In this method, you can also check if your sprite has left the screen by checking the y of the position, and if it has, then delete it from the parent.
I have a problem that I can't seem to get a working algorithm for, I've been trying to days and get so close but yet so far.
I want to draw a triangle defined by 3 points (p0, p1, p2). This triangle can be any shape, size, and orientation. The triangle must also be filled inside.
Here's a few things I've tried and why they've failed:
1
Drawing lines along the triangle from side to side
Failed because the triangle would have holes and would not be flat due to the awkwardness of drawing lines across the angled surface with changing locations
2
Iterate for an area and test if the point falls past the plane parallel to the triangle and 3 other planes projected onto the XY, ZY, and XZ plane that cover the area of the triangle
Failed because for certain triangles (that have very close sides) there would be unpredictable results, e.g. voxels floating around not connected to anything
3
Iterate for an area along the sides of the triangle (line algorithm) and test to see if a point goes past a parallel plane
Failed because drawing a line from p0 to p1 is not the same as a line from p1 to p0 and any attempt to rearrange either doesn't help, or causes more problems. Asymmetry is the problem with this one.
This is all with the intent of making polygons and flat surfaces. 3 has given me the most success and makes accurate triangles, but when I try to connect these together everything falls apart and I get issues with things not connecting, asymmetry, etc. I believe 3 will work with some tweaking but I'm just worn out from trying to make this work for so long and need help.
There's a lot of small details in my algorithms that aren't really relevant so I left them out. For number 3 it might be a problem with my implementation and not the algorithm itself. If you want code I'll try and clean it up enough to be understandable, it will take me a few minutes though. But I'm looking for algorithms that are known to work. I can't seem to find any voxel shape making algorithms anywhere, I've been doing everything from scratch.
EDIT:
Here's the third attempt. It's a mess, but I tried to clean it up.
// Point3i is a class I made, however the Vector3fs you'll see are from lwjgl
public void drawTriangle (Point3i r0, Point3i r1, Point3i r2)
{
// Util is a class I made with some useful stuff inside
// Starting values for iteration
int sx = (int) Util.min(r0.x, r1.x, r2.x);
int sy = (int) Util.min(r0.y, r1.y, r2.y);
int sz = (int) Util.min(r0.z, r1.z, r2.z);
// Ending values for iteration
int ex = (int) Util.max(r0.x, r1.x, r2.x);
int ey = (int) Util.max(r0.y, r1.y, r2.y);
int ez = (int) Util.max(r0.z, r1.z, r2.z);
// Side lengths
float l0 = Util.distance(r0.x, r1.x, r0.y, r1.y, r0.z, r1.z);
float l1 = Util.distance(r2.x, r1.x, r2.y, r1.y, r2.z, r1.z);
float l2 = Util.distance(r0.x, r2.x, r0.y, r2.y, r0.z, r2.z);
// Calculate the normal vector
Vector3f nn = new Vector3f(r1.x - r0.x, r1.y - r0.y, r1.z - r0.z);
Vector3f n = new Vector3f(r2.x - r0.x, r2.y - r0.y, r2.z - r0.z);
Vector3f.cross(nn, n, n);
// Determines which direction we increment for
int iz = n.z >= 0 ? 1 : -1;
int iy = n.y >= 0 ? 1 : -1;
int ix = n.x >= 0 ? 1 : -1;
// Reorganize for the direction of iteration
if (iz < 0) {
int tmp = sz;
sz = ez;
ez = tmp;
}
if (iy < 0) {
int tmp = sy;
sy = ey;
ey = tmp;
}
if (ix < 0) {
int tmp = sx;
sx = ex;
ex = tmp;
}
// We're we want to iterate over the end vars so we change the value
// by their incrementors/decrementors
ex += ix;
ey += iy;
ez += iz;
// Maximum length
float lmax = Util.max(l0, l1, l2);
// This is a class I made which manually iterates over a line, I already
// know that this class is working
GeneratorLine3d g0, g1, g2;
// This is a vector for the longest side
Vector3f v = new Vector3f();
// make the generators
if (lmax == l0) {
v.x = r1.x - r0.x;
v.y = r1.y - r0.y;
v.z = r1.z - r0.z;
g0 = new GeneratorLine3d(r0, r1);
g1 = new GeneratorLine3d(r0, r2);
g2 = new GeneratorLine3d(r2, r1);
}
else if (lmax == l1) {
v.x = r1.x - r2.x;
v.y = r1.y - r2.y;
v.z = r1.z - r2.z;
g0 = new GeneratorLine3d(r2, r1);
g1 = new GeneratorLine3d(r2, r0);
g2 = new GeneratorLine3d(r0, r1);
}
else {
v.x = r2.x - r0.x;
v.y = r2.y - r0.y;
v.z = r2.z - r0.z;
g0 = new GeneratorLine3d(r0, r2);
g1 = new GeneratorLine3d(r0, r1);
g2 = new GeneratorLine3d(r1, r2);
}
// Absolute values for the normal
float anx = Math.abs(n.x);
float any = Math.abs(n.y);
float anz = Math.abs(n.z);
int i, o;
int si, so;
int ii, io;
int ei, eo;
boolean maxx, maxy, maxz,
midy, midz, midx,
minx, miny, minz;
maxx = maxy = maxz =
midy = midz = midx =
minx = miny = minz = false;
// Absolute values for the longest side vector
float rnx = Math.abs(v.x);
float rny = Math.abs(v.y);
float rnz = Math.abs(v.z);
int rmid = Util.max(rnx, rny, rnz);
if (rmid == rnz) midz = true;
else if (rmid == rny) midy = true;
midx = !midz && !midy;
// Determine the inner and outer loop directions
if (midz) {
if (any > anx)
{
maxy = true;
si = sy;
ii = iy;
ei = ey;
}
else {
maxx = true;
si = sx;
ii = ix;
ei = ex;
}
}
else {
if (anz > anx) {
maxz = true;
si = sz;
ii = iz;
ei = ez;
}
else {
maxx = true;
si = sx;
ii = ix;
ei = ex;
}
}
if (!midz && !maxz) {
minz = true;
so = sz;
eo = ez;
}
else if (!midy && !maxy) {
miny = true;
so = sy;
eo = ey;
}
else {
minx = true;
so = sx;
eo = ex;
}
// GeneratorLine3d is iterable
Point3i p1;
for (Point3i p0 : g0) {
// Make sure the two 'mid' coordinate correspond for the area inside the triangle
if (midz)
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.z != p0.z);
else if (midy)
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.y != p0.y);
else
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.x != p0.x);
eo = (minx ? p0.x : miny ? p0.y : p0.z);
so = (minx ? p1.x : miny ? p1.y : p1.z);
io = eo - so >= 0 ? 1 : -1;
for (o = so; o != eo; o += io) {
for (i = si; i != ei; i += ii) {
int x = maxx ? i : midx ? p0.x : o;
int y = maxy ? i : midy ? p0.y : o;
int z = maxz ? i : midz ? p0.z : o;
// isPassing tests to see if a point goes past a plane
// I know it's working, so no code
// voxels is a member that is an arraylist of Point3i
if (isPassing(x, y, z, r0, n.x, n.y, n.z)) {
voxels.add(new Point3i(x, y, z));
break;
}
}
}
}
}
You could use something like Besenham's line algorithm, but extended into three dimensions. The two main ideas we want to take from it are:
rotate the initial line so its slope isn't too steep.
for any given x value, find an integer value that is closest to the ideal y value.
Just as Bresenham's algorithm prevents gaps by performing an initial rotation, we'll avoid holes by performing two initial rotations.
Get the normal vector and point that represent the plane your triangle lies on. Hint: use the cross product of (line from p0 to p1) and (line from p0 to p2) for the vector, and use any of your corner points for the point.
You want the plane to be sufficiently not-steep, to avoid holes. You must satisfy these conditions:
-1 >= norm.x / norm.y >= 1
-1 >= norm.z / norm.y >= 1
Rotate your normal vector and initial points 90 degrees about the x axis and 90 degrees about the z axis until these conditions are satisfied. I'm not sure how to do this in the fewest number of rotations, but I'm fairly sure you can satisfy these conditions for any plane.
Create a function f(x,z) which represents the plane your rotated triangle now lies on. It should return the Y value of any pair of X and Z values.
Project your triangle onto the XZ plane (i.e., set all the y values to 0), and use your favorite 2d triangle drawing algorithm to get a collection of x-and-z coordinates.
For each pixel value from step 4, pass the x and z values into your function f(x,z) from step 3. Round the result to the nearest integer, and store the x, y, and z values as a voxel somewhere.
If you performed any rotations in step 2, perform the opposite of those rotations in reverse order on your voxel collection.
Start with a function that checks for triangle/voxel intersection. Now you can scan a volume and find the voxels that intersect the triangle - these are the ones you're interested in. This is a lousy algorithm but is also a regression test for anything else you try. This test is easy to implement using SAT (separating axis theorem) and considering the triangle a degenerate volume (1 face, 3 edges) and considering the voxels symmetry (only 3 face normals).
I use octtrees, so my preferred method is to test a triangle against a large voxel and figure out which of the 8 child octants it intersects. Then use recursion on the intersected children until the desired level of subdivision is attained. Hint: at most 6 of the children can be intersected by the triangle and often fewer than that. This is tricky but will produce the same results as the first method but much quicker.
Rasterization in 3d is probably fastest, but IMHO is even harder to guarantee no holes in all cases. Again, use the first method for comparison.