Negative coordinates in a grid based game - javascript

I have written a small 2D game in javascript that uses a grid where the player starts at position [0,0] and can move an almost infinite distance in either direction.
Now I want to implement A* pathfinding, but I'm having some problems finding the best way to store the world with all it's different obstacles, enemies and terrain. This is what I have tried or thought about so far.
Array of arrays
Here I store the world in an array of arrays [x][y].
var world = [[]];
world[312][11] = 0;
world[312][12] = 0;
world[312][13] = 1;
world[312][14] = 1;
...
This works great with A* pathfinding! It's easy and very fast to access a specific coordinate and populate the world. In the example above I just store passable (0) or impassable (1) terrain, but I can store pretty much whatever I want there. However, this doesn't work very well with negative coordinates like if my players is at [-12][-230]. Negative keys in a javascript array isn't actually part of the array, they won't be included in world.length or world[3].length and from what I understand, it's overall bad practice and might have some impact on the performance as well. I read somewhere that if you are using negative keys in your array, you are doing it wrong.
I would still not pass the entire world into the A* function for obvious reasons. Just a small part close to my player, but the coordinates would correspond to the positions in the array which is easy to work with.
A separate array of arrays just for A* pathfinding
This is where I'm at right now. I have a separate 50x50 grid called pathMap = [[]], that is only used for pathfinding.
var pathMap = [[]];
pathMap[0][0] = 0;
pathMap[0][1] = 0;
pathMap[0][2] = 1;
pathMap[0][3] = 1;
...
It starts at pathMap[0][0] and goes to pathMap[50][50] and is working as an overlay on my current position where I (as the player) will always be in the center position. My real coordinates may be something like [-5195,323], but it translates to pathMap[25][25] and everything close to me is put on the pathMap in relation to my position.
Now this works, but it's a huge mess. All the translations from one coordinate to another back and forth makes my brain hurt. Also, when I get the path back from A*, I have to translate each step of it back to the actual position my element should move to in the real world. I also have to populate the same object into 2 different grids every update which hurts performance a bit as well.
Array of objects
I think this is where I want to be, but I have some issues with this as well.
var world = [];
world[0] = { x: -10, y: 3, impassable: 0 };
world[1] = { x: -10, y: 4, impassable: 0 };
world[2] = { x: -10, y: 5, impassable: 1 };
world[3] = { x: -10, y: 6, impassable: 1 };
...
Works great with negative x or y values! However, it's not as easy to find for instance [10,3] in this array. I have to loop through the entire array to look for an object where x == 10 and y == 3 instead of the very easy and fast approach world[10][3] in the first example. Also, I can't really rely on the coordinates being in the right order using this version, sorting becomes harder, as does other things that was a lot easier with the array of arrays.
Rebuild the game to always be on the positive side
I would prefer not to do this, but I have considered placing the players starting position at something like [1000000,1000000] instead, and making negative coordinates off limits. It seems like a failure if I have to remove the vision I have of endlessness just to make the pathfinding work with less code. I know there will always be some upper or lower limits anyways, but I just want to start at [0,0] and not some arbitrary coordinate for array related reasons.
Other?
In javascript, is there another option that works better and is not described above? I'm very open to suggestions!
Is there a best practice for similar cases?

