HTML5 + JS: Collision Detection For Rotatable Character - javascript

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.

Related

Slow the movement of circle collision resolution

I have written a collision detection and resolution system for the game I am currently working on. The collision resolution doesn't work quite as intended though. Occasionally I spawn one circle inside of the other, when this happens I'd like the inside circle to slowly move out of the other's diameter, but currently the code that I have working does it quite quickly and jarringly. It instantly teleports outside of the circle, rather than a slow transition.
I know that the code I am currently using tells the circle where it SHOULD be, so I just need to slowly move the circle to that position. But that is proving to be difficult.
I've tried a couple of solutions, one is included in the code below. I have also tried using linear interpolation to move the circle to its current position, to where the collision algorithm is telling it it should be. That didn't work correctly either.
//r is the radius of the circle
var dx = cell.x - cell2.x;
var dy = cell.y - cell2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < cell.r + cells2.r && cell.r > cells2.r){
var unitX = dx/distance;
var unitY = dy/distance;
cell.x = cells2.x + (cell.r + cells2.r + 1) * unitX;
cell.y = cells2.y + (cell.r + cells2.r + 1) * unitY;
//Ive tried below, but the results were not correct.
//cell.x += (cells2.x + (cell.r + cells2.r + 1) * unitX)*0.5;
//cell.y += (cells2.y + (cell.r + cells2.r + 1) * unitY)*0.5;
}
To move cell apart from cell2, you don't need to add cells2 coordinates
cell.x += (cell.r + cells2.r + 1) * unitX;
To move slowly, make speed smaller
//calculate once:
v = 0.01 * (cell.r + cells2.r - distance);
// at every step:
cell.x += v * unitX;

Collision detection in javascript

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.

Snap.svg Getting rotation from slope in point difference

I am working through an animation where I need to make this plane follow the path given and appear to be "circling" the earth. Here is the codePen, which as you can see is fairly simple.
My problem is with angles, I am trying to see how much should I rotate the plane as it moves through the path by calculating the slope of two points, and turning it into degrees.
Even though I have added an epsilon to safe-check for consistent differences across the points and every other safe-check, I am getting that as it approaches +-90 degrees, it changes signs, instead of passing to the other quadrant 120 degrees, etc.
I can understand that this is caused by the fact that
You can see this happening in the console right in the mid point (displays: slope, arctangent, degrees).
To solve this, I am recurring to Math.atan2(), by using newPoint.x - firstPoint.x, newPoint.y - firstPoint.y as its arguments. It starts off with the right values (CodePen here). But it still does a funky rotation.
Here is the code (I'm not posting the SVG image because it's very large):
JS
var snap = Snap('#Globe_1_');
// Trail 1
var trail1 = snap.path('M354.3,707.9c13.9,14.5,27.9,27.2,41.7,38c13.9,10.9,27.2,19.3,39.3,25.4 c12.6,6.1,24.8,9,35.7,10.3c10.9,1.2,21.1-1.2,30.2-5.4c17-7.8,29-24.8,35.7-48.3c7.2-23.5,9-55,5.4-91.8 c-3.7-36.8-12-77.9-24.8-120.9c-12.6-43-30.2-87.6-51.9-131.7c-21.1-44.1-45.2-85.8-70.7-122.7s-50.8-69.5-77.3-95.5 c-27.2-26-52.5-43.6-75.6-53.2c-22.9-9.7-43.6-10.3-60.4-2.5c-16.3,7.8-27.9,24.2-35.1,47.7c-7.2,23.5-9.7,53.8-6.6,88.8')
.attr({
id: 'trail1',
fill:'none',
stroke: '#C25353',
strokeMiterLimit: 10
});
var len = trail1.getTotalLength();
var plane1 = snap.path('M375.7,708.4c0.1,0.8-0.7,1.8-1.6,1.9l-10.4,0.2l-8.4,15.1l-4,0l4.1-14.6l-7.8-0.2l-2.7,3.2L342,714 l1.6-4.9l-1.7-5.4l3.1,0.1l2.5,3.6l7.8,0.2l-4.3-14.6l4,0l8.3,14.7l10.4-0.2C375.5,706.7,376,707.1,375.7,708.4z') .attr({fill: '#CDCCCC' });
var initPoint = trail1.getPointAtLength( 1 ),
lastPoint,
slope = 0,
lastLen = 0;
Snap.animate(0, len, function( value ) {
movePoint = trail1.getPointAtLength( value );
if (lastPoint && ( Math.abs(lastPoint.y - movePoint.y) > 1 || Math.abs(lastPoint.x - movePoint.x) > 1 )) {
var slope_val = (lastPoint.y - movePoint.y) / (lastPoint.x - movePoint.x),
slope_atan = Math.atan2(movePoint.x - initPoint.x, movePoint.y - initPoint.y),
slope_deg = Snap.deg(slope_atan);
slope = slope_deg;
console.log('Capturing rotation', slope_val, slope_atan, slope_deg);
lastLen = value;
}
plane1.transform( 't' + parseInt(movePoint.x - 350) + ',' + parseInt( movePoint.y - 700) + 'r' + slope);
lastPoint = movePoint;
}, 5000, mina.linear);
Can you please help me out, thank you
I'm not sure of the full effect you are after, if its purely 2d angle, Snap already has this built in (returning angle from point along line), so no need to work too hard...
element.getPointAtLength() returns an angle alpha, so movePoint.alpha can be used...
relevant line below, and other calculation lines removed.
plane1.transform( 't' + parseInt(movePoint.x - 350) + ',' + parseInt( movePoint.y - 700) + 'r' + (180 + movePoint.alpha));
codepen

