I am implementing a custom space invader like in HTML/javascript. So far everything works fine but collision detection seems to be a problem. After looking for a couple of solutions online, here is what I have so far.
My enemies are represented in an array like this:
function Logo(I){
I = I || {};
I.sprite = new Image();
I.active = true;
I.width = 25;
I.height = 25;
I.explode = function(){
this.active = false;
}
I.draw = function(){
context.drawImage(I.sprite,this.x,this.y);
}
I.setRes = function(name){
this.sprite.src = name;
}
return I;
}
which is populated like this:
var logoArray = [];
for(i=0;i<logoData.length;i++){
logoArray.push(Logo({
x: logoData[i].x,
y: logoData[i].y
}));
logoArray[i].setRes("./graphics/logo_slices/logo_" + logoData[i].name + ".png");
console.log(logoArray[i].sprite.src);
}
The collision are handled like this (enemy.explode do a this.active = false):
function handleCollision(){
playerBullets.forEach(function(bullet) {
logoArray.forEach(function(enemy) {
if (isCollide(bullet, enemy)) {
enemy.explode();
bullet.active = false;
}
});
});
}
function isCollide(a, b) {
return a.x < b.x + b.width &&
a.x + a.width > b.x &&
a.y < b.y + b.height &&
a.y + a.height > b.y;
}
The problem is that it makes inactive everything that is to the left of the impact point.
I understand that it is quite hard to depict my problem so happy to clarify.
The draw function filters to draw only the active elements of the array:
logoArray.forEach(function(logo_slice){
logo_slice.draw();
});
Thanks for any help you can give!
Regarding your collision logic, i would take the opposite approach:
Take the 4 cases that define a not collide and negate them.
being above, being below, being besides to left / right. not being either of these 4 makes it necessarily a collision.
Just as i guess this is the reason.
The rest of your "engine" looks reasonable and should work.
collide =
!(a.x + a.width < b.x ||
a.x > b.x + b.width ||
a.y + a.height < b.y||
a.y > b.y + b.height )
Additionally you could define a radius per entity that participates in the collisions and use a intersection via the radius. Of course then you need the center of the entities for this to work.
Edit:
For more details and elaborate example of different Collision Detection approaches in JS, see
https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection.
Related
I want to implement a rocket flying through space, which bounces off from incoming meteroids. Currently I have implemented it by comparing the x and y position of both actors and swapping their speeds on collision. The detection of a collision and the speed swapping does work (proved by console.log), however on the screen they only sometimes bounce off.
I tried to make sure that the speed objects of the compared actors do not reference the same JavaScript object (with cSpeedX etc).
The game is built with Pixi JS.
The collision detection function, executed for each actor (all meteroids and the rocket)
export const checkCollision = (current, objects) => {
objects.forEach((o) => {
if (current !== o) {
const dx =
current.x < o.x
? o.x - o.width / 2 - (current.x + current.width / 2)
: current.x - current.width / 2 - (o.x + o.width / 2);
const dy =
current.y < o.y
? o.y - o.height / 2 - (current.y + current.height / 2)
: current.y - current.height / 2 - (o.y + o.height / 2);
if (dx < 0 && dy < 0) {
const cSpeedX = current.speed.x;
const cSpeedY = current.speed.y;
const oSpeedX = o.speed.x;
const oSpeedY = o.speed.y;
current.speed.x = oSpeedX;
current.speed.y = oSpeedY;
o.speed.x = cSpeedX;
o.speed.y = cSpeedY;
}
}
});
The move function implemented both on the rocket and meteroids
this.move = (delta) => {
this.x += this.speed.x * delta;
this.y += this.speed.y * delta;
};
export const checkCollision = (current, objects) => {
objects.forEach((o) => {
if (current !== o) {
You wrote also: The collision detection function, executed for each actor (all meteroids and the rocket)
so i suppose somewhere there is also loop like:
objects.forEach((o) => {
checkCollision(o, objects);
});
This would mean that for every pair of objects the collision is checked twice.
Lets assume that o1 and o2 are some different objects, and that they collide. What will happen then? :
checkCollision(o1, objects); <-- swap speed between o1 and o2
...
checkCollision(o2, objects); <-- swap speed between o2 and o1
So speed will be swapped 2 times between them - in other words: speed of both objects will remain the same.
To investigate if this is indeed the case you can put console.log (or something to print id of object) like this:
if (dx < 0 && dy < 0) {
console.log('swapping speed of of objects:');
console.log(current);
console.log(o);
const cSpeedX = current.speed.x;
Then prepare situation when 2 objects collide and check console logs.
Hi,
I'm developing a visual like a scatter plot using D3.js it has around 20k points without labels. I want to show the labels for the filtered data. I modified a function to avoid labels overlapping. It works but if I have a large number of points after applying the filter it cause crush the browser !!
Any ideas to improve the algorithm ? or to use the force function in D3v3 to do the job ?
function arrangeLabels(svg) {
var move = 1;
while(move > 0) {
move = 0;
svg.selectAll(".dotB")
.each(function() {
var that = this,
a = this.getBoundingClientRect();
svg.selectAll("text.dotB")
.each(function() {
if (this != that) {
var b = this.getBoundingClientRect();
if ((Math.abs(a.left - b.left) * 2 < (a.width + b.width)) &&
(Math.abs(a.top - b.top) * 2 < (a.height + b.height))) {
var dy = (a.bottom + b.height)+2,
move += Math.abs(dy);
d3.select(this).attr("y", dy);
a = this.getBoundingClientRect();
}
}
});
})
}
I found this method to solve the overlapping issue using D3v4 https://walkingtree.tech/d3-quadrant-chart-collision-in-angular2-application/ any idea how to do the same in D3v3 ?!
So the problem is with the if logic:
if (
(Math.abs(a.left - b.left) * 2 < (a.width + b.width)) &&
(Math.abs(a.top - b.top) < a.height + b.height)
) {
var dy = (a.bottom + b.height)+2,
move += Math.abs(dy);
d3.select(this).attr("y", dy);
a = this.getBoundingClientRect();
}
I nailed a collision detection system, but now i'm trying to make a system that makes it so that, when the function is called, the items in the parameters aren't able to touch each other. I'm fairly new to JavaScript and its the first language ive really tried to learn. The way my rectangles are being drawn is so that x and y are in the middle of the rect, rather than in the top left corner of it. The system i've built technically works, but only if its a perfect square, for some reason rectangles are buggy and that i cant figure out. Even when it is a perfect square though, it seems clunky and really bad compared to what i'm used to, which is code.org's item1.collide(item2); which works perfectly and exactly how I want, but i cant find the code behind that. I am using p5.js, by the way.
Here is how i'm drawing my rectangles:
rect(this.x-this.width/2,this.y-this.height/2,this.width,this.height);
And here is the blockCollision function I currently have:
function blockCollision(a,b){
if(a.x+a.width/2 > b.x-b.width/2 &&
a.x-a.width/2 < b.x+b.width/2 &&
a.y-a.height/2 < b.y+b.height/2 &&
a.y+a.height/2 > b.y-b.height/2) {
if(a.x<b.x-b.width/2) a.x=b.x-b.width/2-a.width/2;
if(a.x>b.x+b.width/2) a.x=b.x+b.width/2+a.width/2;
if(a.y<b.x-b.height/2) a.y=b.x-b.height/2-a.height/2;
if(a.y>b.x+b.height/2) a.y=b.x+b.height/2+a.height/2;
}
}
Also, here is the entire code download if It helps: https://drive.google.com/open?id=0B-F5CHOIQvvGVlR3Njd1M1NLS1E
I presume by "block collision", you mean that you would like to move one of the blocks in the "opposite" direction of the collision so that the collision does not happen.
What you need to do is essentially determine the direction of the collision (top/bottom/left/right), and move the offending block away:
function blockCollision(a, b) {
// Assuming (0, 0) is the top left corner
const aTop = a.y - a.height / 2;
const aBottom = a.y + a.height / 2;
const aLeft = a.x - a.width / 2;
const aRight = a.x + a.width / 2;
const bTop = b.y - b.height / 2;
const bBottom = b.y + b.height / 2;
const bLeft = b.x - b.width / 2;
const bRight = b.x + b.width / 2;
const collisions = [];
if (aTop <= bTop && aBottom >= bTop) {
// a is above B, potential collision
if (aLeft <= bRight && bLeft <= aRight) {
// Prevent collision, push a to the top
a.y = bTop - (a.height / 2) - 1;
}
}
if (aBottom >= bBottom && aTop <= bBottom) {
// a is below B, potential collision
if (aLeft <= bRight && bLeft <= aRight) {
// Prevent collision
a.y = bBottom + (a.height / 2) + 1;
}
}
if (aLeft <= bLeft && aRight >= bLeft) {
// a is left of B, potential collision
if (aTop <= bBottom && bTop <= aBottom) {
// Prevent collision, push a to the left
a.x = bLeft - (a.width / 2) - 1;
}
}
if (aRight >= bRight && aLeft <= bRight) {
// a is right of B, potential collision
if (aTop <= bBottom && bTop <= aBottom) {
// Prevent collision, push a to the right
a.x = bRight + (a.width / 2) + 1;
}
}
}
See codepen example.
I need to detect wether two objects collide / overlap with each other,
for achieving this purpose I stumbled upon the collision algorithm used in the "run pixie run" game, that didn't work, so I passed to this other function I found on the pixijs forum ( code follows below ), but even this works only in some cases.
The objects involved in the hit test are two DisplayObjectContainer containing a Sprite and a Graphics element (namely a rectangle that used for showing the boundingBox of the sprite).
The sprite has the anchor point set to 0.5 ( for that reason the x/y values in the function are inited like this )
var hitTest = function(s2, s1)
{
var x1 = s1.position.x - (s1.width/2),
y1 = s1.position.y - (s1.height/2),
w1 = s1.width,
h1 = s1.height,
x2 = s2.position.x - ( s2.width / 2 ),
y2 = s2.position.y - ( s2.height / 2 ),
w2 = s2.width,
h2 = s2.height;
if (x1 + w1 > x2)
if (x1 < x2 + w2)
if (y1 + h1 > y2)
if (y1 < y2 + h2)
return true;
return false;
};
I also read that it might be possible to use the box2d engine to perform such a task, but I find this solution a little bit overwhelming.
I was looking for a simple as convenient way to do so.
In the end I came up with this solution, that I found on mdn and changed in order to fit my scenario.
var isColliding = function(el) {
el.children[0].position, el.children[1].position, el.position);
rect1 = {
x:el.position.x-(el.children[0].width/2),
y:el.position.y-(el.children[0].height/2),
w:el.children[0].width,
h:el.children[0].height
}
for(i=0; i<stage.children.length;i++)
{
if(stage.children[i] != el) {
el2 = stage.children[i]
rect2 = {
x:el2.position.x-(el2.children[0].width/2),
y:el2.position.y-(el2.children[0].height/2),
w:el2.children[0].width,
h:el2.children[0].height
}
if (rect1.x < rect2.x + rect2.w &&
rect1.x + rect1.w > rect2.x &&
rect1.y < rect2.y + rect2.h &&
rect1.h + rect1.y > rect2.y) {
return true;
}
}
}
return false;
I'm working on the Collision system for my game; which is a top down shooter, the character is always static - and everything else (Map/Level), moves around him.
The character also rotates so it's always facing the mouse position.
With that in mind, I can't seem to get my head around my collision system, which needs to take into account of the character rotation, right?
I basically just want to check if the given object is at all 'touching' my character/sprite. I'm not so sure if the maths I'm using is correct.
This is my collision detection (called every update):
function detectCollisions(){
//Detect for game props
if(collides(Map, TestProp)){
console.debug("Touching...");
}
}
function collides(a, b){
//ctxMap.setTransform(1, 0, 0, 1, -Map.x + gameWidth/2, -Map.y + gameHeight/2);
//var transX = -Map.x + gameWidth/2;
//var transY = -Map.y + gameHeight/2;
//need to take player rotation into account too!
//console.debug(a.x + " " + b.x + " " + b.width + " " + Player.width); //A Width
/*return a.x < b.x + b.width && a.x + Player.width > b.x &&
a.y < b.y + b.height && a.y + Player.height > b.y;*/
var xOffset = Math.cos(Player.characterRotation); //+ Player.width;
var yOffset = Math.sin(Player.characterRotation); //+ Player.height;
return Map.x + xOffset > b.x && Map.x + xOffset < b.x + b.width &&
Map.y + yOffset > b.y && Map.y + yOffset < b.y + b.height;
}
Also, not sure if this is relevant, but this is the transform used to move my Map Canvas:
ctxMap.setTransform(1, 0, 0, 1, -Map.x + gameWidth/2, -Map.y + gameHeight/2);
Would much appreciate it if someone helped me out here :) Thanks!
Personally, I wouldn't worry so much about the character colliding. The reason I say that is simple.
Let's saw you're walking real close to a wall. Then you turn to follow the mouse, and the sprite then overlaps the wall. What do you do now? Either you stop the turning, which would screw up movements, or you let the sprite overlap and the player gets completely stuck until they turn free again.
My preference would be to use a collision circle. If the player is closer than R pixels from the wall, count it as a collision and stop the player from moving. This way, even if the player turns, the sprite will never cause the player to get stuck and he will always be able to move away from the wall.
I entered ludum dare this time around and did a tutorial to explain my base code. The tutorials can be found here: http://www.philjeffes.co.uk/wordpress/?p=63
This demonstrates an example of circle based collision detection - please feel free to use any of the code. The following code is an adaptation of that code for general usage:
function CollisionCheck(obj1,obj2){
// If the two objects are less the sum of their collision radii apart then they have collided
// Note that one obj is obj (with a loc and a size) and the other is this.
// Returns true if the objects are touching
var dist = obj2.size + obj1.size; // The distance they must be apart to be not touching
if(obj1.x-obj2.x>dist || obj1.x-obj2.x<-dist)
return false; // Too far apart in x plane
if(obj1.y-obj2.y>dist || obj1.y-obj2.y<-dist)
return false; // Too far apart in y plane
var xDist = obj1.x-obj2.x;
var yDist = obj1.y-obj2.y;
var hyp = Math.sqrt((xDist*xDist)+(yDist*yDist));
if(hyp<dist)
return true;
return false;
}
EDIT
Removed the Math.abs calls as pointed out by vals in the comments.