Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Created April 10, 2025 19:28
Show Gist options
  • Save unitycoder/133de06ebd227c324ec3e7d32877d24e to your computer and use it in GitHub Desktop.
Save unitycoder/133de06ebd227c324ec3e7d32877d24e to your computer and use it in GitHub Desktop.
unityscript 2d voxel travelsal
// from https://unitycoder.com/blog/2012/10/29/2d-raycasting-visibility-voxel-travelsal/
#pragma strict
// ORIGINAL SOURCE: http://dev.mothteeth.com/2011/11/2d-ray-casting-on-a-grid-in-as3/
// TileWorld.as - Lee Grey, November 2011
// Converted to Unity: http://unitycoder.com/blog
// ** remember to donate : ) **
// the mesh generation is based on this one: http://unitycoder.com/blog/2012/02/15/raycast-realtime-visibility-2-0/
public var lightmeshholder:GameObject;
private var RaysToShoot:int=512; //64; 128; 1024;
private var distance:int=15;
private var vertices : Vector3[];
private var vertices2d : Vector2[];
private var triangles : int[];
//private var vertices2 : Vector3[];
private var mesh : Mesh;
// lookuptables
private var lookupTableSize:int=256;
private var mSin:float[]; // using native .NET arrays (see unity help page for ARRAY)
private var mCos:float[];
private var mconst:float;
private var c:float = 0;
private var maxrad:float = 2*Mathf.PI;
private var cAdd:float = 0;
private var prevpos:Vector3;
// added variables here, instead of inside loop
private var dir:Vector3;
private var hit : RaycastHit;
private var angle:float;
private var x:float;
private var y:float;
private var i:int;
private var tmp:Vector3;
private var tmp2:Vector3;
private var layerMask = 1 << 8;
private var size:int=32;
private var map:int[,] = new int[size,size];
private var texture:Texture2D;
function Start ()
{
//vertices = new Vector3[RaysToShoot];
vertices2d = new Vector2[RaysToShoot];
//triangles = new int[RaysToShoot];
// vertices2 = new Vector3[4];
mesh= lightmeshholder.GetComponent(MeshFilter).mesh;
// init lookUp arrays
lookupTableSize = RaysToShoot;
mCos = new float[lookupTableSize];
mSin = new float[lookupTableSize];
mconst = lookupTableSize / (2*Mathf.PI); // array index constant
// build lookup tables
var angle:float = 0;
for (i=0;i<lookupTableSize;i++)
{
mSin[i] = Mathf.Sin(angle); //*distance; //*distance is for linecast distance..(instead of dir)
mCos[i] = Mathf.Cos(angle); //*distance;
angle += 2*Mathf.PI/lookupTableSize;
}
cAdd = maxrad/lookupTableSize;
//print (maxrad);
BuildMesh ();
vertices = mesh.vertices;
texture = renderer.material.mainTexture;
for (var y:int=0;y<size;y++)
{
for (var x:int=0;x<size;x++)
{
if (texture.GetPixel(x,y).r>0)
{
map[x,y]=1;
}else{
map[x,y]=0;
}
}
}
}
//Ray casting technique described in paper:
//A Fast Voxel Traversal Algorithm for Ray Tracing - John Amanatides, Andrew Woo
//http://www.cse.yorku.ca/~amana/research/grid.pdf
function castRay( p1Original:Vector2, p2Original:Vector2, tileSize:int)
{
//INITIALISE//////////////////////////////////////////
// normalise the points
var p1:Vector2 = new Vector2( p1Original.x / tileSize, p1Original.y / tileSize);
var p2:Vector2 = new Vector2( p2Original.x / tileSize, p2Original.y / tileSize);
//if ( int( p1.x ) == int( p2.x ) && int( p1.y ) == int( p2.y ) )
if ( Mathf.Floor( p1.x ) == Mathf.Floor( p2.x ) && Mathf.Floor( p1.y ) == Mathf.Floor( p2.y ) )
{
//since it doesn't cross any boundaries, there can't be a collision
// print ("err");
return p2Original;
}
//find out which direction to step, on each axis
var stepX:int = ( p2.x > p1.x ) ? 1 : -1;
var stepY:int = ( p2.y > p1.y ) ? 1 : -1;
var rayDirection:Vector2 = new Vector2( p2.x - p1.x, p2.y - p1.y );
//find out how far to move on each axis for every whole integer step on the other
var ratioX:double = rayDirection.x / rayDirection.y;
//var ratioX:Number = rayDirection.x / rayDirection.y;
var ratioY:double = rayDirection.y / rayDirection.x;
//var ratioY:Number = rayDirection.y / rayDirection.x;
var deltaY:double = p2.x - p1.x;
var deltaX:double = p2.y - p1.y;
//faster than Math.abs()...
deltaX = deltaX < 0 ? -deltaX : deltaX;
deltaY = deltaY < 0 ? -deltaY : deltaY;
//initialise the integer test coordinates with the coordinates of the starting tile, in tile space ( integer )
//Note: using noralised version of p1
var testX:int = Mathf.Floor(p1.x);
var testY:int = Mathf.Floor(p1.y);
//initialise the non-integer step, by advancing to the next tile boundary / ( whole integer of opposing axis )
//if moving in positive direction, move to end of curent tile, otherwise the beginning
var maxX:double = deltaX * ( ( stepX > 0 ) ? ( 1.0 - (p1.x % 1) ) : (p1.x % 1) );
var maxY:double = deltaY * ( ( stepY > 0 ) ? ( 1.0 - (p1.y % 1) ) : (p1.y % 1) );
var endTileX:int = Mathf.Floor(p2.x);
var endTileY:int = Mathf.Floor(p2.y);
//TRAVERSE//////////////////////////////////////////
var hit:boolean;
var collisionPoint:Vector2; // = new Vector2();
//print ("testX:"+testX+" endTileX:"+endTileX);
while( testX != endTileX || testY != endTileY )
{
if ( maxX < maxY )
{
maxX += deltaX;
testX += stepX;
//if (texture.GetPixel( testX, testY ).g != 0 )
if (map[testX, testY] != 0 )
{
collisionPoint.x = testX;
if ( stepX < 0 ) collisionPoint.x += 1.0; //add one if going left
collisionPoint.y = p1.y + ratioY * ( collisionPoint.x - p1.x);
collisionPoint.x *= tileSize;//scale up
collisionPoint.y *= tileSize;
// texture.SetPixel(testX,testY,Color.yellow);
return collisionPoint;
}
} else {
maxY += deltaY;
testY += stepY;
// if (texture.GetPixel( testX, testY ).g != 0 )
if (map[testX, testY] != 0 )
{
collisionPoint.y = testY;
if ( stepY < 0 ) collisionPoint.y += 1.0; //add one if going up
collisionPoint.x = p1.x + ratioX * ( collisionPoint.y - p1.y);
collisionPoint.x *= tileSize;//scale up
collisionPoint.y *= tileSize;
// texture.SetPixel(testX,testY,Color.gray);
return collisionPoint;
}
}
}
//no intersection found, just return end point:
// print ("no coll");
return p2Original;
}
// was Update
function LateUpdate ()
{
angle = 0;
for (i=0;i<RaysToShoot;i++)
{
x = getCos(c);
y = getSin(c);
c = Mathf.Repeat(c+cAdd,maxrad);
//print (dir);
// var pos:Vector2 = new Vector2(size*0.5,size*0.5);
var pos:Vector2 = new Vector2(lightmeshholder.transform.position.x,lightmeshholder.transform.position.z);
// do raycast
dir = Vector3(pos.x,0,pos.y)+(Vector3(x,0,y)*10);
var target:Vector2 = new Vector2(dir.x,dir.z);
var voxelHit:Vector2 = castRay(pos,target,1);
tmp = lightmeshholder.transform.InverseTransformPoint(Vector3(voxelHit.x,0,voxelHit.y));
vertices[i] = Vector3(tmp.x,0.5,tmp.z); // replaced 0y=.5 with 0
}
// last vertice is at the player location (center point) // dont use this..?
//vertices[i] = lightmeshholder.transform.InverseTransformPoint(transform.position);
// move the whole object to player position (if needed)
// lightmeshholder.transform.position = transform.position;
mesh.vertices = vertices;
}
function BuildMesh ()
{
var angle:float = 0;
for (var i:int=0;i<RaysToShoot;i++)
{
var x = Mathf.Sin(angle);
var y = Mathf.Cos(angle);
angle += 2*Mathf.PI/RaysToShoot;
var dir:Vector3 = Vector3(x,0,y);
var hit : RaycastHit;
if (Physics.Raycast (transform.position, dir, hit, distance))
{
var tmp = lightmeshholder.transform.InverseTransformPoint(hit.point);
vertices2d[i] = Vector2(tmp.x,tmp.z);
}else{ // no hit
// Debug.DrawRay (transform.position, dir*distance, Color(1,1,0,1));
var tmp2 = lightmeshholder.transform.InverseTransformPoint(lightmeshholder.transform.position+dir);
vertices2d[i] = Vector2(tmp2.x,tmp2.z);
}
}
// build mesh
var uvs : Vector2[] = new Vector2[vertices2d.Length+1];
var newvertices : Vector3[] = new Vector3[vertices2d.Length+1];
for (var n : int = 0; n<newvertices.Length-1;n++)
{
newvertices[n] = new Vector3(vertices2d[n].x, 0, vertices2d[n].y);
// create some uv's for the mesh?
// uvs[n] = vertices2d[n];
}
//print("len"+newvertices.Length+" n:"+n);
triangles = new int[newvertices.Length*3];
// triangle list. noob triangulate
i = -1;
for (n=0;n<triangles.length-3;n+=3)
{
i++;
triangles[n+2] = newvertices.Length-1;
if (i>=newvertices.Length)
{
triangles[n+1] = 0;
//print ("hit:"+i);
}else{
triangles[n+1] = i+1;
}
triangles[n] = i;
}
i++;
// central point
newvertices[newvertices.Length-1] = new Vector3(0,0,0);
triangles[triangles.length-1] = newvertices.Length-1;
triangles[triangles.length-2] = 0;
triangles[triangles.length-3] = i-1;
// Create the mesh
//var msh : Mesh = new Mesh();
mesh.vertices = newvertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals(); // need?
mesh.RecalculateBounds(); // need ?
mesh.Optimize(); // need?
}
// helper functions
function getSin(radians:float) {return mSin[radians * mconst];}
function getCos(radians:float) {return mCos[radians * mconst];}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment