Rotated elements collision / overlapping detection - javascript

need some help with detecting collision / overlap of 2 elements of which one is dynamically rotating.
I'm making a game where a user controls a sword which rotates and moves depending on mouse cursor position, the image used for that is 5 pixels wide and 130 long. Rotation possible over a full 360 degrees. I will have targets spawning across the page which the user needs to slash with his sword, so I need to detect when the image slices through one of the target divs. I can't seem to be able to get a working detection going because of the rotation of the image.
Is there anyone that can point me in the right direction / has a solution for detections such as these?
Example of what I have so far here, no proper detection yet
http://tinyurl.com/ovfhfwx
I started off with something like the following
var square = {x: 500, y: 500, width: 25, height: 25}
var intervalId = setInterval(function(){
var lightsaber = {
x: getOffset( document.getElementById('lightsaber') ).left,
y: getOffset( document.getElementById('lightsaber') ).top,
width: $("#lightsaber").width(),
height: $("#lightsaber").height()
}
if (lightsaber.x < square.x + square.width &&
lightsaber.x + lightsaber.width > square.x &&
lightsaber.y < square.y + square.height &&
lightsaber.height + lightsaber.y > square.y) {
console.log('collision');
//clearInterval(intervalId);
}
}, 33);
This obviously didn't rotate the collision box and just worked as if it was always standing straight, I was hoping there would be some similar solution like this but where it does rotate the collision box.
Thanks!

Related

Three.JS how to use ray casters with multiple cameras?

I'm hoping someone here has done this before. I have a ThreeJS scene, which is essentially a cube. The cube has 6 images one on each of the scenes, basically rendering a 360 panoramic photo.
Within our 3D space, we have 6 cameras, one each pointing at each of the directions, forward, backward, left, right, up, and down. Basically we want to be able to project each of these views all at once.
So, to do this I've made 6 cameras, and added them to the scene. In the scene, we have clickable targets. I want to be able to use ray caster to register a click in any camera view. For example, this is what it looks like:
Within our scene we have clickable hot spots. I want to be able to click on a hotspot on ANY of the 6 cameras. But, I can't get the ray caster to work on even one. This is the code I am using:
if (this._my_cameras && this._my_cameras[0])
{
var mouse = {};
mouse.x = ( this._nextMouseX / this._my_cameras[0].viewport.width ) * 2 - 1;
mouse.y = - ( this._nextMouseY / this._my_cameras[0].viewport.height ) * 2 + 1;
Object.assign(
this._mouse,
coords.normalizeScreenXY(mouse, this._my_cameras[0].viewport)
);
console.log("this._mouse: " + util.inspect(this._mouse));
this._raycaster.setFromCamera(this._mouse, this._my_cameras[0]);
}
else
{
this._raycaster.setFromCamera(this._mouse, this._camera);
}
let intersections = this._raycaster.intersectObjects(
this._scene.children,
true
);
this._nextMouseX / Y are the raw mouse coordinates on screen. My normalize function should normally the mouse coordinates to -1 to 1 as needed. This all works fine if I have one camera taking up the whole view. But with 6 cameras, I never get a ray caster intersection with my targets.
Does anyone have an idea on how to get raycasting or object picking working across multiple cameras?
Edit 1:
This is the code I am now trying to get to work using viewports for the cameras for the raycasting:
for (var i = 0; i < this._igloo_cameras.length; i++)
{
this._mouse.x = ( this._nextMouseX / this._igloo_cameras[i].viewport.w ) * 2 - 1;
this._mouse.y = -1 * (this._nextMouseY / this._igloo_cameras[i].viewport.z) * 2 + 1;
console.log("this._igloo_cameras[i].viewport: " + util.inspect(this._igloo_cameras[i].viewport));
console.log("Igloo Camera #" + i + " this._mouse: " + util.inspect(this._mouse));
this._raycaster.setFromCamera(this._mouse, this._igloo_cameras[i]);
let intersections = this._raycaster.intersectObjects(
this._scene.children,
true
);
all_intersections.push(...intersections);
}
with this I get mouse values outside of 1 to -1 and I still don't get accurate click locations/targets.
This is what I get in my console:
this._igloo_cameras[i].viewport: { x: 0, y: 685, z: 685, w: 685 }
Viewer3D.js:521 Igloo Camera #0 this._mouse: { x: -0.5883211678832116,
y: -1.3649635036496353,
rawX: 141,
rawY: 810 }
Viewer3D.js:520 this._igloo_cameras[i].viewport: { x: 685, y: 685, z: 685, w: 685 }
Viewer3D.js:521 Igloo Camera #1 this._mouse: { x: -0.5883211678832116,
y: -1.3649635036496353,
rawX: 141,
rawY: 810 }

Zoom in on a photo in canvas using javascript