You have three coordinates system you must distinguish :
the world coordinates.
the world model / path-finding (array) coordinates.
the screen coordinates.
The screen coordinates system depends upon :
the viewport = the canvas. (width, height in pixels).
a camera = (x,y) center in world coordinates + a viewWidth (in world coordinates).
To avoid headaches, build a small abstraction layer that will do the math for you.
You might want to use Object.defineProperty to define properties, that will provide a fluent interface.
var canvas = ... ;
var canvasWidth = canvas.width;
var canvasHeigth = canvas.heigth;
var world = {
width : 1000, // meters
height : 1000, // meters
tileSize : 0.5, // height and width of a tile, in meter
model : null, // 2D array sized ( width/tileSize, XtileSize )
};
// possibles world coordinates range from -width/2 to width/2 ; - height/2 height/2.
var camera = {
x : -1,
y : -1,
viewWidth : 10, // we see 10 meters wide scene
viewHeight : -1 // height is deduced from canvas aspect ratio
};
camera.viewHeight = camera.viewWidth * canvasWidth / canvasHeight ;
Then your character looks like :
// (x,y) is the center of the character in centered world coordinates
// here (0,0) means (500,500) world coords
// (1000,1000) array coords
// (320, 240) screen coords in 640X480
function /*Class*/ Character(x, y) {
var _x=x;
var _y=y;
var _col=0;
var _row=0;
var _sx=0.0;
var _sy=0.0;
var dirty = true;
Object.defineProperty(this,'x',
{ get : function() {return _x; }
set : function(v) { _x=v;
dirty=true; } });
Object.defineProperty(this,'x',
{ get : function() {return _y; }
set : function(v) { _y=v;
dirty=true; } });
Object.defineProperty(this,'col',
{ get : function() {
if (dirty) updateCoords();
return _col; } });
Object.defineProperty(this,'row',
{ get : function() {
if (dirty) updateCoords();
return _row; } });
Object.defineProperty(this,'sx',
{ get : function() {
if (dirty) updateCoords();
return _sx; } });
Object.defineProperty(this,'sy',
{ get : function() {
if (dirty) updateCoords();
return _sy; } });
function updateCoords() {
_row = ( ( _x + 0.5 * world.width )/ world.tileSize ) | 0 ;
_col = ( ( _x + 0.5 * world.height )/ world.tileSize ) | 0 ;
_sx = canvasWidth * ( 0.5 + ( _x - camera.x ) / camera.viewWidth ) ;
_sy = canvasHeight * ( 0.5 + ( _y - camera.y ) / camera.viewHeight ) ;
dirty = false;
}
}

Related

3D point rotation algorithm

I'm currently working on a JavaScript project which involves 3D point rotation. Using simple trigonometry, I have sketched my own 3D point rotation algorithm, but I have to deal with a huge amount of data (+300 000 points) and my function slows down the runtime substantially (the FPS rate drops from 60 to 12).
I'm looking for another 3D point rotation ALGORITHM which...
rotates points around origin by X, Y and Z axes' angles (PITCH, YAW and ROLL)
has a quite good efficiency (don't worry about this too much, it will always be faster than mine)
is written in JavaScript, C-like code or pseudo-code
Any help will be greatly appreciated :)
Context: 3D point cloud renderer (I want every point to be rotated)
A rotated vector can be described as a product of a rotation matrix with that vector. The German Wikipedia page on pitch, roll and yaw describes the rotation matrix for given Euler rotation angles.
With that information, the rotation of all points with the same angles can be written as JavaScript function, where the points array is global:
function rotate(pitch, roll, yaw) {
var cosa = Math.cos(yaw);
var sina = Math.sin(yaw);
var cosb = Math.cos(pitch);
var sinb = Math.sin(pitch);
var cosc = Math.cos(roll);
var sinc = Math.sin(roll);
var Axx = cosa*cosb;
var Axy = cosa*sinb*sinc - sina*cosc;
var Axz = cosa*sinb*cosc + sina*sinc;
var Ayx = sina*cosb;
var Ayy = sina*sinb*sinc + cosa*cosc;
var Ayz = sina*sinb*cosc - cosa*sinc;
var Azx = -sinb;
var Azy = cosb*sinc;
var Azz = cosb*cosc;
for (var i = 0; i < points.length; i++) {
var px = points[i].x;
var py = points[i].y;
var pz = points[i].z;
points[i].x = Axx*px + Axy*py + Axz*pz;
points[i].y = Ayx*px + Ayy*py + Ayz*pz;
points[i].z = Azx*px + Azy*py + Azz*pz;
}
}
Most of that is setting up the rotation matrix as described in the article. The last three lines inside the loop are the matrix multiplication. You have made a point of not wanting to get into matrices, but that's hardly intimidating, is it? Sooner or later you will encounter more matrices and you should be prepared to deal with them. The stuff you need – multiplication, mainly – is simple. The more complicated stuff like inverting matrices is not needed for your requirements.
Anyway, that performs reasonably fast for 300,000 points. I was able to rotate a point cloud of that size and render it on a 1000px &times 1000px canvas in about 10ms.
From wikipedia:
If you multiply your points by each of these matrices they will be rotated by the amount you want.
For example, if I want to rotate point [1, 0, 0] by 90° around the z axis (in the xy plane), sin(90) = 1 and cos(90) = 0 so you get this:
| 0 -1 0 | |1| |0|
| 1 0 0 | * |0| = |1|
| 0 0 1 | |0| |0|

