Point-to-Point Line-of-Site

Point-to-Point Line-of-Sight* [size=1]Oops[/size]
Man that sounds so cool.

edit:
Since this original post I have learnt of the world of Raycasting. You can see my attempts of it in this post:

Here’s the script. I have used Flash 8’s BitmapData and Rectangle classes, but the theory could easily be applied to another tile-based world without any of these, these classes were simply used to display everything.

Use the Arrow keys to move the character, and the character aims to the mouse position.

http://icio.co.uk/random/files/flash/sight.swf

import flash.display.BitmapData;
import flash.geom.Rectangle;

Stage.scaleMode = "noScale";
Mouse.hide();

//*********************************************************
// Variables
var tileSize:Number = 10;
var map:Array = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1], [1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // This is an awesomely-long line, y0. (762 characters before comment)
var baseBitmap:BitmapData = new BitmapData(map[0].length*tileSize, map.length*tileSize, false, 0xEEEEEE);	_root.attachBitmap(baseBitmap, _root.getNextHighestDepth());	// \
var objBitmap:BitmapData = new BitmapData(baseBitmap.width, baseBitmap.height, true, 0x00000000);			_root.attachBitmap(objBitmap, _root.getNextHighestDepth());		//  |- The bitmaps used in the game
var hitBitmap:BitmapData = new BitmapData(baseBitmap.width, baseBitmap.height, true, 0x0000FF00);			_root.attachBitmap(hitBitmap, _root.getNextHighestDepth());		// /
var tileRectangle:Rectangle = new Rectangle(0, 0, tileSize, tileSize); // A Rectangle object for drawing tiles onto the base bitmap.
var objects:Array = [];																						// An array of all the objects
var player:Object = {y:10*tileSize, x:1*tileSize, color:0xFFFF3399, xspd:0, yspd:0}; objects.push(player);	// The player object

for (y=0; y<map.length; y++) { for (x=0; x<map[0].length; x++) { if (map[y][x] == 1) {	// Cycle through the numbers in the map and process, then
	tileRectangle.x = x*tileSize; tileRectangle.y = y*tileSize;							// Reposition the tile rectangle
	baseBitmap.fillRect(tileRectangle, 0x999999);										// Draw it onto the base bitmap
} } }

// Draw the objects onto the object bitmap
function drawObjects() { objBitmap.fillRect(objBitmap.rectangle, 0x00000000); for (i in objects) { objBitmap.fillRect(new Rectangle(objects*.x, objects*.y, tileSize, tileSize), objects*.color); } objBitmap.fillRect(new Rectangle(_xmouse-1, _ymouse-1, 3, 3), 0xFFFF0000); }
	
//****************************************************************************************
// THE IMPORTANT FUNCTION - THIS IS WHAT IT'S ALL ABOUT !! *******************************
//****************************************************************************************
function lineOfSite(x1:Number, y1:Number, x2:Number, y2:Number) {
	var dist:Number = Math.sqrt(Math.pow(x2-x1, 2)+Math.pow(y2-y1, 2));		// The distance from the start point to end point
	var angle:Number = Math.atan((y2-y1)/(x2-x1));							// The angle from start point to end point (in radians)
		angle += x2-x1<0 ? Math.PI : 0;											// angle correction
		angle += x2-x1>=0 && y2-y1<0 ? 2*Math.PI : 0;							// angle correction
		hitBitmap.fillRect(hitBitmap.rectangle, 0x0000FF00);				// (Eye-Candy)

	var pos:Number = 0;														// Where we're going to start from on the line
	while (pos < dist) {													// Loop until we've tested further than, or to the same distance as, the total distance
		pos = pos+1<=dist ? pos+1 : dist;									// Move onto the test position. (Increasing/Decreasing '1' increases/decreases accuracy)
		var x:Number = Math.floor(pos*Math.cos(angle)+x1);						// Calculate the new x-coordinate for testing
		var y:Number = Math.floor(pos*Math.sin(angle)+y1);						// Calculate the new y-coordinate for testing
		if (map[Math.floor(y/tileSize)][Math.floor(x/tileSize)] == 1) {		// Check to see if the current point's relative tile is '1' or '0'
			break;															// Exit the loop if the tile is a '1'. Upon exitting, `x` and `y` maintain the coordinates
		}
			hitBitmap.setPixel32(x, y, 0xFF666666);							// (Eye-Candy)
	}
		hitBitmap.fillRect(new Rectangle((pos==dist?x2: x)-1, (pos==dist?y2:y)-1, 3, 3), pos==dist?0xFF0000FF:0xFF000000);		// (Eye-Candy)
	return pos==dist;														// Will return true if the target was reachable and false otherwise.
}
//****************************************************************************************

//*********************************************************
// Player Movement - Can't be assed commenting all of this.
function movePlayer() {
	player.xspd += Key.isDown(Key.RIGHT)?1: (Key.isDown(Key.LEFT)?-1:0); player.xspd = Math.abs(player.xspd)>tileSize?tileSize*player.xspd/Math.abs(player.xspd):player.xspd;
	player.yspd += Key.isDown(Key.DOWN)?1: (Key.isDown(Key.UP)?-1:0); player.yspd = Math.abs(player.yspd)>tileSize?tileSize*player.yspd/Math.abs(player.yspd):player.yspd;
	if (!Key.isDown(Key.RIGHT) && !Key.isDown(Key.LEFT)) { player.xspd *= Math.abs(player.xspd)<0.1?0:0.8; }
	if (!Key.isDown(Key.UP) && !Key.isDown(Key.DOWN)) { player.yspd *= Math.abs(player.yspd)<0.1?0:0.8; }
	var xL:Number = Math.floor(player.x / tileSize); var xR:Number = Math.floor((player.x+tileSize-1) / tileSize);
	var yT:Number = Math.floor(player.y / tileSize); var yB:Number = Math.floor((player.y+tileSize-1) / tileSize);
	var maxL:Number = xL*tileSize; var maxR:Number = xR*tileSize;
	var maxU:Number = yT*tileSize; var maxD:Number = yB*tileSize;
	var cL:Boolean = !(map[yT][xL-1]==1 || map[yB][xL-1]==1);
	var cR:Boolean = !(map[yT][xR+1]==1 || map[yB][xR+1]==1);
	var cU:Boolean = !(map[yT-1][xL]==1 || map[yT-1][xR]==1);
	var cD:Boolean = !(map[yB+1][xL]==1 || map[yB+1][xR]==1);
	var xmove:Number = player.xspd; var ymove:Number = player.yspd;
	if (!cL && player.x+player.xspd < maxL) { xmove = maxL - player.x; player.xspd = 0; }
	if (!cR && player.x+player.xspd > maxR) { xmove = maxR - player.x; player.xspd = 0; }
	if (!cU && player.y+player.yspd < maxU) { ymove = maxU - player.y; player.yspd = 0; }
	if (!cD && player.y+player.yspd > maxD) { ymove = maxD - player.y; player.yspd = 0; }
	player.x += xmove; player.y += ymove;
}

// Call Everything
_root.onEnterFrame = function() { movePlayer(); drawObjects(); lineOfSite(player.x+tileSize/2, player.y+tileSize/2, _xmouse, _ymouse); }

[size=1]Dam, smilies make it difficult to post code :P[/size]

Update:
Problems upload .swf at the moment, so here’s the .fla:

I wish I could do that… it makes me feel dumb.

Wah.

As for the little bug… couldn’t you try building two more lines, one pixel above, and one pixel below, and see if both of those hit. If they do… then it can’t see it, and you’ve reached the bug.

respect

Maybe im the only one who isnt impressed, no affence. But i made this like really easily in a WAY simpler way but the accuracey can be changed in a for() loop. This is good mathematicalled but when you make games using tiles how are you supposed to use this code?
I like the player movement though i would like to have a closed look at this, but using bitmap is all too easy.
Good job anyway :stuck_out_tongue:

The function is really easy to use in a game. As are tiles.
Plus, the tile-based option allows for easy making of a 3D engine.

And would you care to share your code with us?

great!

I dont really want to post my code cos it suks compared to yours. Im only 14 and unlike you I dont know loads of maths and stuff so mine is made up more of if() statments :P. I hope to post it once i finish my game which is called uNatural :smiley: tile based heaven

Meh, age doesn’t have anything to do with it. I’ve only just turned 17.
And I made the function so that it’s easy to incorporate into any tile-based game, by the way.

omg this is imbarrising (OMFG i need to learn to spell :s) but your code is simillar mine in that you use a while loop like me! :smiley:
I will post my code now yay! I assumed cos you have all this maths and stuff that it was complicated but I read through it in detail and found its really simple. Im sorry for being a noob and not looking before i open my stupid mouth.


map = [[1,1,1],[1,0,1],[1,1,1]]
function checkCollision(){
var radians = Math.atan2(_root._ymouse-start._y, _root._xmouse-start._x);
//angle in degrees (only if you need it, not used in rest of code)
var angle = (radians/Math.PI)*180;
var i:Number = 1;
var j:Number = 0;
while (j != 1) {
//xpos/ypos are exact possitions, make 5 smaller for even more accuracy
var xpos = plr._x+Math.cos(radians)*(5*i);
var ypos = plr._y+Math.sin(radians)*(5*i);
//xpos2/ypos2 are the possitions used when checking again map array
var xpos2 = xpos-(xpos%game.tile);
var ypos2 = ypos-(ypos%game.tile);
i++;
//
var mapCollide = map[ypos2/tileHeight][xpos2/tileWidth];
if ((xpos<0) || (xpos>stageWidth) || (ypos<0) || (ypos>stageHeight) || (mapCollide == 1)) {
//do you code for what happens when its off the stage or hits a block
j = 1;
}
}
}

Im sorry that mine is not so user friendly because I like re-wrote it from my code in the lil text box thingy here, so any mistakes will be contributed to that. Its wierd cos I like saw this post a day after i had just worked everything out! Cos i couldnt find anywhere to tell me, this sucks but shows me I can do it too :smiley: yay!

Nice code … but it is pretty much the same code that I posted at the start …