I was searching for a couple of days how to solve this problem and I decided to ask here for the help.
The thing is, I made a canvas that is 640x480px and preloaded it with an image.
After I used the mouse to select the area that is going to be zoomed in (I used a draggable square, same type like if you would press mouse on windows desktop and select multiple icons) I changed the canvas to be 480x480px (since the zoom in part of the photo is a square), and within that new canvas I have displayed a new zoomed in part of that photo.
My question is: since I am doing all of this so I can zoom in on someones face so I can get a user to more precisely place dots on eyes and mouth (face recognition software like thing) how can I get real coordinates of these dots? In respect to an original image and original canvas that was 640x480px.
Everything is in pure javascript no jQuery, and without any js libraries
Thank you
The same way you'd convert between Fahrenheit and Celsius: decide on a reference point and adjust your scale. The reference point is easy: (0, 0) in the zoomed context is the upper left corner of the selected area in the original context. For the scale, convert the zoomed click point from pixels to percentages. A click at (120, 240) is a click at (25%, 50%). Then multiply that percentage by the size of the selected area and add the reference point offset.
// Assume the user selected in the 640x480 canvas a 223x223
// square whose upper left corner is (174, 36),
let zoomArea = {x: 174, y: 36, size: 223};
// and then clicked (120, 260) in the new 480x480 canvas.
let pointClicked = {x: 120, y: 260};
function getOriginalCoords(area, clicked) {
const ZOOMED_SIZE = 480;
// Get the coordinates of the clicked point in the zoomed
// area, on a scale of 0 to 1.
let clickedPercent = {
x: clicked.x / ZOOMED_SIZE,
y: clicked.y / ZOOMED_SIZE
};
return {
x: clickedPercent.x * area.size + area.x,
y: clickedPercent.y * area.size + area.y
};
}
console.log(getOriginalCoords(zoomArea, pointClicked));
At the end I did it this way
// get bounding rect of canvas
var rectangle = canvas.getBoundingClientRect();
// position of the point in respect to new 480x480 canvas
var xPositionZoom = e.clientX - crosshairOffSet - rectangle.left;
var yPositionZoom = e.clientY - crosshairOffSet - rectangle.top;
// position of the point in respect to original 640x480 canvas
var xPosition = rect.startX + (rect.w * (xPositionZoom / canvas.width));
var yPosition = rect.startY + (rect.h * (yPositionZoom / canvas.height));

How to do pixel-perfect collision detection of a player and the walls (JavaScript Game)

I'm making a 2D game in JavaScript. For it, I need to be able to "perfectly" check collision between my players(the game has two players, open the picture please) and the walls! I mean, I have a function that actually works, but when I make them jump against the walls they pass through the walls and keep moving until they reach another area or even leave the canvas!
Also, if they are falling down and I make them collide with a wall, they just stop there wich is also pretty bad!
I really need help with that!! It's a university project and I have to finnish it really soon!
My game looks like this
The collision detection function I have is here:
function blockRectangle (objA, objB) {
var distX = (objA.x + objA.width / 2) - (objB.x + objB.width / 2);
var distY = (objA.y + objA.height / 2) - (objB.y + objB.height / 2);
var sumWidth = (objA.width + objB.width) / 2;
var sumHeight = (objA.height + objB.height) / 2;
if (Math.abs(distX) < sumWidth && Math.abs(distY) < sumHeight) {
var overlapX = sumWidth - Math.abs(distX);
var overlapY = sumHeight - Math.abs(distY);
if (overlapX > overlapY) {
objA.y = distY > 0 ? objA.y + overlapY : objA.y - overlapY;
}
else {
objA.x = distX > 0 ? objA.x + overlapX : objA.x - overlapX;
}
}
}
I did the walls with a maze and I'm using a for cycle to check the collisions with all of the walls I have saved in an array!
As you can see here:
for (var i in walls) {
var wall = walls[i];
if ((player.x < (wall.x + wall.width)) && ((player.x + player.width) > wall.x) && (player.y < (wall.y + wall.height)) && ((player.height + player.y) > wall.y)) {
player.falling = false;
}
blockRectangle(player, wall);
}
Please help me!!! Thank you all!
In your case I doubt a pixel perfect collision is required.
You can maintain a boolean matrix to store the position of solid objects. Solid objects like walls or players. Then in every frame you can check if your player is trying to move to a position where there is a solid object, if it is then stop it. You don't have to create grid of width x height in pixels, but rather choose a largest block (single element in the grid) in which each solid object reasonably occupies most of the block.
For example you can choose block size to be player_width / 2 x player_height /2.
See following image with grid
Another simple way could be to just check the background pixel color. Since your game is simple, background and object colors are different. So you just have to check if the player is trying to move somewhere where pixel color is not of background, thus there is a solid object and player should stop. You don't have to test for a lot of pixels, just 1 pixel in the direction the player is trying to move. (1 for horizontal and 1 for vertical). This however can not be used if you don't have a clear background color. Background color here is kind of the boolean grid for us in the previous suggestion.

Calculate distance to move a box to remove intersection