Handling object going off plane - Three.js

I am working on a project of mine that has a plane with a grid that allows you to just snap on objects. Where I am stuck is, I can't seem to figure out how to tell if an object overhangs the plane, than don't add it if the user triggers a click.
Currently:
event.preventDefault();
this.mouse.x = ( event.clientX / this.renderer.domElement.width ) * 2 - 1;
this.mouse.y = - ( event.clientY / this.renderer.domElement.height ) * 2 + 1;
this.raycaster.setFromCamera( this.mouse, this.camera.cam );
var intersects = this.raycaster.intersectObjects( this.blocks );
if ( intersects.length > 0 ) {
var intersect = intersects[ 0 ];
if ( this.isShiftDown ) { // see if shift is down to remove
if ( intersect.object != this.plane ) {
this.scene.remove( intersect.object );
this.blocks.splice( this.blocks.indexOf( intersect.object ), 1 );
}
} else if(intersect.object == this.plane ){ // add room to canvas
var voxel = new THREE.Mesh( this.room.cubeGeometry.clone(), this.room.cubeMaterial.clone() );
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
this.scene.add( voxel );
this.blocks.push( voxel );
var domEvents = new THREEx.DomEvents(this.camera.cam, this.renderer.domElement)
console.log(voxel);
// DOM events for inside 3d rendering
domEvents.addEventListener(voxel, 'mouseover', this.onDocumentMouseOverCube.bind(this),false);
domEvents.addEventListener(voxel, 'mouseout', this.onDocumentMouseOutCube.bind(this),false);
}
this.render();
}
So what happens here is: first we get our mouse position - send it to our raycaster to setup. Than we check to see if we have intersected any objects. Now this is all great except. If an object is bigger than a single 20x20 slot, it will over hang the plane canvas. I want to be able to deny the user from placing anything if the object over hangs our plane. The plane object is always at position this.blocks[0] So as you can see in the else if we check to see if its on plane. But I am not accounting for, excess of object overhang
Any suggestions on the best way to accomplish this?
Compute the bounding box of your object.
var voxel = new THREE.Mesh( this.room.cubeGeometry.clone(), this.room.cubeMaterial.clone() );
voxel.geometry.computeBoundingBox();
When the user clicks on the plane, move the object there and compare the intersection coordinate (x,z) with the bounding box min and max (x,z) values.
If one of the bounding box values is bigger or smaller than the plane, then it overhangs.
This is just an idea, I have not tested this. If you need help with the code, let me know.
As per Falk's request to share my function I created off of his answer.
When dealing with our volex on creation the key here is to set a cube.geometry.computeBoundingBox() - on every volex creation. Reason be, by default we wont have a min or max bounding size. Which to be able to grab our depth and width of our object this is crucial.
We already know that the user is on a PlaneBufferGeometry or plane in my case. So we pass our volex and plane to a custom function called checkOverlapPlane(volex,plane)
Now in a jiffy, this function grabs our plane and volex max and min boundaries. In addition we grab the volex position which is crucial to figure out the volex position on the x and z axis. We than convert all of our variables to positive numbers only for the fact that it makes it easy to deal with positive numbers and add our different x and z. After converting our intagers to positive intagers we than get our total position of the min and max volex z and the volex x. Which in turn is compared with our plane min x z and max x z to return whether the volex is hanging off of our plane.
Here is the code:
checkOverlapPlane: function(voxel,plane) { // THIS IS USED TO TELL US IF WE ARE OUTSIDE THE GRIDS BOUNDRIES
// Now before doing anything lets check out min and max boundries to see if our x and z are either bigger or smaller
var planeMX = plane.geometry.boundingBox.max.x;
var planeMZ = plane.geometry.boundingBox.max.z;
var planemX = plane.geometry.boundingBox.min.x;
var planemZ = plane.geometry.boundingBox.min.z;
//lets get our voxel min and max vars
var voxelMX = voxel.geometry.boundingBox.max.x;
var voxelMZ = voxel.geometry.boundingBox.max.z;
var voxelmX = voxel.geometry.boundingBox.min.x;
var voxelmZ = voxel.geometry.boundingBox.min.z;
// we need to get our voxel position ad to do some math to see if voxel min and max do not go beyound our boundries for our plane
var voxelPX = voxel.position.x;
var voxelPZ = voxel.position.z;
// lets do some calucation and condition
// first lets turn everything over to positive for some easy calcuation
voxelPX = Math.abs(voxelPX); // positions on world view
voxelPZ = Math.abs(voxelPZ);
voxelmZ = Math.abs(voxelmZ); // min and max values of our voxel
voxelmX = Math.abs(voxelmX);
voxelMZ = Math.abs(voxelMZ);
voxelMX = Math.abs(voxelMX);
planemZ = Math.abs(planemZ); // min and max values of our plane our voxels are on
planemX = Math.abs(planemX);
planeMZ = Math.abs(planeMZ);
planeMX = Math.abs(planeMX);
// now lets calucate X and Z to see if total goes over any of our planes min and max X or Y - remember everything is position but so we can add it to see if its above - we will not actually change the direct objects values
var totalPositionVoxelminZ = (voxelPZ + voxelmZ);
var totalPositionVoxelminX = (voxelPX + voxelmX);
var totalPositionVoxelMAXZ = (voxelPZ + voxelMZ);
var totalPositionVoxelMAXX = (voxelPX + voxelMX);
// now lets compare
if(totalPositionVoxelminZ > planemZ)
return false; // do nothing
else if(totalPositionVoxelminX > planemX)
return false; // do nothing
else if(totalPositionVoxelMAXZ > planeMZ)
return false; // do nothing
else if(totalPositionVoxelMAXX > planeMX)
return false; // do nothing
else
return true;
},
Here is the results:
Feel free to use and incorporate this in any project!

