JavaScript: Collision detection - javascript

How does collision detection work in JavaScript?
I can't use jQuery or gameQuery - already using prototype - so, I'm looking for something very simple. I am not asking for complete solution, just point me to the right direction.
Let's say there's:
<div id="ball"></div>
and
<div id="someobject0"></div>
Now the ball is moving (any direction). "Someobject"(0-X) is already pre-defined and there's 20-60 of them randomly positioned like this:
#someobject {position: absolute; top: RNDpx; left: RNDpx;}
I can create an array with "someobject(X)" positions and test collision while the "ball" is moving... Something like:
for(var c=0; c<objposArray.length; c++){
........ and code to check ball's current position vs all objects one by one....
}
But I guess this would be a "noob" solution and it looks pretty slow.
Is there anything better?

Here's a very simple bounding rectangle routine. It expects both a and b to be objects with x, y, width and height properties:
function isCollide(a, b) {
return !(
((a.y + a.height) < (b.y)) ||
(a.y > (b.y + b.height)) ||
((a.x + a.width) < b.x) ||
(a.x > (b.x + b.width))
);
}
To see this function in action, here's a codepen graciously made by #MixerOID.

An answer without jQuery, with HTML elements as parameters:
This is a better approach that checks the real position of the elements as they are being shown on the viewport, even if they're absolute, relative or have been manipulated via transformations:
function isCollide(a, b) {
var aRect = a.getBoundingClientRect();
var bRect = b.getBoundingClientRect();
return !(
((aRect.top + aRect.height) < (bRect.top)) ||
(aRect.top > (bRect.top + bRect.height)) ||
((aRect.left + aRect.width) < bRect.left) ||
(aRect.left > (bRect.left + bRect.width))
);
}

The first thing to have is the actual function that will detect whether you have a collision between the ball and the object.
For the sake of performance it will be great to implement some crude collision detecting technique, e.g., bounding rectangles, and a more accurate one if needed in case you have collision detected, so that your function will run a little bit quicker but using exactly the same loop.
Another option that can help to increase performance is to do some pre-processing with the objects you have. For example you can break the whole area into cells like a generic table and store the appropriate object that are contained within the particular cells. Therefore to detect the collision you are detecting the cells occupied by the ball, get the objects from those cells and use your collision-detecting function.
To speed it up even more you can implement 2d-tree, quadtree or R-tree.

You can try jquery-collision. Full disclosure: I just wrote this and released it. I didn't find a solution, so I wrote it myself.
It allows you to do:
var hit_list = $("#ball").collision("#someobject0");
which will return all the "#someobject0"'s that overlap with "#ball".

Mozilla has a good article on this, with the code shown below.
2D collision detection
Rectangle collision
if (rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.height + rect1.y > rect2.y) {
// Collision detected!
}
Circle collision
if (distance < circle1.radius + circle2.radius) {
// Collision detected!
}

bcm's answer, which has 0 votes at this time, is actually a great, under-appreciated answer. It uses good old Pythagoras to detect when objects are closer than their combined bounding circles. Simple collision detection often uses rectangular collision detection, which is fine if your sprites tend to be, well, rectangular. If they are circular (or otherwise less than rectangular), such as a ball, an asteroid, or any other shape where the extreme corners are usually transparent, you may find this efficient routine to be the most accurate.
But for clarity, here is a more fully realized version of the code:
function doCollide(x1, y1, w1, x2, y2, w2) {
var xd = x1 - x2;
var yd = y1 - y2;
var wt = w2 + w1;
return (xd * xd + yd * yd <= wt * wt);
}
Where the parameters to pass in are the x,y and width values of two different sprite objects.

This is a lightweight solution I've come across -
function E() { // Check collision
S = X - x;
D = Y - y;
F = w + W;
return (S * S + D * D <= F * F)
}
The big and small variables are of two objects, (x coordinate, y coordinate, and w width)
From here.