HTML5 Game - Walking Boundary Issue

I'm working on a top down shooter, and basically the character starts in the middle of the screen, inside a rect (Safe Zone). The character isn't static, the scene is. He can walk around, inside the safe zone. As soon as the character walks out of this zone, the statics switch over ... the character is static, and the scene is moving around him.
The only problem with this is that I can't walk back into the safe zone, allowing my statics to switch over again.
So I'm forever stuck outside the zone. All I'm doing is checking to see whether my character position is 'within' a certain value (which is the rect), if he's out - then my KeyControls then affect the Map, not the character.
So this is my boundary (Safe Zone) checker:
//Walking Window Boundaries
var boundarySizeX = 400;
var boundarySizeY = 200;
ctxWalkBoundary.fillStyle = "grey";
ctxWalkBoundary.fillRect(gameWidth/2 - boundarySizeX/2, gameHeight/2 - boundarySizeY/2, boundarySizeX, boundarySizeY);
ctxWalkBoundary.clearRect((gameWidth/2 - boundarySizeX/2) + 2, (gameHeight/2 - boundarySizeY/2) + 2, (boundarySizeX) - 4, (boundarySizeY) -4 );
var paddingLeft = (gameWidth - boundarySizeX) / 2;
var paddingRight = gameWidth - ((gameWidth - boundarySizeX) / 2) - this.charWidth;
var paddingTop = (gameHeight - boundarySizeY) / 2;
var paddingBottom = gameHeight - ((gameHeight - boundarySizeY) / 2) - this.charHeight;
var paddingY = (gameHeight - boundarySizeY) / 2;
if(this.drawX > paddingLeft && this.drawX < paddingRight && this.drawY > paddingTop && this.drawY < paddingBottom){
inBoundary = true;
}
else{
inBoundary = false;
console.debug("Out Of Boundary!");
}
And this is my KeyChecker:
//UP
if(this.isUpKey){
//Character movement
if(inBoundary){
this.drawX = this.drawX + this.speed * Math.cos((this.characterRotation));
this.drawY = this.drawY + this.speed * Math.sin((this.characterRotation));
}
else{
mapPositionX = mapPositionX - this.speed * Math.cos((this.characterRotation));
mapPositionY = mapPositionY - this.speed * Math.sin((this.characterRotation));
}
My character always faces my mouse (rotates). So every time the user pressed W, or Up - the character will always walk towards the mouse position.
Any ideas how I can get back into the zone?
----- Update -----
I guess I need to somehow check if I'm still facing outside the safe zone - if not, then reverse he statics.
Just separate two things: map and view.
Map is your level, you keep there objects with coordinates.
View is part of map you see on screen.
View has 4 properties: x, y, widht and height, where widht and height most likely is your canvas size.
If your game start with view on map point (0,0) in the middle of screen, then your view (x,y) coordinates should be (-view.width/2, -view.height/2).
How to draw your character and objects in a view?
In first place, draw only thing that are in the view rectangle.
So loop over all objects and check if
object.x >= view.x && object.x <= view.x + view.width && object.y >= view.y && object.y <= view.y + view.height
(you probably should take into account objects boundaries too).
If object is in view area then draw it at position (object.x - view.x, object.y - view.y).
And that's all about drawing things.
Moving character and view area with him.
Now when your character collides with boundary, in example (colliding with right border)
character.x >= view.x + view.width
then move view to the right by incrementing view.x with some value (that might be character.width/2).
-- UPDATE --
I see that you are not using OOP in your game (actually you are because everything in JS is an object, but you are not using it on purpose).
OOP in JS is a lot of explaining, so I'll try to make it short.
You can make objects like your Character, Map and View using JSON format.
character = {
x: 0,
y: 0,
xspeed: 0,
yspeed: 0,
speed: 0,
radius: 20,
}
map = {
objects: [
{sprite: 'tree.jpg', x: 100, y: 50},
{sprite: 'stone.jpg', x: 20, y: 30},
],
}
view = {
width: canvas.width,
height: canvas.height,
x: -this.width/2,
y: -this.height/2,
}
These are objects that you can use like in your functions like that:
for (var i=0; i++, i<map.objects.length) {
if (map_obj.x >= view.x && map_obj.x <= view.x + view.width && map_obj.y >= view.y && map_obj.y <= view.y + view.height) {
var map_obj = map.objects[i];
draw_sprite(map_obj.sprite, map_obj.x - view.x, map_obj.y - view.y);
}
}
It's not the best way, but it's still much better than yours right now. When you understand what OOP is about you will make it better for your own.
The problem here is that you're waiting for the character to go out of bounds, then moving the map instead. But the flag has already been tripped, and now the character movement is static no matter what direction you go in, because you're already out of bounds.
You could instead detect when a character is going to cross the boundary and prevent it by moving the map instead:
//UP
if(this.isUpKey){
// save the x and y offset to prevent needless recalculation
var xOffset = this.speed * Math.cos(this.characterRotation),
yOffset = this.speed * Math.sin(this.characterRotation);
//Character movement
if( boundaryCheck(xOffset, yOffset) ){
this.drawX = this.drawX + xOffset;
this.drawY = this.drawY + yOffset;
}
else{
mapPositionX = mapPositionX - xOffset
mapPositionY = mapPositionY - yOffset;
}
then boundaryCheck takes the x and y delta's and figures out if they're still in bounds. If the character will still be in bounds, return true and the character will move, otherwise the map will move.
function boundaryCheck(xOffset, yOffset){
// variables set and other stuff done...
if(this.drawX + xOffset > paddingLeft && this.drawX + xOffset < paddingRight && this.drawY + yOffset > paddingTop && this.drawY + yOffset < paddingBottom){
return true;
}
else{
console.debug("Out Of Boundary!");
return false;
}
};
This way you don't have to figure out whether an out of bounds character is moving toward the boundary or not. Instead, you pre-determine where the character is going, and adjust accordingly, always keeping him in boundaries.
Without full code this isn't testable, of course, but I think it should work with what you've given.

How to determine which enemy is hit and destroy it alone

http://jsfiddle.net/5DB6K/
I have this game being made where you shoot enemies from the sides of the screen. I've got the bullets moving and being removed when they reach the end of the screen (if they didn't hit any enemy) and removing the enemy when they collide with it.
//------------collision----------------//
if(shot === true){
bulletY = $('.bullet').position().top + 2;
bulletX = $('.bullet').position().left + 2;
$('.enemy').each(function(){
if($('.enemy').hasClass('smallEnemy')){
enemyY = $(this).position().top + 7;
enemyX = $(this).position().left + 7;
if(Math.abs(bulletY - enemyY) <= 9 && Math.abs(bulletX - enemyX) <=9){
$(this).remove();
score = score + 40;
bulletDestroy();
}
}
});
}
However, the bullet destroys every enemy if the collision check is right which isn't what I want. I want to check if the enemy has the class of either smallEnemy, medEnemy, or lrgEnemy and then do the collision check which is what I thought I had but it doesn't seem to work.
Also, the game starts to lag the more and more time goes on. Would anyone know the reason for that?
This will remove all enemies any time a small enemy is present, due to your .each conditions. Try this:
if($(this).hasClass('smallEnemy')){
enemyY = $(this).position().top + 7;
enemyX = $(this).position().left + 7;
if(Math.abs(bulletY - enemyY) <= 9 && Math.abs(bulletX - enemyX) <=9){
$(this).remove();
score = score + 40;
bulletDestroy();
}
As for lag, it's due to memory availability and overhead. Instead of looping through your "enemeies", I would attach an observer to your bullets, and whenever they intersect with .enemy you should fire a custom event to remove this enemey. Much less code too.

Categories