Temple of The Roguelike Forums
Development => Programming => Topic started by: Vanguard on June 27, 2009, 03:12:13 AM
-
What's your favorite way to do line of sight?
What do you think is the easiest way to do line of sight?
-
What's your favorite way to do line of sight?
What do you think is the easiest way to do line of sight?
There's currently massive amounts (http://angband.oook.cz/forum/showthread.php?t=2018) of line of sight (http://angband.oook.cz/forum/showthread.php?t=2046) discussion going on in Angband.oook.cz and RogueBasin (http://roguebasin.roguelikedevelopment.org/index.php?title=Discussion:Field_of_Vision).
Or did you come from there?
-
No, I didn't, I'm just at the point where I need to implement that, but I have no idea how to actually go about doing it. I'll take a look at those links though, thanks!
-
I personally just use a simple ray caster with a post processing step. Here (http://rickclark58.googlepages.com/main42.png/main42-full;init:.png) is an example of the results. In the pic the zombies block line of sight (the gray areas are seen tiles) which make corridor fighting a bit more interesting imo. The advantage is that is is quite fast and I don't really see the need to use the more esoteric models.
-
Raycasting, while not the best option, is quick and easy to implement and it works. I either raycast in a 360 degree circle around the player, or I send a ray to every square within a certain radius. Not perfect or even very good, but it gets the job done and it's very simple to implement.
-
I was thinking that shadow casting looks like the most attractive option to me, from reading through Paul's links. I'll let you guys know how it goes, but I'm still open to suggestions if I hear a good idea.
Edit: This is harder than I thought it'd be. I think I've got raycasting figured out though, so maybe I'll just do one of those for now, and if I want to change it later I can come back to it.
-
I agree with the others, just do a naive raycasting and see if it's good enough.
You can implement one very easily, for example (from one of my older games, Y_ and X_ represent the size of the map):
//radial field of view
void fov(int y, int x, int radius) {
for (int yy=max(y-radius,0); yy<=min(y+radius,Y_-1); yy++)
for (int xx=max(x-radius,0); xx<=min(x+radius,X_-1); xx++)
if (in_range(y,x,yy,xx,radius) && los(y,x,yy,xx,WALL))
view[yy][xx]=IN_SIGHT;
}
Where los is a simple line-of-sight algorithm (mostly ripped straight off of wikipedia) that plots a line to that point and return true if it reached it without bumping into a wall (or another opaque tile):
//line of sight
bool los(int y0,int x0,int y1,int x1,chtype opaque) {
//Bresenham's line algorithm
//taken from: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
bool steep=fabs(y1-y0)>fabs(x1-x0);
if (steep) {
swap(&x0,&y0);
swap(&x1,&y1);
}
if (x0>x1) {
swap(&x0,&x1);
swap(&y0,&y1);
}
float err_num=0.0;
int y=y0;
for (int x=x0; x<=x1; x++) {
if (x>x0 && x<x1) {
if (steep) {
if (opaque==map[x][y].type)
return false;
} else {
if (opaque==map[y][x].type)
return false;
}
}
err_num+=(float)(fabs(y1-y0))/(float)(x1-x0);
if (0.5<fabs(err_num)) {
y+=y1>y0?1:-1;
err_num--;
}
}
return true;
}
and in_range is the euclidean distance, used to create a nice circular shape:
bool in_range(int y0,int x0,int y1,int x1,int r) {
return dist(y0,x0,y1,x1)<=pow(r,2);
}
I used this basic code or variation-thereof in a couple of games, the first and easiest to understand is in the 7drl version of CryptRover (http://code.google.com/p/cryptrover/downloads/) from the 2008 7drl challange.
-
Alright, well thanks for your help guys! I've got a raycaster up and running now.
Have some screens:
http://img38.imageshack.us/img38/7599/screen1hfu.png (http://img38.imageshack.us/img38/7599/screen1hfu.png)
http://img38.imageshack.us/img38/6303/screen2sts.png (http://img38.imageshack.us/img38/6303/screen2sts.png)
http://img33.imageshack.us/img33/6909/screen3kgt.png (http://img33.imageshack.us/img33/6909/screen3kgt.png)
It's not perfect yet. As you can see it's not being as generous as it should be with showing wall tiles, but I should be able to work that out. Thanks again for your advice!
-
Looks like you are stopping 1 step too early or beginning 1 step too late.
Maybe when checking if there is a obstacle in the line of sight omit checking the origin and destination tiles, and only check for the tiles in-between.
Also, that's a nice character - did you draw it yourself? If you invest the same amount of effort in all tiles/sprites I think you could have a very good looking game.
-
I'm guessing you could have a flag/option on each tile which defined whether the object occupies the whole square. If that was the case, you'd be able to see the wall if the ray hit one of the corners or sides. It could be set for big monsters, walls and the like.
-
Looks like you are stopping 1 step too early or beginning 1 step too late.
Maybe when checking if there is a obstacle in the line of sight omit checking the origin and destination tiles, and only check for the tiles in-between.
I think the reason is because right now the code that determines which direction the ray moves in to reach its target is too absolute.
It works by getting the slope of a line from the character in question to the tile being checked, and then as the ray moves towards the tile, it checks that against the current slope of a line from where it is to the destination tile.
If the it gets above the original line, it goes down, and if it's gotten to the left of it, it goes right until it reaches the end. It's just not very flexible or forgiving with angles right now - it will only move diagonally if the destination tile is exactly diagonal from its current location, and with a nearly, but not quite horizontal line, it'll move horizontally at first, and then it'll be above/below the original slope, even if only by a tiny bit, and for that reason it will move vertically, and in tight corridors, this means it'll prematurely hit a wall.
I'll add a little bit of "breathing room" for the slope checks, so it doesn't do such dramatic movements for such negligible differences, and it should be okay after that.
Does that explanation make sense?
Also, that's a nice character - did you draw it yourself? If you invest the same amount of effort in all tiles/sprites I think you could have a very good looking game..
Hey, thanks! I really appreciate you saying that!
I think having a unique tileset will help my game stand out in the crowd a little more. Ascii will still be supported for those who prefer it.
I'm guessing you could have a flag/option on each tile which defined whether the object occupies the whole square. If that was the case, you'd be able to see the wall if the ray hit one of the corners or sides. It could be set for big monsters, walls and the like.
I'm not sure how I'd be able to do that for walls and other "inanimate visual obstructions, for the way I have everything set up, but it'd be really easy to do it for monsters. I don't know if I actually want to do that or not, but I'm keeping the option open.
-
Hey, I'm back for some more advice.
I replaced my old method of comparing the current slope with the end slope to one that only uses the current slope of where the "scanner" is to where the destination tile is. It's much simpler, and is working mostly better, but with one problem - it's being too permissive with what it shows, but only to targets that are mostly horizontal. I'll add some screens to show what I mean:
http://img6.imageshack.us/img6/8611/badscreen.png (http://img6.imageshack.us/img6/8611/badscreen.png)
See, in this one it's expanding at a 45 degree angle at the end of the hallway. That's just too much.
http://img6.imageshack.us/img6/1197/goodscreen.png (http://img6.imageshack.us/img6/1197/goodscreen.png)
Here is how I want it. It expands, but much more conservatively.
Here's the code I'm using for this, anyone who wants to use or modify this is free to do so.
public void calculateFieldOfView() {
initializeTileVisibility();
for (int tileCounterY = 0; tileCounterY < Map.getStageSizeY(); tileCounterY++) {
for (int tileCounterX = 0; tileCounterX < Map.getStageSizeX(); tileCounterX++) {
// This will cut out unnecessary tiles to save processing time.
if (tileCounterX < xLocation - lineOfSightRange) {
tileCounterX = xLocation - lineOfSightRange;
}
if (tileCounterY < yLocation - lineOfSightRange) {
tileCounterY = yLocation - lineOfSightRange;
}
if (tileCounterX > xLocation + lineOfSightRange) {
tileCounterX = Map.getStageSizeX();
}
if (tileCounterY > yLocation + lineOfSightRange) {
tileCounterY = Map.getStageSizeY();
}
if (Calculate.calculateLineOfSightDistance(xLocation, yLocation, tileCounterX, tileCounterY) <= lineOfSightRange) {
if (tileVisibility[tileCounterX][tileCounterY] == false) {
checkLineOfSightConnection(tileCounterX, tileCounterY);
}
}
}
}
}
// This will check whether the character in question can see the location in question
private void checkLineOfSightConnection(int xCoordinate, int yCoordinate) {
Map currentLevel = Main.getCurrentLevel();
int scanX = xLocation;
int scanY = yLocation;
boolean blocked = false;
double currentSlope;
tileVisibility[scanX][scanY] = true;
if (this == Main.getPlayer()) {
int tileType = currentLevel.getMapData(scanX, scanY);
currentLevel.setMapMemoryData(scanX, scanY, tileType);
tileLighting[scanX][scanY] = true;
}
// This will loop until the ray has reached its target, or is blocked by something;
while (scanX != xCoordinate || scanY != yCoordinate) {
// This is to ensure we don't get any divide by zero errors. The slope is high enough that it'll work no matter what.
if (scanX == xCoordinate) {
currentSlope = 10000;
} else {
// We figure out what our current slope is, so we know how to reach our target.
currentSlope = Math.abs(Calculate.getSlope(scanX, scanY, xCoordinate, yCoordinate));
}
// Now we use the slope to figure out how the scan coordinates should move.
if (currentSlope <= 0.50) {
if (scanX < xCoordinate) {
scanX += 1;
}
if (scanX > xCoordinate) {
scanX -= 1;
}
} else if (currentSlope >= 2.00) {
if (scanY < yCoordinate) {
scanY += 1;
}
if (scanY > yCoordinate) {
scanY -= 1;
}
} else {
if (scanX < xCoordinate) {
scanX += 1;
}
if (scanX > xCoordinate) {
scanX -= 1;
}
if (scanY < yCoordinate) {
scanY += 1;
}
if (scanY > yCoordinate) {
scanY -= 1;
}
}
// If the ray hits something opaque, we set blocked to true so the ray stops.
if (blocked == false) {
tileVisibility[scanX][scanY] = true;
if (this == Main.getPlayer()) {
int tileType = currentLevel.getMapData(scanX, scanY);
currentLevel.setMapMemoryData(scanX, scanY, tileType);
tileLighting[scanX][scanY] = true;
}
}
if (currentLevel.checkOpacity(scanX, scanY) == true) {
blocked = true;
}
}
}
// This sets every tile to unseen to start the turn out.
private void initializeTileVisibility() {
for (int yCounter = 0; yCounter <
Map.getStageSizeY(); yCounter++) {
for (int xCounter = 0; xCounter <
Map.getStageSizeX(); xCounter++) {
tileVisibility[xCounter][yCounter] = false;
}
}
}
Can any of you spot what the cause of the problem I'm having is? I can't figure it out.
Edit: And a few explanations to help make this more clear maybe:
Calculate.getSlope returns the slope of the two coordinate pairs it's given.
Calculate.calculateLineOfSightDistance returns the distance between the two tiles.
xLocation and yLocation are the current character's coordinates.
tileLighting is a boolean array for whether the player character can currently see the tile's location. It currently does nothing, but that'll change in the near future when I make it so tiles that are remembered but not seen are darkened/don't have characters drawn on them.
currentLevel.setMapMemoryData sets what the current tile on the current level is remembered as. This is exclusively what is currently used for determining what to draw.
tileVisibility is whether the current character can see the current tile, regardless of whether they're a player character or not. This is what will be used for ai characters' sight.
scanX and scanY are the locations of the scanner that moves from the character's location to the destination tile.
Does that explain everything well enough?
-
When doing a raycaster in a tiled enviornment, remember to always add 0.5 onto your coordinates so that you check LOS from the center of one tile to another, rather than from the upper left corner of one to the upper left corner of the other. Assuming you're using actual rays, instead of just a line algorithm like Bresenham.
-
When doing a raycaster in a tiled enviornment, remember to always add 0.5 onto your coordinates so that you check LOS from the center of one tile to another, rather than from the upper left corner of one to the upper left corner of the other. Assuming you're using actual rays, instead of just a line algorithm like Bresenham.
I'm not really familiar with this stuff, since this is the first time I've ever done anything with LOS, but I'm assuming that what I'm doing is the second one. Adding 0.5 to my coordinates wouldn't do anything other than confuse and break the game.
Anyway, if my code I copy pasted above is readable (I hope it is, I tried my best to make it readable) then you can see what I did for yourself.
-
Note: I haven't checked the code below, this is just an example. It's roughly designed in the same way as the above code by Curseman.
void FOV()
{
int i,j;
float x,y,l;
for(i=0;i<80;i++)for(j=0;j<25;j++)
{
MAP[i][j]= NOT_VISIBLE;//Pseudo code
x=i-PLAYERX;
y=j-PLAYERY;
l=sqrt((x*x)+(y*y));
if(l<VIEW_RADIUS)
if(DoFov(i,j)==true)
MAP[i][j] = VISIBLE;//Pseudo code, you understand.
};
};
bool DoFov(int x,int y)
{
float vx,vy,ox,oy,l;
int i;
vx = x-PLAYERX;
vy = y-PLAYERY;
ox = (float)x+0.5f;
oy= (float)y+0.5f;
l=sqrt((vx*vx)+(vy*vy));
vx/=l;
vy/=l;
for(i=0;i<(int)l;i++)
{
if(MAP[(int)ox][(int)oy]==BLOCK)
return false;
ox+=vx;
oy+=vy;
};
return true;
};
Edit; If this is a bit slow, try only casting rays in a 360 degree or less circle around the player. You can convert degrees to radians and then use sin and cos to figure out the unit vector to use, which can be precomputed. Then you can cast rays along each of the degrees marking squares as visible until you hit a block at which point the ray terminates. That is technically much closer to raycasting than the above example.
Raycasting:
void FOV()
{
float x,y;
int i;
CLEAR_MAP_TO_NOT_VISIBLE();//Initially set all tiles to not visible.
for(i=0;i<360;i++)
{
x=cos((float)i*0.01745f);
y=sin((float)i*0.01745f);
DoFov(x,y);
};
};
void DoFov(float x,float y)
{
int i;
float ox,oy;
ox = (float)PLAYERX+0.5f;
oy = (float)PLAYERY+0.5f;
for(i=0;i<VIEW_RADIUS;i++)
{
MAP[(int)ox][(int)oy]=VISIBLE;//Set the tile to visible.
if(MAP[(int)ox][(int)oy]==BLOCK)
return;
ox+=x;
oy+=y;
};
};
-
Alright, well I found out what the problem was.
As usual, it was both something completely stupid, and in a place I didn't expect.
In Calculate.getSlope (of course it's one of the methods I didn't post here...) I used ints instead of doubles for the calculations, even though the return type is a double. That meant that I would always get whole numbers for the slope and it messed things up.
Why is that always how it happens?
Anyway, the code I posted above works, so if anyone just does not want to bother with this or doesn't think they know how, it's up for grabs.
-
Nice :)
Didn't your compiler warn you about assigning double to int?
-Ido.
-
No, it didn't say anything, but I don't think it would with the way I wrote that method. It looks like this in case you're curious:
public static double getSlope(double firstX, double firstY, double secondX, double secondY) {
return (secondY - firstY) / (secondX - firstX);
}
-
You can still send int, and then multiply one number by 1.0. The compiler implicitly converts everything to doubles/floats :)
Also, doubles are really rather heavy. Do you need that kind of precision? Why not take floats? Doubles are 8 bytes, floats are 4, if I'm not mistaken.
Something like this should work, and be a little faster:
public static float getSlope(int firstX, int firstY, int secondX, int secondY) {
return (1.0*secondY - firstY) / (secondX - firstX);
}
-
Hmm... yeah, it looks like you're right. I changed Calculate.getSlope and the numbers within it as well as currentSlope to floats, and I don't notice any difference. It's only... what, 20 bytes? But I'll take it anyway.
-
I'm guessing it's normal that you don't notice it!
But when you're doing loads and loads of calculations on each step (for other kinds of games, where 3d needs to use that kind of stuff a bunch), it may eventually lag quite a bit. Anyway, CPU's nowadays are mostly 64 bits, so it shouldn't be that big of a difference... I just tend to use floats unless I need a high level of precision for whatever reason :)
-
I'm using a variant on a flood fill to handle LOS at the moment. For each tile traversed I store a list of directions to fill from that point. Each fill direction restricts 5 of the 8 cardinal directions from being filled from the destination tile. Tiles are processed in order of their distance from the source. If two tiles fill into the same tile, the restricted directions from that tile are only the directions restricted by both operations.
Any thoughts on this approach?
-
That is technically much closer to raycasting than the above example.
Bad thing is that it doesn't work. I know, I just tried it.
-
I'm in the process of re-creating LOS for Kaduria. This is something I discovered in Teemu and it would be nice if someone has explanation for the problem. When traced to edges of levels (like in Teemu where the entire level is visible) the los is working, but when traced around the player in some distance the routine doesn't make symmetric LOS:
http://koti.mbnet.fi/paulkp/temp/kaduria3.jpg
-
I guess your routine fails
-
It'll be hard just guessing if we don't have your algorithm here...
Either way, I'm betting on it being some sort of = where there shouldn't be one, like <= and >=... however, I find it weird that it happens in that situation!
It can also be what has been mentioned before: you're not considering the CENTER of each square, but rather the top-left corner or something along those lines :)
-
I'm in the process of re-creating LOS for Kaduria. This is something I discovered in Teemu and it would be nice if someone has explanation for the problem. When traced to edges of levels (like in Teemu where the entire level is visible) the los is working, but when traced around the player in some distance the routine doesn't make symmetric LOS:
http://koti.mbnet.fi/paulkp/temp/kaduria3.jpg
I have no idea if this will help in any way at all, since we don't have your code to look at, but I experienced a similar problem, and by changing the number comparisons it measures the slope against from a < and a > to a <= and a >= it seems to have been resolved. Again, I have no idea if your stuff is similar to mine in any way at all, but that's what my problem was - the slope was exactly equal to, rather than greater than or less than the numbers it was measuring, so I got stuff like that.
Basically what I'm saying is that I had the problem Anvilfolk is suggesting might be the case with your problems, just in reverse.
-
It can also be what has been mentioned before: you're not considering the CENTER of each square, but rather the top-left corner or something along those lines :)
Something like that. Here is the routine:
int Fovmap::Round(float f)
{
return (int)(f+0.5f);
}
void Fovmap::Los_Line(int x1, int y1, int x2, int y2)
{
//check if source and destination positions are the same
if (x1==x2 && y1==y2) return;
int dx=x2-x1;
int dy=y2-y1;
int loop;
if (abs(dy)<=abs(dx)) loop=abs(dx);
else loop=abs(dy);
float x_inc=(float)dx/loop;
float y_inc=(float)dy/loop;
float x=(float)x1;
float y=(float)y1;
//origin point of los map coordinates
float fx=(float)origin_x;
float fy=(float)origin_y;
int rx, ry, tx, ty;
unsigned char b, c=fovVisible;
for (int i=0; i<=loop; i++)
{
x+=x_inc;
y+=y_inc;
fx+=x_inc;
fy+=y_inc;
rx=Round(x);
ry=Round(y);
tx=Round(fx);
ty=Round(fy);
if (c==fovVisible)
{
b=terrain->Get(rx, ry);
if (g_terrain[b].los>0) c=fovBlocked; //los in blocked from now on
Put(tx, ty, fovVisible); //use los map coordinates
explored->Put(rx, ry, 1); //use explore map coordinates
}
else Put(tx, ty, fovBlocked);
}
}
void Fovmap::Trace(int x, int y)
{
const int mw=Get_Width();
const int mw_half=(mw/2)-1;
//origin point is always visible
Put(x, y, fovVisible);
explored->Put(x, y, 1);
for (int o=x-mw_half; o<=x+mw_half; o++)
{
Los_Line(x,y, o, y-fov_len);
Los_Line(x,y, o, y+fov_len);
}
for (int o=y-mw_half; o<=y+mw_half; o++)
{
Los_Line(x,y, x+fov_len, o);
Los_Line(x,y, x-fov_len, o);
}
}
-
I spent the past day or so improving my LoS code I posted earlier. It had some issues with how the LoS worked at the ends of hallways, but I think I've got them worked out. I don't think the way I worked around it is especially pretty (from the standpoint of how the code looks), but it should be alright as to practical concerns.
Here's a link to the jar file so anyone who wants to take a look at the LoS code in action can do so. I'd appreciate any comments and/or suggestions.
http://rapidshare.com/files/259173760/Guardian.zip.html (http://rapidshare.com/files/259173760/Guardian.zip.html)
Everything is very much so a WIP. There's no gameplay in there, the inventory and equipment screens are as bland as can be, and there's only one NPC who just follows ou around, but none of those things are the reason why I'm posting this.
Also, here is the updated code if anyone is interested:
public void calculateFieldOfView() {
initializeTileVisibility();
for (int tileCounterY = 0; tileCounterY < Map.getStageSizeY(); tileCounterY++) {
for (int tileCounterX = 0; tileCounterX < Map.getStageSizeX(); tileCounterX++) {
// This will cut out unnecessary tiles to save processing time.
if (tileCounterX < xLocation - lineOfSightRange) {
tileCounterX = xLocation - lineOfSightRange;
}
if (tileCounterY < yLocation - lineOfSightRange) {
tileCounterY = yLocation - lineOfSightRange;
}
if (tileCounterX > xLocation + lineOfSightRange) {
tileCounterX = Map.getStageSizeX();
}
if (tileCounterY > yLocation + lineOfSightRange) {
tileCounterY = Map.getStageSizeY();
}
if (Calculate.calculateLineOfSightDistance(xLocation, yLocation, tileCounterX, tileCounterY) <= lineOfSightRange) {
if (tileVisibility[tileCounterX][tileCounterY] == false) {
checkLineOfSightConnection(tileCounterX, tileCounterY);
}
}
}
}
}
// This will check whether the character in question can see the location in question
private void checkLineOfSightConnection(int xCoordinate, int yCoordinate) {
Map currentLevel = Main.getCurrentLevel();
// This will follow a path from the character's location to the destination point to see if there's something in the way.
int scanX = xLocation;
int scanY = yLocation;
// This is marked as true if something is blocking the character's view
boolean blocked = false;
// This is the slope of the scanner's location measured against the destination.
float currentSlope;
// These are for dealing with the hallway issues.
boolean firstMove = true;
boolean limitHorizontal = true;
tileVisibility[scanX][scanY] = true;
if (this == Main.getPlayer()) {
int tileType = currentLevel.getMapData(scanX, scanY);
currentLevel.setMapMemoryData(scanX, scanY, tileType);
tileLighting[scanX][scanY] = true;
}
// This will loop until the ray has reached its target, or is blocked by something;
while (scanX != xCoordinate || scanY != yCoordinate) {
// This is to ensure we don't get any divide by zero errors. The slope is high enough that it'll work no matter what.
if (scanX == xCoordinate) {
currentSlope = 10000;
} else {
// We figure out what our current slope is, so we know how to reach our target.
currentSlope = Math.abs(Calculate.getSlope(scanX, scanY, xCoordinate, yCoordinate));
}
// Now we use the slope to figure out how the scan coordinates should move.
if (currentSlope <= 0.50) {
if (scanX < xCoordinate) {
scanX += 1;
}
if (scanX > xCoordinate) {
scanX -= 1;
}
limitHorizontal = false;
} else if (currentSlope >= 2.00) {
if (scanY < yCoordinate) {
scanY += 1;
}
if (scanY > yCoordinate) {
scanY -= 1;
}
limitHorizontal = true;
} else {
if (scanX < xCoordinate) {
scanX += 1;
}
if (scanX > xCoordinate) {
scanX -= 1;
}
if (scanY < yCoordinate) {
scanY += 1;
}
if (scanY > yCoordinate) {
scanY -= 1;
}
firstMove = false;
}
// This is for resolving the problems I've been having at the ends of hallways.
if (firstMove == true) {
// We only need to do this if the target is more than one space away in both directions
if ((Math.abs(xLocation - xCoordinate)) > 1 && Math.abs(yLocation - yCoordinate) > 1) {
int xTargetDirection;
int yTargetDirection;
int limitScanX;
int limitScanY;
boolean limitPov = false;
int targetDistance;
targetDistance = 0;
if (limitHorizontal == true) {
targetDistance = Math.abs(xLocation - xCoordinate);
} else {
targetDistance = Math.abs(yLocation - yCoordinate);
}
if (xLocation > xCoordinate) {
xTargetDirection = -1;
} else {
xTargetDirection = 1;
}
if (yLocation > yCoordinate) {
yTargetDirection = -1;
} else {
yTargetDirection = 1;
}
// This part checks to see if there are obstructions in the way.
// It moves diagonally starting at the player's location moving towards the target.
for (int obstructionDistance = 1; obstructionDistance <= targetDistance; obstructionDistance++) {
limitScanX = xLocation + (xTargetDirection * (obstructionDistance));
limitScanY = yLocation + (yTargetDirection * (obstructionDistance));
if (limitScanX >= Map.getStageSizeX()) {
limitScanX = Map.getStageSizeX() - 1;
}
if (limitScanX < 0) {
limitScanX = 0;
}
if (limitScanY >= Map.getStageSizeY()) {
limitScanY = Map.getStageSizeY() - 1;
}
if (limitScanY < 0) {
limitScanY = 0;
}
if (currentLevel.checkOpacity(limitScanX, limitScanY) == true) {
limitPov = true;
}
// These check either straight horizontal or vertical rows to see if the view is blocked.
if (limitHorizontal == true) {
if (limitScanY > yCoordinate) {
while (limitScanY > yCoordinate + obstructionDistance) {
limitScanY += yTargetDirection;
if (currentLevel.checkOpacity(limitScanX, limitScanY) == true) {
limitPov = true;
//xCoordinate = xLocation + xTargetDirection;
limitScanY = yCoordinate;
}
}
} else {
while (limitScanY < yCoordinate - obstructionDistance) {
limitScanY += yTargetDirection;
if (currentLevel.checkOpacity(limitScanX, limitScanY) == true) {
limitPov = true;
//xCoordinate = xLocation + xTargetDirection;
limitScanY = yCoordinate;
}
}
}
} else {
if (limitScanX > xCoordinate) {
while (limitScanX > xCoordinate + obstructionDistance) {
limitScanX += xTargetDirection;
if (currentLevel.checkOpacity(limitScanX, limitScanY) == true) {
limitPov = true;
//xCoordinate = xLocation + xTargetDirection;
limitScanX = xCoordinate;
}
}
} else {
while (limitScanX < xCoordinate - obstructionDistance) {
limitScanX += xTargetDirection;
if (currentLevel.checkOpacity(limitScanX, limitScanY) == true) {
limitPov = true;
//xCoordinate = xLocation + xTargetDirection;
limitScanX = xCoordinate;
}
}
}
}
// Now if we do find something blocking the way, we move the target coordinates in to better simulate real LoS.
if (limitPov == true) {
// First we do this so the loop ends. There's no reason to keep it going.
obstructionDistance = targetDistance;
// Now we move the target coordinates appropriately.
if (limitHorizontal == true) {
xCoordinate = limitScanX;
} else {
yCoordinate = limitScanY;
}
}
}
}
firstMove = false;
}
// If the ray hits something opaque, we set blocked to true so the ray stops.
if (blocked == false) {
tileVisibility[scanX][scanY] = true;
if (this == Main.getPlayer()) {
int tileType = currentLevel.getMapData(scanX, scanY);
currentLevel.setMapMemoryData(scanX, scanY, tileType);
tileLighting[scanX][scanY] = true;
}
}
if (currentLevel.checkOpacity(scanX, scanY) == true) {
blocked = true;
}
}
}
// This sets every tile to unseen to start the turn out.
private void initializeTileVisibility() {
for (int yCounter = 0; yCounter <
Map.getStageSizeY(); yCounter++) {
for (int xCounter = 0; xCounter <
Map.getStageSizeX(); xCounter++) {
tileVisibility[xCounter][yCounter] = false;
if (this == Main.getPlayer()) {
tileLighting[xCounter][yCounter] = false;
}
}
}
}
-
You can still send int, and then multiply one number by 1.0. The compiler implicitly converts everything to doubles/floats :)
Also, doubles are really rather heavy. Do you need that kind of precision? Why not take floats? Doubles are 8 bytes, floats are 4, if I'm not mistaken.
Something like this should work, and be a little faster:
public static float getSlope(int firstX, int firstY, int secondX, int secondY) {
return (1.0*secondY - firstY) / (secondX - firstX);
}
Floating point arithmetic (on current x86 processors) is done on the FPU which internally uses 'double' or 'long double' floating point types (I am not sure which ones) -- and you don't require the new 64 bit processors for that, it was so for a long time on 32 bit processors. There is no reason to use singles (floats), it could actually make your program slower because it requires to convert the internal type to float. Thus, use doubles, unless you really care about space usage and floats' bad precision is sufficient for you.
You can also name your floating point type somehow, use this name everywhere in your program, and do some benchmarks yourself easily by changing this type (you can also port to a new hardware easily). My simple test program works in 0.56 s for doubles and long doubles, and in 1.21 s for floats. (This was a really simple calculation, not any real useful stuff.) Remember to turn optimization on.
Unless you need the floating point (i.e. you don't know the magnitude of the numbers you are working on), you can also try avoiding floats altogether and use integers only... this has some advantages (as they are easier to understand, you won't be hit by unexpected rounding errors; also testing for equality actually working).
-
Whoa, that's totally new information for me! Pretty awesome, thanks! :)