//Off the cuff, Prototype style.
//Note, this is not optimal; there should be some basic partitioning and caching going on.
(function () {
var elements = [];
Element.register = function (element) {
for (var i=0; i<elements.length; i++) {
if (elements[i]==element) break;
}
elements.push(element);
if (arguments.length>1)
for (var i=0; i<arguments.length; i++)
Element.register(arguments[i]);
};
Element.collide = function () {
for (var outer=0; outer < elements.length; outer++) {
var e1 = Object.extend(
$(elements[outer]).positionedOffset(),
$(elements[outer]).getDimensions()
);
for (var inner=outer; inner<elements.length; innter++) {
var e2 = Object.extend(
$(elements[inner]).positionedOffset(),
$(elements[inner]).getDimensions()
);
if (
(e1.left+e1.width)>=e2.left && e1.left<=(e2.left+e2.width) &&
(e1.top+e1.height)>=e2.top && e1.top<=(e2.top+e2.height)
) {
$(elements[inner]).fire(':collision', {element: $(elements[outer])});
$(elements[outer]).fire(':collision', {element: $(elements[inner])});
}
}
}
};
})();
//Usage:
Element.register(myElementA);
Element.register(myElementB);
$(myElementA).observe(':collision', function (ev) {
console.log('Damn, '+ev.memo.element+', that hurt!');
});
//detect collisions every 100ms
setInterval(Element.collide, 100);

This is a simple way that is inefficient, but it's quite reasonable when you don't need anything too complex or you don't have many objects.
Otherwise there are many different algorithms, but most of them are quite complex to implement.
For example, you can use a divide et impera approach in which you cluster objects hierarchically according to their distance and you give to every cluster a bounding box that contains all the items of the cluster. Then you can check which clusters collide and avoid checking pairs of object that belong to clusters that are not colliding/overlapped.
Otherwise, you can figure out a generic space partitioning algorithm to split up in a similar way the objects to avoid useless checks. These kind of algorithms split the collision detection in two phases: a coarse one in which you see what objects maybe colliding and a fine one in which you effectively check single objects.
For example, you can use a QuadTree (Wikipedia) to work out an easy solution...
Take a look at the Wikipedia page. It can give you some hints.

hittest.js; detect two transparent PNG images (pixel) colliding.
Demo and download link
HTML code
<img id="png-object-1" src="images/object1.png" />
<img id="png-object-2" src="images/object2.png" />
Init function
var pngObject1Element = document.getElementById( "png-object-1" );
var pngObject2Element = document.getElementById( "png-object-2" );
var object1HitTest = new HitTest( pngObject1Element );
Basic usage
if( object1HitTest.toObject( pngObject2Element ) ) {
// Collision detected
}

Related

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

Using a grid of points to detect if a object is in range

I have a grid of points and I made them using a for loop at the beginning of my app. Each point has two arrays, one is named objectsAroundMe and the other is called pointsAroundMe.
The objective is to detect if the object is near the point (using for loop for both objects and points)
After detection then if the object is in range we push it to the point.objectsAroumdMe array.
I have all of this fine and working, but the problem is getting the point to release the reference when the object is no longer near, I've tried running an if statement to do it and make the reference null but it doesn't work. If there were an efficient way of doing this that made it so only one reference was moving from array to array then that would be perfect. next I'm gonna try using array.splice and slice to copy amd paste references. But for now I've tried using array.filter and indexof and findindex none worked. But I'm newish to classes so if their is a difference between using a for loop iteration Id and using the"this" statement to clarify the object then please give me an example of how I would find the index of a "this" object and delete it's reference from the point array.
onHitTest(){
for (let ii = 0; ii < jsEngine.pointGrid.length; ii++) {
let point = jsEngine.pointGrid[ii];
let distanceBetween = calcTotalDistance(this.transform.x,this.transform.y,point.x,point.y);
let pointPosition = point.x + point.y;
if (!point.objectsAroundMe.includes(this)) {
if ( distanceBetween < mapWidth/density*1.4) {
point.objectsAroundMe.push(this);
this.hitTestArray = point.objectsAroundMe;
this.pointArray = point.pointsAroundMe;
//console.log(this.hitTestArray);
}
if(point.objectsAroundMe.includes(this)) {
if (pointPosition - distanceBetween > 100000) {
let indx= point.objectsAroundMe.indexOf(this);
point.objectsAroundMe[indx] = null;
}
}
}
}
//// second for loop for hit testing the passed array from the point to the object.
for (let i = 0 ; i < this.hitTestArray.length; i++){
let hitTestObject = this.hitTestArray[i];
if(hitTestObject.transform=== null)
continue;
if(hitTestObject === this)
continue;
let distance = calc_distance(this.transform,hitTestObject.transform);
if (distance < hitTestObject.transform.width + hitTestObject.transform.width
&& distance < this.transform.height + this.transform.height){
//console.log("hit!")
}
}
}
Mapwidth = 1000000 and density is 10.
distanceBetween: The distance between the object and the point using: return Math.sqrt((x1 - x2)**2 + (y1 - y2)**2);
this = the object in question (to avoid double for loop)
pointGrid= a grid of points with a total of 90 points equally spaced by mapwidth/density
I found out after finally giving up on this technique after 2 weeks that it really was not performing as well as expected, now I am going to take a similar approach and I will upload my code via: (functions and order of operation) shortly.

Drawing any function's graph using Javascript

