Skip to content

Instantly share code, notes, and snippets.

@Zammy
Forked from zloedi/roguelike_FOV.c
Last active November 8, 2018 15:52
Show Gist options
  • Save Zammy/90773d9931b890d09b48c6cf752c8c9d to your computer and use it in GitHub Desktop.
Save Zammy/90773d9931b890d09b48c6cf752c8c9d to your computer and use it in GitHub Desktop.
Unity3d Implementation of rougelike_FOV
/*
Check original C code and explanation!
*/
using System;
using System.Collections.Generic;
using UnityEngine;
public static class Visibility
{
static int Cross(this Vector2Int a, Vector2Int b)
{
return a.x * b.y - a.y * b.x;
}
static readonly Vector2Int[][] _bases =
{
new Vector2Int[] { new Vector2Int(1,0), new Vector2Int(0,1) },
new Vector2Int[] { new Vector2Int(1,0), new Vector2Int(0,-1) },
new Vector2Int[] { new Vector2Int(-1,0), new Vector2Int(0,-1) },
new Vector2Int[] { new Vector2Int(-1,0), new Vector2Int(0,1) },
new Vector2Int[] { new Vector2Int(0,1), new Vector2Int(-1,0) },
new Vector2Int[] { new Vector2Int(0,1), new Vector2Int(1,0) },
new Vector2Int[] { new Vector2Int(0,-1), new Vector2Int(1,0) },
new Vector2Int[] { new Vector2Int(0,-1), new Vector2Int(-1,0) },
};
public static void MarkVisbleInRadius(
Vector2Int origin,
int radius,
int octant,
Func<Vector2Int, bool> isVisibilityBlockedOn,
Action<Vector2Int> markVisible)
{
Vector2Int e0 = _bases[octant][0];
Vector2Int e1 = _bases[octant][1];
var rayLists = new List<List<Vector2Int>>()
{
new List<Vector2Int>()
{
new Vector2Int(1, 0),
new Vector2Int(1, 1)
},
new List<Vector2Int>()
};
int limit0 = radius;
int limit1 = radius;
Vector2Int ci = origin;
for (int i = 0; i <= limit0; i++)
{
int i2 = i * 2;
var currRays = rayLists[(i + 0) & 1];
var nextRays = rayLists[(i + 1) & 1];
nextRays.Clear();
for (int r = 0; r < currRays.Count - 1; r += 2)
{
Vector2Int r0 = currRays[r + 0];
Vector2Int r1 = currRays[r + 1];
int inyr0 = (i2 - 1) * r0.y / r0.x;
int outyr0 = (i2 + 1) * r0.y / r0.x;
int inyr1 = (i2 - 1) * r1.y / r1.x;
int outyr1 = (i2 + 1) * r1.y / r1.x;
int starty = outyr0 + 1;
if (r0.Cross(new Vector2Int(i2, outyr0)) < 0)
{
starty++;
}
starty /= 2;
Vector2Int start = ci + e1 * starty;
int endy = inyr1 + 1;
if (r1.Cross(new Vector2Int(i2, inyr1 + 1)) > 0)
{
endy--;
}
endy /= 2;
{
int y;
Vector2Int p;
int miny = starty;
int maxy = Math.Min(endy, limit1);
for (y = miny, p = start; y <= maxy; y++, p = p + e1)
{
if ((origin - p).magnitude < radius + 1)
{
markVisible(p);
}
}
}
Vector2Int bounds0;
Vector2Int bounds1;
Vector2Int firstin = ci + e1 * ((inyr0 + 1) / 2);
Vector2Int firstout = ci + e1 * ((outyr0 + 1) / 2);
if (!isVisibilityBlockedOn(firstin) && !isVisibilityBlockedOn(firstout))
{
bounds0 = r0;
}
else
{
int top = (outyr0 + 1) / 2;
int bottom = Math.Min((inyr1 + 1) / 2, limit1);
int y;
Vector2Int p = ci + e1 * top;
for (y = top * 2; y <= bottom * 2; y += 2, p = p + e1)
{
if (!isVisibilityBlockedOn(p))
{
break;
}
markVisible(p);
}
bounds0 = new Vector2Int(i2 - 1, y - 1);
inyr0 = (i2 - 1) * bounds0.y / bounds0.x;
outyr0 = (i2 + 1) * bounds0.y / bounds0.x;
}
Vector2Int lastin = ci + e1 * ((inyr1 + 1) / 2);
Vector2Int lastout = ci + e1 * ((outyr1 + 1) / 2);
if (!isVisibilityBlockedOn(lastin) && !isVisibilityBlockedOn(lastout))
{
bounds1 = r1;
}
else
{
int top = (outyr0 + 1) / 2;
int bottom = Math.Min((inyr1 + 1) / 2, limit1);
int y;
Vector2Int p = ci + e1 * bottom;
for (y = bottom * 2; y >= top * 2; y -= 2, p = p - e1)
{
if (!isVisibilityBlockedOn(p))
{
break;
}
// pixels that force ray corrections are lit too
markVisible(p);
}
bounds1 = new Vector2Int(i2 + 1, y + 1);
inyr1 = (i2 - 1) * bounds1.y / bounds1.x;
outyr1 = (i2 + 1) * bounds1.y / bounds1.x;
}
if (bounds0.Cross(bounds1) <= 0)
{
continue;
}
// push actual rays
{
nextRays.Add(bounds0);
int top = (outyr0 + 1) / 2;
int bottom = Math.Min((inyr1 + 1) / 2, limit1);
Vector2Int p = ci + e1 * top;
bool prevPixel = isVisibilityBlockedOn(p);
for (int y = top * 2; y <= bottom * 2; y += 2, p = p + e1)
{
bool pixel = isVisibilityBlockedOn(p);
if (prevPixel != pixel)
{
Vector2Int ray;
if (pixel)
{
ray = new Vector2Int(i2 + 1, y - 1);
}
else
{
ray = new Vector2Int(i2 - 1, y - 1);
}
nextRays.Add(ray);
}
prevPixel = pixel;
}
nextRays.Add(bounds1);
}
}
ci = ci + e0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment