Working on a little "zombies" or "tag you're it" or "ew! you got cooties"-styled game where each AI object (a person, basically) runs around randomly. There is an initial object that is "it" or "infected" and as it moves about the screen and touches/overlaps/collides with another object it should change the touched object to the same color as the object that touched it. Newly infected objects can continue to infect other objects they randomly collide with, until - in principle - the whole population is the same color as the first infected object. (I'll worry about fancier AI where infected actively hunt nearby objects or healthy objects can avoid infected objects, later).
But after looking at various similar questions in StackOverflow that generally deal with 2 DIVs colliding, or use some sort of jQuery draggable detection trick, I'm still at a bit of a loss as to how to build upon those ideas to scale up a simple "if I am touching/overlapping/colliding with another object it should get infected too" that can be applied to a large number of elements on the page, say... less than 100 so as not to drag the browser down.
I basically get as far as determining position and widths/heights of the objects so that I know how much space they take, but then the brain goes 'bzzzzt' when trying to develop a function that checks over all the population for collisions.
Got the population moving around randomly without trouble - see JSFiddle https://jsfiddle.net/digitalmouse/5tvyjhjL/1/ for the related code. Affected function should be in the 'animateDiv()', seen below to make the stackoverflow question asking editor happy that I included some code in my question. :)
function animateDiv($target) {
var newq = makeNewPosition($target.parent());
var oldq = $target.offset();
var speed = calcSpeed([oldq.top, oldq.left], newq);
// I believe collision should be dealt with here,
// just before moving an object
$target.animate({
top: newq[0],
left: newq[1]
}, speed, function () {
animateDiv($target);
});
}
Any hints, tricks, adaptations, or code snippets that push me in the right direction are appreciated.
a quick, down and dirty solution (there are more complex algorithms) would be to use:
document.elementFromPoint(x, y);
It gets the element at the position specified. The full spec can be found here.
Assuming your 'zombies' are rectangular, you could call this for each corner, and if you get a hit, that isn't the background or the element you're checking, you've got a collision...
EDIT:
An alternate method, even 'downer and dirtier' than above, but stupidly quick, would be to get the centre points of the two objects to check, then find their absolute displacements in X and Y. If the differences are less than the sum of half their widths and heights then they are overlapping. It's by no means pix perfect, but it should be able to handle a large number objects really quickly.
EDIT 2:
First off, we need to get the centres of each object (to check)
// Values for main object
// pop these in vars as we'll need them again in a sec...
hw = object.style.width >> 1; // half width of object
hh = object.style.height >> 1; // (bit shift is faster than / 2)
cx = object.style.left + hw; // centre point in x
cy = object.style.top + hh; // and in y
// repeat for secondary object
If you don't know / store the width and height you can use:
object.getBoundingClientRect();
which returns a 'rect' object with the fields left, top, right and bottom.
Now we check proximity...
xDif = Math.abs(cx - cx1); // where cx1 is centre of object to check against
if(xDif > hw + hw1) return false; // there is no possibility of a collision!
// if we get here, there's a possible collision, so...
yDif = Math.abs(cy - cy1);
if(yDif > hh + hh1) return false; // no collision - bug out.
else {
// handle collision here...
}
Danny
Related
I'm trying to make an array of images 'spring' onto the canvas from the bottom of the screen and then land in random positions, like this image here:enter image description here (this is a screenshot of my canvas after you remove the physics)
Here is my attempt so far:
https://editor.p5js.org/holographicleah/sketches/DUY0EDnqN
I like the animation of the spring that i've managed, but I want the cats to be scattered across the whole screen like in the image above. I understand that i'm affecting the same 'force' on all of the objects, so it's natural that they all end up at the same height at the top of the screen. How could I randomise it so that they end up everywhere? Should I have used some kind of lerp to absolute positions instead? Open to trying something different if needs be. Still a beginner to code really so classes are still new to me!
Inspiration for this code came from both https://www.youtube.com/watch?v=Rr-5HiXquhw&t=937s for the spring physics and https://www.youtube.com/watch?v=cl-mHFCGzYk&t=149s for the 'particles'. I've adapted what I can but I've hit an experience wall!
You already have the x position covered in your linked code. In order to randomize the y position, change your constructor() method, by adding this line:
this.randomf = random(Math.floor(height/2) - 50);
Then, in the update() method, add this line:
this.pos.y += this.vel + this.randomf;
With the first line, you're giving individual objects a property which tells them what their (randomly chosen) y limit should be.
With the second line, you're limiting the y position. You would need to adjust it a bit, to fit your use case.
And some advice - with a large number of objects springing up, you might want to consider limiting the number of cycles, by dropping the updates, once the velocity falls below a certain value. Something like this:
update(){
if(this.vel <= 0.009) {
let force = - spring * this.pos.y;
this.vel+= force;
this.pos.y += this.vel;
this.vel*=0.9;
}
}
I have a total of 1 to 64 blobs and they all move to my mouse position. But i want them to not go into each other, in other words circle collision detection. However i can't seem to make it smooth and also push new objects after they move for the first time?
Tried checking each blob for collision with other blobs. If collosion is true, then set the distance between the blobs to their accumulated radiuses.
This is how i wrote the colliding function, but this way of doing it makes the resetting of positions too fast. I want it to be a smooth, but fast transition. Like instead of now 1 frame, lets say 10 frames. And another problem is when two objects' are distanced to their radiuses, they might collide into new ones and that will cause this code to run again, and then all blobs go crazy.
this.collide = function() {
var length = this.blobs.length; // How many blobs?
this.blobs.forEach(function(item, index) {
for (var i = 0; i < length; i++) {
// Get absolute distance between two vectors
var v0 = vectorFromTo(blob.blobs[i].pos.x, blob.blobs[i].pos.y, //[x2, y2]
item.pos.x, item.pos.y); //[x1, y1]
// if colliding, set distance between to their accumulated radiuses
if (magnitude(v0) < blob.blobs[i].r + item.r) {
item.pos.add(v0.setMag(magnitude(v0) - (blob.blobs[i].r + item.r)));
}
}
});
}
I haven't tried to code another way of doing this yet because i haven't learned about vectors in school, but i do understand them quite a bit. But what i think would work is if i checked for collision, and if they collide they go opposite directions 50% of the deficit distance, and then they check if they hit new blobs. But this would require physics right? Cause then it would have to do something with the mass and speed of the blob as well to know whats gonna happen to the new blob it crashes into?
EDIT:
This is what im looking for: https://youtu.be/QvlhRGtlcsw
This is what it currently looks like: https://youtu.be/QEpHnCgomqY
Leading up from this question Detecting mouse coordinates with precision, I have learnt quite a bit in the past few days. Here are what I picked as best learning resources on this topic:
http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/
http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/quadtrees-r1303
http://jsfiddle.net/2dchA/2/
The code in (3) works in JSFiddle but breaks at this section in my testing environment (VS2012):
var myTree = new Quadtree({
x: 0,
y: 0,
width: 400,
height: 300
});
with the message Quadtree is undefined in IE. FF & Chrome just gloss over it and display an empty page. I couldn't sort it out. Question 1: Can someone help out with that?
My main question:
I have a region (parcels of land like a map) with about 1500 parcels drawn in html5, not jpg or png images. It is a lot of lines of code to complete that but the rendering is great, so I am keeping it that way. I intend to have a mouseover event tell me which parcel I am standing on when the mouse stops. As you will see in the previous question referred my previous attempts were not impressive. Based on the learning I have been doing, and thanks to Ken J's answer/comments, I would like to go with this new approach of slicing up my canvas into say 15 quads of 100 objects each. However, I would like some guidance before I take another wild dive the wrong way.
Question 2: Should I slice it up at creation or should the slicing happen when the mouse is over a region, ie, trail the mouse? The latter sounds better to me but I think I can do with some advice and, if possible, some start out code. The quadtree concept is completely new to me. Thanks.
Can't help with question 1.
You should definitely build the tree as early as possible, given that the objective is to get the page to respond as quick as possible once the user clicks somewhere.
Keep the tree for as long as the user interacts with the 2d area. Updating a quad tree shouldn't be too hard, so even if the area changes contents, you should be able to reuse the existing tree (just update it).
Given the fact that your draw area is well know i see no advantage in a QuadTree over a spacial hash function. This function will give you an integer out of an (x,y) point.
var blocWidth = 20;
var blocHeight = 20;
var blocsPerLine = ( 0 | ( worldWidth / blocWidth) ) + 1 ;
function hashPoint(x,y) {
return ( 0 | (x/blocWidth)) + blocsPerLine*(0|(y/blocHeight));
}
once you built that, hash all your parcels within an array :
parcelHash = [];
function addHash(i,p) {
if (!parcelHash[i]) { parcelHash[i]=[ p ]; return; }
if (parcelHash[i].indexOf(p) != -1 ) return;
parcelHash[i].push(p);
}
function hashParcel (p) {
var thisHash = hashPoint(p.x,p.y); // upper left
addHash( thisHash, p);
thisHash = hashPoint(p.x+width, p.y); // upper right
addHash(thisHash, p);
thisHash = hashPoint(p.x, p.y+p.height); // lower left
addHash(thisHash, p);
thisHash = hashPoint(p.x+width, p.y+p.height); // lower right
addHash(thisHash, p);
};
for (var i=0; i<allParcels.length; i++) { hashParcel(allParcels[i]) };
now if you have a mouse position, you can retrieve all the parcels in the
same block with :
function getParcels(x,y) {
var thisHash = hashPoint(x,y);
return parcelHash[thisHash];
}
I'll just give you few tips in addition to what others have said.
... have a mouseover event tell me which parcel I am standing on ...
From your other messages I conclude that parcels will have irregular shapes. Quadtrees in general work with rectangles, so you'd have to calculate the bounding rectangle around the shape of the parcel and insert that rectangle in the quadtree. Then are when you want to determine whether mouse is over a parcel, you'll query the quadtree which will give you a set of parcels that might be under the mouse, but you'll have to then do a more precise check on your own to see if it indeed is.
... when the mouse stops.
From your other questions I saw that you try to detect when the mouse has "stopped". Maybe you should look at it this way: mouse cursor is never moving, it's teleporting around the screen from previous point to next. It's always stopped, never moving. This might seem a bit philosophical, but it'll keep your code simpler. You should definitely be able to achieve what you intended without any setTimeout checks.
... slicing up my canvas into say 15 quads of 100 objects each.
... Should I slice it up at creation or should the slicing happen when the mouse is over a region
You won't (and can't) do slicing, quadtree implementation does that automatically (that's its purpose) when you insert or remove items from it (note that moving the item is actually removing then re-inserting it).
I didn't look into the implementation of quadtree that you're using, but here are two MX-CIF quadtree implementations in case that one doesn't work out for you:
https://github.com/pdehn/jsQuad
https://github.com/bjornharrtell/jsts/tree/master/src/jsts/index/quadtree
The problem in question 1 probably happens because jsfiddle (http) page is trying access quadtree.js which is on https
I've searched all over and couldn't find an answer to this seemingly common question, surprisingly. The problem I'm currently facing is checking if the player is facing an enemy, then if so within what range of the players' view (adjustable) and if it's within that range then move away in the nearest safe direction.
Here's a picture :D
So, how would I accomplish this? I have the x, y, and direction, of every ship object. This is my last failed attempt, attempting to consider that the player's direction will be exactly 180 degrees away from the enemy's direction relative to the player.
var direction=Math.direction(this.x,this.y,player.x,player.y,1),
playerview=Math.abs(direction)-Math.abs(player.direction-180)
if(Math.abs(playerview)<10) {
console.log('in view')
this.xVelocity+=AI.speed*Math.sin(playerview*Math.PI/180)
this.xVelocity+=AI.speed*Math.cos(playerview*Math.PI/180)
}
In this example, 10 would be the range. Of course, I've only managed to make ships rotate to the right, so aside from the detection only working on half a circle I can't make the enemy go to the right side either. Any ideas?
In Your code, You are modifying this.xVelocity twice instead of modifying this.yVelocity.
I guess, Math.direction is not in the JavaScript/ECMA standard. Are You sure, You are using it correctly? Consider Math.atan2.
Moreover, You should provide a definition of "facing each other".
If it's "seeing each other", then Your comment "in view" is misleading.
But the main issue is:
Math.abs(angleInDegrees) modifies the angle!
The correct way to build an absolute value of an angle is:
while (angleInDegrees < 0)
{
angleInDegrees += 360;
}
// If necessary, add this too:
while (angleInDegrees >= 360)
{
angleInDegrees -= 360;
}
-10 and 350 are identical, -10 and 10 are 20 degrees apart.
For further explanation, let's call the code above normalizeAngle
(Note: For huge values, the loop may run very long or even forever. If such values may occur, this should be checked.)
The following should do the trick:
playerview = normalizeAngle(direction - player.direction - 180)
if (playerview < range || playerview > 360-range) {
By the way: "playerview" should be the mininum from the player's field of view and the enemy's field of view.
I need help/advice for improving/commenting my current design please :)
This relates to collision detection in a simple game: Dynamic bodies (moving ones) might collide with static bodies (i.e. ground, walls). I'm porting my Obj-C model to Javascript and am facing memory/performance questions as to my way of implementing this.
I'm using a very basic approach: An array of arrays represents my level in terms of physic opacity.
bit set to 0: Transparent area, bodies can go through
bit set to 1: Opaque area, bodies collide
Testing the transparency/opacity of a pixel simply goes as follows:
if (grid[x][y]) {
// collide!
}
My knowledge of JS is pretty limited in termes of performance/memory and can't evaluate how good this approach is :) No idea of the efficiency of using arrays that being said.
Just imagine a 1000-pixel wide level that's 600px high. It's a small level but this already means an array containing 1000 arrays each containing up to 600 entries. Besides, I've not found a way to ensure I create a 1bit-sized element like low-level languages have.
Using the following, can I be sure an entry isn't something "else" than a bit?
grid[x][y] = true;
grid[x][y] = false;
Thanks for your time and comments/advices!
J.
If you have an 1000x600 grid, you can guarantee you have at least 601 arrays in memory (1001 if you do it the other way round).
Rather than doing this, I would consider using either 1 array, or (preferrably) one object with a mapping scheme.
var map = {};
map["1x1"] = 1;
map["1x3"] = 1;
// assume no-hits are empty and free to move through
function canGoIn(x, y) {
return map.hasOwnProperty(x + "x" + y);
};
Alternately;
var map = [];
var width = 600;
map.push(0);
map.push(1);
// etc
function canGoIn(x, y) {
return map[(x * width) + y] == 1;
}
a boolean value won't be stored as just one bit, and that is also true for any other language I know (C included).
If you are having memory issues, you should consider implementing a bitarray like this one: https://github.com/bramstein/bit-array/blob/master/lib/bit-array.js
You will have to make your 2d array into a simple vector and converting your x, y coordinates like this: offset = x + (y * width);
Browsing an array will still lead to a multiplication to evaluate the offset so using a vector is equivalent to arrays.
But I suspect that calling a function (in case your using a bit-array) and doing some evaluations inside will lead to poorer performances.
I don't think you can gain performances and save memory at the same time.