Incorrect angle, wrong side calculated

I need to calculate the angle between 3 points. For this, I do the following:
Grab the 3 points (previous, current and next, it's within a loop)
Calculate the distance between the points with Pythagoras
Calculate the angle using Math.acos
This seems to work fine for shapes without angels of over 180 degrees, however if a shape has such an corner it calculates the short-side. Here's an illustration to show what I mean (the red values are wrong):
This is the code that does the calculations:
// Pythagoras for calculating distance between two points (2D)
pointDistance = function (p1x, p1y, p2x, p2y) {
return Math.sqrt((p1x - p2x)*(p1x - p2x) + (p1y - p2y)*(p1y - p2y));
};
// Get the distance between the previous, current and next points
// vprev, vcur and vnext are objects that look like this:
// { x:float, y:float, z:float }
lcn = pointDistance(vcur.x, vcur.z, vnext.x, vnext.z);
lnp = pointDistance(vnext.x, vnext.z, vprev.x, vprev.z);
lpc = pointDistance(vprev.x, vprev.z, vcur.x, vcur.z);
// Calculate and print the angle
Math.acos((lcn*lcn + lpc*lpc - lnp*lnp)/(2*lcn*lpc))*180/Math.PI
Is there something wrong in the code, did I forget to do something, or should it be done a completely different way?
HI there your math and calculations are perfect. Your running into the same problem most people do on calculators, which is orientation. What I would do is find out if the point lies to the left or right of the vector made by the first two points using this code, which I found from
Determine which side of a line a point lies
isLeft = function(ax,ay,bx,by,cx,cy){
return ((bx - ax)*(cy - ay) - (by - ay)*(cx - ax)) > 0;
}
Where ax and ay make up your first point bx by your second and cx cy your third.
if it is to the left just add 180 to your angle
I've got a working but not necessarily brief example of how this can work:
var point1x = 0, point1y = 0,
point2x = 10, point2y = 10,
point3x = 20, point3y = 10,
point4x = 10, point4y = 20;
var slope1 = Math.atan2(point2y-point1y,point2x-point1x)*180/Math.PI;
var slope2 = Math.atan2(point3y-point2y,point3x-point2x)*180/Math.PI;
var slope3 = Math.atan2(point4y-point3y,point4x-point3x)*180/Math.PI;
alert(slope1);
alert(slope2);
alert(slope3);
var Angle1 = slope1-slope2;
var Angle2 = slope2-slope3;
alert(180-Angle1);
alert(180-Angle2);
(see http://jsfiddle.net/ZUESt/1/)
To explain the multiple steps the slopeN variables are the slopes of the individual line segments. AngleN is the amount turned at each junction (ie point N+1). A positive angle is a right turn and a negative angle a left turn.
You can then subtract this angle from 180 to get the actual interior angle that you want.
It should be noted that this code can of course be compressed and that five lines are merely outputting variables to see what is going on. I'll let you worry about optimizing it for your own use with this being a proof of concept.
You need to check boundary conditions (apparently, if points are colinear) and apply the proper calculation to find the angle.
Also, a triangle can't have any (interior) angle greater than 180 degress. Sum of angle of triangle is 180 degrees.

Sub pixel antialiasing canvas pixel shift algorithms in javascript

I'm trying to work out a way of doing pseudo 3d, distorting textures with the javascript canvas.
The best method for my needs so far has been to use displacement maps which I'm largely following from this tutorial and source code example.
The basic principle is to use the channel level (RGBA) from a selected pixel in the displacement map then applying a pixel shifting algorithm... all good so far.
The problem is that this method of shifting the texture image's pixels is very binary and renders a slightly 'jagged' edge due to the fact that - it's simply shifting 'full' pixels.
When compared to PhotoShop or some of the ImageMagick examples here the javascript method looks much less realistic. This is due to PS & IMs sub-pixel processing abilities whereby medians can be derived for inter-pixel data.
Question: Can anyone suggest a step which can be integrated into my algorithm to produce a gaussian/aliased smoothness to the output?
Perhaps I can simply run the imagedata through an FFT and back again? are there any examples of this in action?
I'm a little stumped and would very much appreciate some pointers.
1) You are mentionning two very differents algorithms : Displacement mapping is a 3D technique, so it involves 'Z', and projection, and the other one is a 2D pixel shifting algorithm way way simpler.
(The soundstep link provided uses the word 'displacement mapping', yet it is a pixel shifting
technique.)
2) Whatever the size of your MVC project, the algorithm should be isolated, and have a signature like :
var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset)
and either return a new canvas OR change sourceCanvas in place.
If there's no such function, please do not talk about MVC, unless the M stands for 'Mess'. ;-) -
3) The algorithm is quite simple in fact, you must iterate through the destination pixel and look the color of the pixel they should come from (and not the other way around) :
var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset) {
var shiftXY = { xS:0, yS:0 };
var shiftCanvasWidth = shiftCanvas.width ;
var shiftCanvasHeight = shiftCanvas.height;
for ( var x=0 ; x < shiftCanvasWidth ; x ++) {
for ( var y = 0 ; y < shiftCanvasHeight ; y++ ) {
readShift ( shiftCanvas, x, y, shiftXY );
var sourceColor = readPixelColor ( sourceCanvas,
xOffset + shiftXY.xS,
yOffset + shiftXY.yS) ;
writePixel(sourceCanvas, xOffset + x , yOffset + y, sourceColor );
}
}
};
// sourceColor represents the color within a 32 bits integer (r,g,b,a * 8 bits).
it would be too long to write everything here but :
-- within pixelShift loop you should not deal with the source canvas, but a with 32 bits performance array.
-- the shift canvas should be converted ONCE into a Int8Array array, and stored as this.
this array size is shiftWidth * shiftHeight
odd index contains x shift, even contains y shift.
the array is pre-processed, and contains the shift value - 128.
for this shiftArray :
shiftX (x,y) = shiftArray [ 2 * (x + y * shiftWidth) ] ;
shiftY (x,y) = shiftArray [ 2 * (x + y * shiftWidth) + 1 ] ;
-- So pixelShift should look like :
var pixelShift = function (sourceCanvas,
shiftArray, shiftWidth, shiftHeight, xOffset, yOffset) {
[ Get a 32 bit performance array out of the canvas's target area ]
[ process this array using the shiftArray ]
[ write back the processed array onto the canvas's target area ]
}
-- And the core loop can be processed in a linear fashion :
var areaSize = shiftWidth * shiftHeight ;
for ( pixelIndex=0 ; pixelIndex < areaSize ; pixelIndex++ ) {
var linearShift = shiftArray [ 2*pixelIndex ]
+ shiftWidth * shiftArray [ 2*pixelIndex + 1 ] ;
targetAreaArray [ pixelIndex ] = targetAreaArray [ pixelIndex + linearShift ] ;
}
-- Rq : you might want to perform a boundary check on (pixelIndex + linearShift) within [0, areaSize[.
-- I think now you cannot get any faster.
The performance bottleneck will be the getImageData and putImageData you need to get/put the target area, but as far as i know, there's no other way to get a binary view on a Canvas than those two slooooow functions.

raphaelJS 2.1 animate along path

I want to animate a path (actually a set of paths, but I'll get to that) along a curved path.
RaphaelJS 2 removed the animateAlong method, for reasons I haven't been able to discern. Digging into the Raphael documentation's gears demo as abstracted by Zevan, I have got this far:
//adding a custom attribute to Raphael
(function() {
Raphael.fn.addGuides = function() {
this.ca.guide = function(g) {
return {
guide: g
};
};
this.ca.along = function(percent) {
var g = this.attr("guide");
var len = g.getTotalLength();
var point = g.getPointAtLength(percent * len);
var t = {
transform: "t" + [point.x, point.y]
};
return t;
};
};
})();
var paper = Raphael("container", 600, 600);
paper.addGuides();
// the paths
var circ1 = paper.circle(50, 150, 40);
var circ2 = paper.circle(150, 150, 40);
var circ3 = paper.circle(250, 150, 40);
var circ4 = paper.circle(350, 150, 40);
var arc1 = paper.path("M179,204c22.667-7,37,5,38,9").attr({'stroke-width': '2', 'stroke': 'red'});
// the animation
// works but not at the right place
circ3.attr({guide : arc1, along : 1})
.animate({along : 0}, 2000, "linear");
http://jsfiddle.net/hKGLG/4/
I want the third circle to animate along the red path. It is animating now, but at a distance from the red path equal to the third circle's original coordinates. The weird thing is that this happens whether the transform translate in the along object is relative (lowercase "t") or absolute (uppercase "T"). It also always animates in the same spot, even if I nudge it with a transform translation just before the animate call.
Any help very appreciated. I just got off the boat here in vector-land. Pointers are helpful--a working fiddle is even better.
You're just a hop, skip, and jump away from the functionality that you want. The confusion here concerns the interaction between transformations and object properties -- specifically, that transformations do not modify the original object properties. Translating simply adds to, rather than replaces, the original coordinates of your circles.
The solution is extremely straightforward. In your along method:
this.ca.along = function(percent) {
var box = this.getBBox( false ); // determine the fundamental location of the object before transformation occurs
var g = this.attr("guide");
var len = g.getTotalLength();
var point = g.getPointAtLength(percent * len);
var t = {
transform: "...T" + [point.x - ( box.x + ( box.width / 2 ) ), point.y - ( box.y + ( box.height / 2 ) )] // subtract the center coordinates of the object from the translation offset at this point in the guide.
};
return t;
Obviously, there's some room for optimization here (i.e., it might make sense to create all your circles at 0,0 and then translate them to the display coordinates you want, avoiding a lot of iterative math). But it's functional... see here.
One other caveat: the ...T translation won't effect any other transforms that have already been applied to a given circle. This implementation is not guaranteed to play nicely with other transforms.

Categories

Resources