Created
April 10, 2025 19:28
-
-
Save unitycoder/133de06ebd227c324ec3e7d32877d24e to your computer and use it in GitHub Desktop.
unityscript 2d voxel travelsal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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