I'm taking a javascript class for my bachelor in CS, and the IDE we're using (it's an educative IDE developed by one of the graduates here, called codeboot) has a "turtle drawing" feature. Basically, it works using several basic commands;
Forward; fd(x), Back; bk(x), rotate right; rt(x), rotate left; lt(x), pen up; pu() and pen down; pd(). Where x represent pixels.
For example, to have it draw a circle, one would write something like:
for (var n = 0; n < 360; n++) {
fd(1);
rt(1);
}
I'm trying to have it draw graphs for basic functions,but the only way I found how to do it is to have it draw a single point for each 0.005x (so it looks like a continuous line when zoomed out), but it makes the execution extremely slow, and you can kind of see it's not legit. How would I go about having just a continuous line instead of individually plotting each point?
Screenshot of what it looks like using my first technique:
Screenshot of what it looks like using my first technique:
and the JS code for said technique:
var x = -9;
var traceDown = function(d) {
var y = Math.cos(x) * 30;
setpw(3);
pu();
fd(y);
pd();
fd(1);
pu();
bk(1 + (y));
rt(90);
fd(d);
lt(90);
pd();
x += 0.0005;
};
pu();
lt(90);
fd(90);
lt(90);
fd(45);
pd();
rt(180);
while (x < 9) {
traceDown(0.005);
}

Read & change rotate value of an Element in SnapSVG

Pls Check out this incomplete fiddle (https://jsfiddle.net/JayKandari/srqLLd97/).
Markup:
<svg id="paper" ></svg>
<div id="workingArea">
<button onclick="calculateTransform()">Get Transform</button>
<div class="results">
Transform: <span class="transformResult"></span>
</div>
<button onclick="rotateText('add')">Rotate + 10 Degrees</button>
<button onclick="rotateText('sub')">Rotate - 10 Degrees</button>
</div>
JS Code:
console.clear();
var paper = Snap("#paper");
var t = paper.text(0, 0, "SnapSVG is Awesome !");
//Translate to 250, 250 & Scale by 2 & 45Deg Rotate
t.transform('T250,250 S2 R45');
// Enable Drag
t.drag();
var calculateTransform = function() {
var currentTransform = t.transform().string;
console.log('test', currentTransform)
$('.results .transformResult').html(currentTransform)
}
var rotateText = function(op) {
// Read Current Rotate Value here.
var rotateVal = 0;
// add/Subtract based on option
if(op == 'add'){
rotateVal += 10;
}else if(op == 'sub') {
rotateVal -= 10;
}
// Calculate newTransform here.
var newTransform = 'T250,250 R' + rotateVal;
// Update object's rotation here.
t.attr({transform: newTransform });
}
Problems:
Read current transformation applied to Element.
Rotate current Element to a specific value.
What you probably are after in your mind is something like this...
var rotateVal = t.transform().localMatrix.split().rotate;
localMatrix gives the matrix specifically applied to that element.
globalMatrix gives the total matrix applying to that element (eg matrices from outer containers).
diffMatrix gives the difference between localMatrix and globalMatrix.
split() ( docs here ) breaks down that matrix into its elements. So we can find 'rotate' from here.
example fiddle
So this is the answer I think you are after.
However, I would actually design the code the slightly different if this is just a minimal example of a more complex design.
Rather than doing something like transform().localMatrix.split().rotate, I would store your own transform components. So something like...
var rotate = 45;
var tx = 250;
var ty = 250;
...then in func
rotate += +10;
var newTransform = 'T'+tx+','+ty+'R'+rotate;
The reason for this, is that you are now controlling precisely the values, and its not really prone to errors (from the lib or misunderstanding about matrices). If you look at your matrix values you display, you will note there are subtle rounding differences, plus with complex matrix calculations, the values aren't quite what we expect and don't always split in a nice easy way that makes sense.
So if this is the main example, the first bit of code will probably suffice. If you are doing more complex transformations, I would think about controlling the values yourself and building up the transform string like the latter example.

Canvas Maze character's distance from walls

I am working on a 2D maze game with a torch effect in canvas without the use of any raytracing. Everything is working great, however the torch effect's algorithm is causing immense lags in several browsers and computers. (It is weird as well, that the game runs smoother on older computers. The funniest is, IExplorer runs the game without any lags, while mozzila dies on every move..)
My general idea for solving this problem was, to get how far the character is from the walls (4 functions) and make the rest of the maze grey.
Here is an example with the Northern wall detection:
http://webprogramozas.inf.elte.hu/~ce0ta3/beadando/maze_example.png
And an example how it is working at the moment and what I would like to achieve without lag issues.
http://webprogramozas.inf.elte.hu/~ce0ta3/beadando/ce0ta3_html5_maze.html
As I mentioned above, the algorithm that tracks the character's distance from the walls is causing incredible lags.
//Get the character's X,Y position as parameter
function distanceFromNorth (posX,posY)
{
distNorth = 0;
var l = false;
//Start getting charSize x 1 px lines from the character position towards the up, until we reach the max viewDistance or we find a black pixel in the charSize x 1 line.
for (var i = posY; i > posY - viewDistance && !l; i--)
{
var mazeWallData = context.getImageData(posX, i, charSize, 1);
var data = mazeWallData.data;
//Check if there are any black pixels in the line
for (var j = 0; j < 4 * charSize && !l; j += 4)
{
l = (data[j] === 0 && data[j + 1] === 0 && data[j + 2] === 0);
}
distNorth++;
}
return distNorth;
}
I am fairly sure, that the ctx.getImageData() is the most costly method in this linear search and if I only requested this method once for a charSize x viewDistance rectangle, and then check for black pixels in that huge array, then the lag could be reduced greatly. However, I still want to keep searching in lines, because finding only one black pixel will return false distNorth value.
I would be grateful if anyone could convert my code into the form I mentioned in the previous paragraph.
Assuming the image data isnt changing then you can precompute all the pixel values that have black pixel. Then use simple binary search on it to get the any black pixels in the given range.
Algorithm : -
cols[];
rows[];
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
if(pixel(j,i)==black) {
row[i].add(j);
col[j].add(i);
}
}
}
for query on (x,y) :
distance = binarysearch(col[x],y,y-distance) - y

Categories

Resources