I have two boxes that are overlapping and I want to figure out how to move one to remove the intersection.
I defined a vector from the center of one box to the other and move one box along that line, but that may be further than it needs to move, or may not be far enough. So I think what I need to do is figure out which corner of the stationary box lines closest to the line between the centers and then figure out how far along that line (or possibly projecting beyond that line) that corner would be. Then I can multiple my vector by that amount, but I'm having trouble wrapping my head around it.
Here's what I have at the moment, I'm adding items with an x,y,width and height property to a list and as I add each item, I'm checking for intersections with items already in the list. If an intersection is found, I try to move the new item and then try again:
function BoxList() {
var self = this;
var boxes = [];
self.add = function(item, iteration) {
// check intersections with existing boxes
iteration = iteration || 0;
if (iteration < 5) {
for (var i=0; i < boxes.length; i++) {
if (doesIntersect(getBounds(item),getBounds(boxes[i]))) {
item.elem.addClass("overlapped");
// Find vector from mid point of one box to the other
var centerA = { x: item.x + item.width / 2, y: item.y + item.height / 2 };
var centerB = { x: boxes[i].x + boxes[i].width / 2, y: boxes[i].y + boxes[i].height / 2 };
var line = { x1 : centerA.x, y1 : centerA.y, x2 : centerB.x, y2 : centerB.y };
var vector = { x : line.x1 - line.x2, y: line.y1 - line.y2 };
item.x = item.x + vector.x;
item.y = item.y + vector.y;
item.elem.offset({ left: item.x , top: item.y }); // TODO: calculate size of move needed
return self.add(item, iteration + 1);
}
}
}
boxes.push(item);
}
function getBounds(item) {
return { x1: item.x, x2: item.x + item.width, y1: item.y, y2: item.y + item.height };
}
function doesIntersect(a,b) {
return a.x1 < b.x2 && a.x2 > b.x1 && a.y1 < b.y2 && a.y2 > b.y1;
}
}
Here's a simple fiddle
Click move to attempt to arrange the two boxes, note that the overlapping box is moved twice and gets moved further than it really needs to.
Any thoughts? Suggestions on better ways to approach this also greatly appreciated.
As I read it now you calculate the centers of both boxes and use those two points to make the vector that pushes one of the boxes. That's the wrong vector. If you place the boxes right on top of each other that vector will be (0,0). If the boxes only just clip each other the vector will be at it's highest possible value.
You can see this in action with the ghosts. First it gets pushed only a little bit, then it gets pushed a lot.
Instead the vector you need should be based on the size of the overlap. If the overlap is 20px by 30px your vector is (+20,+30)
var vector = {
x: Math.min(box1.x + box2.width, box2.x + box2.width) - Math.max(box1.x, box2.x),
y: Math.min(box1.y + box2.height, box2.y + box2.height) - Math.max(box1.y, box2.y)
}
vector.x is the top-right of the bounding box minus the bottom-left of the bounding box. Idem for vector.y.
This moves the box by exactly the right amount: http://jsfiddle.net/x8MT3/2/
I added a 3rd box pair that needs 2 iterations, probably the top box should move the other way. The vector as I've set it up is always (+,+), you can do your center point calculation to determine which sign each direction should have.

Place square in front of other square

Here is my code:
//ct is a canvas context for drawing stuff
//bw is image width, bh is image height
function drawBox() {
ct.translate(x, y)
ct.rotate(rot)
ct.drawImage(box, -bw/2, -bh/2)
ct.fillRect((-bw/2) + (50 * Math.sin(rot)), (-bh/2) - (50 * Math.cos(rot)), 20, 20)
ct.rotate(-rot)
ct.translate(-x, -y)
}
It is supposed to draw the box, and then place the rectangle 50 pixels in front of it. However, it is not working. The rectangle is rotating two times around the image for every time the image rotates once.
I've experimented a bit, and this code works:
function drawBox() {
ct.drawImage(box, x, y)
ct.fillRect((x) + (50 * Math.sin(rot)), (y) - (50 * Math.cos(rot)), 20, 20)
}
I have removed the rotation and changed the coordinates to x and y. If the above code works, why doesn't rotating it and then doing this code work? How can I fix this problem?
I seem to have solved this. I realized the problem was that I was rotating around the wrong point when drawing my other box, so I used this code:
function drawBox() {
ct.translate(x, y)
ct.rotate(rot)
ct.drawImage(box, -halfbw, -halfbh)
ct.rotate(-rot)
ct.translate(-x, -y)
if (sCount > 0) {
sCount --
ct.translate(x, y+halfbh)
ct.rotate(rot+PI)
ct.fillRect((halfbh * Math.sin(rot)), (halfbh * Math.cos(rot))+halfbh, 10, 50)
ct.rotate(-rot-PI)
ct.translate(-x, -y-halfbh)
}
}
(I added a couple of irrelevant things, like sCount, but this is essentially what the problem was.) I had to un-translate and un-rotate so I could re-translate and rotate to where the second box would be at the right place.

Categories

Resources