Detect collision between two elements - javascript

I want to detect a collision between two divs and I've tried to do that using the offsetLeft and offsetTop of each one of the divs and by using getBoundingClientRect(). But none of these work.
Is there a way I can get the exact coordinates in which these two elements touch?
const bigDiv = document.querySelector(".big")
const blue = document.querySelector(".blue")
const startBtn = document.querySelector("button")
const red = document.querySelector(".red")
const bigDivDimensions = bigDiv.getBoundingClientRect();
let speed = 5;
//let counter = 0;
let bigDivLeft = bigDivDimensions.left;
startGame();
function random() {
return Math.floor(Math.random() * (bigDivDimensions.width - 40));
}
function startGame() {
//let randomSnake = Math.floor(Math.random()*(bigDivDimensions.width-40));
//let randomApple = Math.ceil(Math.random()*(bigDivDimensions.width -40));
blue.style.left = random() + "px"
blue.style.top = random() + "px"
red.style.left = random() + "px"
red.style.top = random() + "px"
}
function move(e) {
let blueX = blue.offsetLeft;
let blueY = blue.offsetTop;
let key = e.keyCode;
if (key !== 37 && key !== 38 && key !== 39 && key !== 40) {
return
} else if (key === 37 && blueX > 3) {
blueX -= speed;
} else if (key === 38 && blueY > 3) {
blueY -= speed;
} else if (key === 39 && blueX < (bigDivDimensions.width - 25)) {
blueX += speed;
} else if (key === 40 && blueY < (bigDivDimensions.height - 23)) {
blueY += speed;
}
blue.style.left = blueX + "px";
blue.style.top = blueY + "px";
colision(blueX, blueY)
}
function colision(blueX, blueY) {
/* let redX = red.offsetLeft;
let redY = red.offsetTop;
if(redY === blueY || redX == blueX){console.log("hit")} */
let redRect = red.getBoundingClientRect();
let blueRect = blue.getBoundingClientRect();
if ((redRect.top < blueRect.bottom) || (redRect.bottom < blueRect.top) || (redRect.right > blueRect.left) || (redRect.left < blueRect.right)) {
console.log("hit")
}
}
startBtn.addEventListener("click", startGame)
document.addEventListener("keydown", move)
.big {
width: 400px;
height: 400px;
outline: 1px solid red;
margin: 0 auto;
position: relative;
}
.blue {
width: 20px;
height: 20px;
background: blue;
position: absolute;
}
.red {
width: 20px;
height: 20px;
background: red;
position: absolute;
}
<button type="button">Start Game</button>
<div class="big">
<div class="blue"></div>
<div class="red"></div>
</div>
codepen

Two rectangles do collide when their
horizontal side and vertical side
overlap.
Consider the following answer: Checking for range overlap
// l1 (line 1) --> [x1, x2]
// l2 (line 2) --> [x1, x2]
function checkOverlap(l1, l2) {
return ((l1[1] - l2[0]) > 0 && (l2[1] - l1[0]) > 0) ? true : false;
}
Check collision with range overlap for the x-side and y-side:
// Rectangle a --> [[x1, x2], [y1, y2]]
// Rectangle b --> [[x1, x2], [y1, y2]]
function collide(a, b) {
return (checkOverlap(a[0], b[0]) && checkOverlap(a[1], b[1]));
}
Spoiler
SVG-Example
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
var width = 200;
var height = 200;
svg.setAttribute("width", width);
svg.setAttribute("height", height);
document.body.appendChild(svg);
function createRect(svg, width, height, x, y, style, id) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("width", width);
rect.setAttribute("height", height);
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("style", style);
rect.setAttribute("id", id);
svg.appendChild(rect);
}
function createSquare(svg, side, x, y, style, id) {
createRect(svg, side, side, x, y, style, id);
}
function getRandomNumber(a, b) {
return Math.round(Math.random() * (b - a)) + a;
}
function createRandomSquare(svg, xRange, yRange, side, style, id) {
var x = getRandomNumber(xRange[0], xRange[1]);
var y = getRandomNumber(yRange[0], yRange[1]);
createSquare(svg, side, x, y, style, id);
return [
[x, x + side],
[y, y + side]
];
}
function checkOverlap(l1, l2) {
return ((l1[1] - l2[0]) > 0 && (l2[1] - l1[0]) > 0) ? true : false;
}
// [[x1, x2], [y1, y2]]
function collide(a, b) {
return (checkOverlap(a[0], b[0]) && checkOverlap(a[1], b[1]));
}
function getCoordinates(arr) {
return {
"top-left": [arr[0][0], arr[1][0]],
"top-right": [arr[0][1], arr[1][0]],
"bottom-left": [arr[0][1], arr[1][1]],
"bottom-right": [arr[0][0], arr[1][1]]
}
}
function run() {
var bs = document.getElementById("blueSquare");
var rs = document.getElementById("redSquare");
var output = document.getElementById("output");
output.innerHTML = "";
if (bs !== null && rs !== null) {
var parent = bs.parentNode;
parent.removeChild(bs);
parent.removeChild(rs);
}
var side = 30;
var blueSquare = createRandomSquare(svg, [0, (width - side)], [0, (height - side)], side, "fill:blue", "blueSquare");
var redSquare = createRandomSquare(svg, [0, (width - side)], [0, (height - side)], side, "fill:red", "redSquare");
var output1 = "Collision? " + collide(blueSquare, redSquare);
var output2 = "Blue square: " +
JSON.stringify(getCoordinates(blueSquare));
var output3 = "Red square: " + JSON.stringify(getCoordinates(redSquare));
output.innerHTML = output1 + "<br>" + output2 + "<br>" + output3 + "<br>";
}
var button = document.createElement("button");
button.setAttribute("id", "button");
button.textContent = "Run";
button.addEventListener("click", run);
document.body.appendChild(button);
svg {
border: 1px solid;
}
<div id="output"></div>

Related

Prevent block from touching the containers borders

I have this little block that I move around using javascript code. It works all good except if I keep moving it, it can easily get out of the box where it is supposed to be.
Can I prevent this somehow? So no matter how far I want to move it, it will stay stuck inside of the container/box ?
Here's my snippet code:
/// store key codes and currently pressed ones
var keys = {};
keys.UP = 38;
keys.LEFT = 37;
keys.RIGHT = 39;
keys.DOWN = 40;
/// store reference to character's position and element
var character = {
x: 100,
y: 100,
speedMultiplier: 2,
element: document.getElementById("character")
};
var is_colliding = function(div1, div2) {
var d1_height = div1.offsetHeight;
var d1_width = div1.offsetWidth;
var d1_distance_from_top = div1.offsetTop + d1_height;
var d1_distance_from_left = div1.offsetLeft + d1_width;
var d2_height = div2.offsetHeight;
var d2_width = div2.offsetWidth;
var d2_distance_from_top = div2.offsetTop + d2_height;
var d2_distance_from_left = div2.offsetLeft + d2_width;
var not_colliding =
d1_distance_from_top <= div2.offsetTop ||
div1.offsetTop >= d2_distance_from_top ||
d1_distance_from_left <= div2.offsetTop ||
div1.offsetLeft >= d2_distance_from_left;
return !not_colliding;
};
/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup =
document.body.onkeydown = function(e){
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
var kc = e.keyCode || e.which;
keys[kc] = e.type == 'keydown';
};
/// character movement update
var moveCharacter = function(dx, dy){
character.x += (dx||0) * character.speedMultiplier;
character.y += (dy||0) * character.speedMultiplier;
character.element.style.left = character.x + 'px';
character.element.style.top = character.y + 'px';
};
/// character control
var detectCharacterMovement = function(){
if ( keys[keys.LEFT] ) {
moveCharacter(-5, 0);
}
if ( keys[keys.RIGHT] ) {
moveCharacter(5, 0);
}
if ( keys[keys.UP] ) {
moveCharacter(0, -5);
}
if ( keys[keys.DOWN] ) {
moveCharacter(0, 5);
}
};
/// update current position on screen
moveCharacter();
/// game loop
setInterval(function(){
detectCharacterMovement();
}, 1000/24);
body{
display: flex;
justify-content: center;
align-items: center;
}
#character {
position: absolute;
width: 42px;
height: 42px;
background: red;
z-index:99;
}
#container{
width: 400px;
height: 400px;
background: transparent;
border:5px solid rgb(0, 0, 0);
position: relative;
overflow: hidden;
}
<div id="container">
<div id="character"></div>
</div>
PS: You can move the box using keyboard arrows.
Get the container width and height into variable and set a condition on your move
var moveCharacter = function(dx, dy){
let div_width = document.getElementById('container').clientWidth;
let div_height = document.getElementById('container').clientHeight;
if((div_width - character.x) < 50 ){ // 50 = width of character and padding
character.x = div_width - 50;
}
if(character.x < 10){ // Padding
character.x = 11;
}
if((div_height - character.y) < 50 ){
character.y = div_height - 50;
}
if(character.y < 10){
character.y = 11;
}

Why do my Elements have this weird offset?

Hello Stackoverflow Users, so I am making a game with HTML, I have to eat berries, but when I collect them, the more I collect (the max is 5), the more offset the extra body parts add to all of the body parts (minus the main one).
I have tried changing offset with the variables and loop I used, but nothing worked, no matter the math operator.
All of jQuery code:
// Null Variables \\
/* These variables have no value. */
var bound;
var newFood;
var horizontalMatch;
var vertialMatch;
// Main Variables \\
/* These variables keep values in store. */
var background = $(".game-background");
var player = $(".game-player");
var verticalNum = 0;
var horizontalNum = 0;
var score = 0;
var speed = 20;
var updatePos = 0;
var numOfSnakes = 0;
var snakesArray = [];
// Main Functions \\
/* These are all of the function being called, and created. */
updateSnakeCSS = function (snakes) {
if (snakes.length > -1) {
//for (var i = 0; i < snakes.length; i++) {
console.log(snakes[numOfSnakes - 1]);
$("." + snakes[numOfSnakes - 1]).css("position", "absolute");
$("." + snakes[numOfSnakes - 1]).css("left", player.offset().left - 20);
$("." + snakes[numOfSnakes - 1]).css("top", player.offset().top);
$("." + snakes[numOfSnakes - 1]).css("border", "black");
$("." + snakes[numOfSnakes - 1]).css("border-style", "outset");
$("." + snakes[numOfSnakes - 1]).css("border-width", 2 + "px");
$("." + snakes[numOfSnakes - 1]).css("width", player.width());
$("." + snakes[numOfSnakes - 1]).css("height", player.height());
$("." + snakes[numOfSnakes - 1]).css("background-color", "red");
//}
}
};
addSnake = function (snakes) {
if (numOfSnakes < 5) {
var body = document.getElementsByTagName("body")[0];
var newBodyPart = document.createElement("div");
newBodyPart.className = "game-player" + numOfSnakes;
body.appendChild(newBodyPart);
snakes.push("game-player" + numOfSnakes);
console.log(updatePos);
numOfSnakes++;
}
};
updateScore = function () {
score++;
$(".game-scoreboard").text("Berries: " + score);
};
createFood = function ($parentDiv) {
if (newFood == null) {
var x = WMath.random(220, background.width() * 1.5 - 100);
var y = WMath.random(100, background.height());
var body = document.getElementsByTagName("body")[0];
newFood = document.createElement("div");
newFood.className = "game-food";
body.appendChild(newFood);
$(".game-food").css("width", player.width() / 2);
$(".game-food").css("border", "black");
$(".game-food").css("border-style", "outset");
$(".game-food").css("border-width", 1 + "px");
$(".game-food").css("height", player.width() / 2);
$(".game-food").css("background-color", "red");
$(".game-food").css("position", "absolute");
$(".game-food").css("left", x);
$(".game-food").css("top", y);
}
};
var WMath = {
random: function (min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min); //The maximum is inclusive and the minimum is inclusive
},
};
var WObject = {
isTouching: function ($div1, $div2) {
var x1 = $div1.offset().left;
var y1 = $div1.offset().top;
var h1 = $div1.outerHeight(true);
var w1 = $div1.outerWidth(true);
var b1 = y1 + h1;
var r1 = x1 + w1;
var x2 = $div2.offset().left;
var y2 = $div2.offset().top;
var h2 = $div2.outerHeight(true);
var w2 = $div2.outerWidth(true);
var b2 = y2 + h2;
var r2 = x2 + w2;
if (b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2) return false;
console.log("Touching WObject");
newFood = null;
createFood(document.body);
updateScore();
addSnake(snakesArray);
updateSnakeCSS(snakesArray);
speed += 2;
$div2.remove();
return true;
},
};
createFood(document.body);
$(document).keyup(function (e) {
var key = e.keyCode || e.which;
const keys = {
UP: 38,
DOWN: 40,
LEFT: 37,
RIGHT: 39,
};
if (key === keys.UP && verticalNum > 0 && !String(verticalNum).includes("e")) {
verticalNum -= speed;
if (verticalNum < 0) {
verticalNum = 0;
}
if (snakesArray.length > -1) {
for (var i = 0; i < snakesArray.length; i++) {
$("." + snakesArray[i]).css("top", player.offset().top + i * 23 + updatePos);
$("." + snakesArray[i]).css("left", player.offset().left + updatePos);
}
}
$(".game-player").css("top", verticalNum);
WObject.isTouching($(".game-player"), $(".game-food"));
} else if (key === keys.DOWN && verticalNum < 475.8000000000002) {
verticalNum += speed;
if (verticalNum > 475.8000000000002) {
verticalNum = 475.8000000000002;
}
if (snakesArray.length > -1) {
for (var i = 0; i < snakesArray.length; i++) {
$("." + snakesArray[i]).css("top", player.offset().top - i * 23 - updatePos);
$("." + snakesArray[i]).css("left", player.offset().left - updatePos);
}
}
$(".game-player").css("top", verticalNum);
WObject.isTouching($(".game-player"), $(".game-food"));
} else if (key === keys.LEFT && horizontalNum > 0 && !String(horizontalNum).includes("e")) {
horizontalNum -= speed;
if (horizontalNum < 0) {
horizontalNum = 0;
}
if (snakesArray.length > -1) {
for (var i = 0; i < snakesArray.length; i++) {
$("." + snakesArray[i]).css("left", player.offset().left + i * 23 + updatePos);
$("." + snakesArray[i]).css("top", player.offset().top + updatePos);
}
}
$(".game-player").css("left", horizontalNum);
WObject.isTouching($(".game-player"), $(".game-food"));
} else if (key === keys.RIGHT && horizontalNum < 475.8000000000002) {
horizontalNum += speed;
if (horizontalNum > 475.8000000000002) {
horizontalNum = 475.8000000000002;
}
if (snakesArray.length > -1) {
for (var i = 0; i < snakesArray.length; i++) {
$("." + snakesArray[i]).css("left", player.offset().left - i * 23 - updatePos);
$("." + snakesArray[i]).css("top", player.offset().top - updatePos);
}
}
$(".game-player").css("left", horizontalNum);
WObject.isTouching($(".game-player"), $(".game-food"));
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>GAME</title>
<style>
.game-background {
background-color: lightgray;
width: 500px;
height: 500px;
position: absolute;
top: 55px;
left: 200px;
border: black;
border-style: solid;
border-width: 10px;
}
.game-player {
background-color: blue;
width: 20px;
height: 20px;
position: relative;
top: 0px;
left: 0px;
border: black;
border-style: inherit;
border-width: 2px;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="game-scoreboard">Berries: 0</p>
<div class="game-background">
<div class="game-player"></div>
</div>
<script>
// The Script shown in the JavaScript
</script>
</body>
</html>
It's because of:
speed += 2;
Remove it and it will work fine.
As you're using speed in calculating the verticalNum and horizontalNum everytime, so it increments it with 2 each time, giving a wrong position.
The output after removing it:

JS: detecting a win in a tictactoe game?

I am just doing some practice with a color alignment game. I want to run a function after every move to see if it's winner but I can't figure out the best way to check if 3 colors are in a row or diagonal. I guess I could iterate down the DOM tree each time and see if certain multiples are fulfilled but I don't know if that's the best option.
(() => {
let counter = 0;
let conts = document.querySelectorAll('.parent div')
conts.forEach(x => {
x.addEventListener('click', (evt) => {
if (!!evt.target.style.backgroundColor) {
return;
}
counter++
if (counter % 2 == 0) {
evt.target.style.backgroundColor = "red"
} else {
evt.target.style.backgroundColor = "green"
}
})
});
})();
html, body {
height: 100%;
}
.parent {
height: 100%;
display: grid;
grid-template-columns: 50px 50px 50px;
grid-template-rows: 50px 50px 50px;
grid-gap: 2px;
width: 40%;
}
.parent div {
border: 1px solid #000;
}
<div class="parent">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
You could solve this problem traversing the grid horizontally, vertically, and diagonally, in search of a sequence of "boxes" with the same color.
// used to walk the grid
const size = Math.sqrt(boxes.length);
// used to check grid columns, rows, and diagonal
const check = (color, index, inc = 1) => {
for (let y = 0; y < size; y += inc) {
let score = 0;
for (let x = 0; x < size; x++) {
// index callback to retrieve the next box
if (boxes[index(x, y)].style.backgroundColor === color)
score++;
else
break;
}
if (score === size)
return true;
}
return false;
};
Whenever a color is set, you can perform such check via:
if (
// horizontally
// 0 1 2,
// 3 4 5,
// 6 7 8
check(color, (x, y) => (y * size) + x) ||
// vertically
// 0 3 6,
// 1 4 7,
// 2 5 8
check(color, (x, y) => (y + (size * x))) ||
// diagonally (y loops only once)
// 0 4 8
check(color, (x, y) => x * (size + 1), size) ||
// diagonally (y loops only once)
// 2 4 6
check(color, (x, y) => ((x + 1) * (size - 1)), size)
)
alert(color + ' won!');
Differently from other answers, this logic is independent of the grid size.
You can see the 3x3 live and play around, but you can also see that without changing code you can have a 4x4 grid too.
The whole code in a nutshell:
(() => {
let counter = 0;
const boxes = document.querySelectorAll('.parent div');
const size = Math.sqrt(boxes.length);
const check = (color, index, inc = 1) => {
for (let y = 0; y < size; y += inc) {
let score = 0;
for (let x = 0; x < size; x++) {
if (boxes[index(x, y)].style.backgroundColor === color)
score++;
else
break;
}
if (score === size)
return true;
}
return false;
};
boxes.forEach(x => {
x.addEventListener('click', (evt) => {
if (!!evt.target.style.backgroundColor)
return;
counter++;
const color = counter % 2 ? "green" : "red";
evt.target.style.backgroundColor = color;
if (
check(color, (x, y) => (y * size) + x) ||
check(color, (x, y) => (y + (size * x))) ||
check(color, (x, y) => x * (size + 1), size) ||
check(color, (x, y) => ((x + 1) * (size - 1)), size)
)
alert(color + ' won!');
})
});
})();
I hope this helps 👋
you can try something like this.
let counter = 0;
let winner = null;
const match = (player, row, col) => {
const div = document.querySelector(`.parent #r${row}c${col}`);
const bgColor = div.style.backgroundColor;
return bgColor === player;
}
const win_hori = (player, row) => {
return ![1, 2, 3].some(col => !match(player, row, col));
}
const win_verti = (player, col) => {
return ![1, 2, 3].some(row => !match(player, row, col));
}
const win_slash = (player) => {
return match(player, 1, 1) && match(player, 2, 2) && match(player, 3, 3);
}
const win_backslash = (player) => {
return match(player, 3, 1) && match(player, 2, 2) && match(player, 1, 3);
}
const win = (player, row, col) => {
return win_hori(player, row) || win_verti(player, col) || win_slash(player) || win_backslash(player);
}
document.querySelectorAll('.parent div').forEach(x => {
x.addEventListener('click', (evt) => {
const cell = evt.target;
if (winner || !!cell.style.backgroundColor) {
return;
}
const player = (++counter % 2 == 0) ? "red" : "green";
cell.style.backgroundColor = player;
const row = parseInt(cell.id.substring(1, 2));
const col = parseInt(cell.id.substring(3, 4));
if (win(player, row, col)) {
winner = player;
document.getElementById("winner").innerText = `${player} wins`;
}
})
});
html, body {
height: 100%;
}
.parent {
height: 100%;
display: grid;
grid-template-columns: 50px 50px 50px;
grid-template-rows: 50px 50px 50px;
grid-gap: 2px;
width: 40%;
}
.parent div {
border: 1px solid #000;
}
<div id="winner">nobody wins</div>
<div class="parent">
<div id="r1c1"></div>
<div id="r1c2"></div>
<div id="r1c3"></div>
<div id="r2c1"></div>
<div id="r2c2"></div>
<div id="r2c3"></div>
<div id="r3c1"></div>
<div id="r3c2"></div>
<div id="r3c3"></div>
</div>

Why is my sprite speeding up without changing variables?

I have a bug in this code somewhere that cause the ship to fly faster and faster in space after returning from the planet. I cant figure this one out.
I set the speed to 0.1 so that its super slow in space. When you click on the planet, i run a function that will move canvas's off screen and use zindex to brong a div tag to the top of the stack. When you land and return to orbit, the ship will move slightly faster. After doing it about 10-15 times the ship moves far greater than the 0.1 speed that is set. I will include the html, js and css so you can run it to test.
Here is the whole code.
// Canvas Context's
var canvasMS = document.getElementById('MainScreen_cvs');
var ctxMain = canvasMS.getContext('2d');
var canvasShip = document.getElementById('Ship_cvs');
var ctxShip = canvasShip.getContext('2d');
var PlanetDiv = document.getElementById('PlanetDiv');
var OrbitReturn = document.getElementById('OrbitReturn');
var canvasPlanets = document.getElementById('Planets_cvs');
var ctxPlanets = canvasPlanets.getContext('2d');
var canvasHUD = document.getElementById('HUD_cvs');
var ctxHUD = canvasHUD.getContext('2d');
var canvasSurface = document.getElementById('Surface_cvs');
var ctxSurface = canvasSurface.getContext('2d');
// ----------------------------------End Canvas Context
var Player1;
var Planet1;
var planetClicked;
var gameWidth = canvasMS.width;
var gameHeight = canvasMS.height;
var mouseX = 10000;
var mouseY = 10000;
var SpaceMapX = 10;
var SpaceMapY = 10;
var SurfaceMap = 0;
var SurfaceMap2 = -1600;
var inSpace = true;
var onSurface = false;
var requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
// Load Images
var imgMap = new Image();
imgMap.src = 'images/bg1.png';
var imgButtons = new Image();
imgButtons.src = 'images/button_sprite.png';
var imgBlueWindow = new Image();
imgBlueWindow.src = 'images/blue_window.png';
var imgSprite = new Image();
imgSprite.src = 'images/sprite.png';
var imgPlanets = new Image();
imgPlanets.src = 'images/earthlike_p1.png';
var imgBluesky1 = new Image();
imgBluesky1.src = 'images/bluesky1.png';
var imgBluesky2 = new Image();
imgBluesky2.src = 'images/bluesky2.png';
imgMap.addEventListener('load',init,false);
// ------------- End Loading Images
//-------------------- Create Game Objects ----------------
function CreateGameObjects(){
Player1 = new Ship();
Planet1 = new Planet();
};
//---------------END CREATING GAME OBJECTS------------------
function init(){ //----------------------------------------------- GAME INITIALIZATION
document.addEventListener('keydown',checkKeyDown,false);
document.addEventListener('keyup',checkKeyUp,false);
document.addEventListener("mousedown", checkMouseDown, false);
document.addEventListener("mouseup", checkMouseUp, false);
CreateGameObjects();
Loop();
};
function Loop() { // ---------------- Main Game Loop
clearCtx();
DrawGameObjects();
requestAnimFrame(Loop);
};
function planetSurface(){
if(onSurface){
clearCtx();
HUD();
Player1.draw();
if(mouseY < 21 && mouseX > 693){
ReturntoOrbit();
}
planetClicked.drawSurfaceImg();
var CloseButton = '<button style="float:right;" type="button">Return to Orbit</button>' ;
OrbitReturn.innerHTML = CloseButton;
requestAnimFrame(planetSurface);
}
};
function DrawGameObjects(){
Player1.draw();
Planet1.draw();
HUD();
};
function HUD(){
canvasHUD.style.zIndex = "100";
if (onSurface){
ctxHUD.fillStyle = "#000";
ctxHUD.fillText("locSurface: " + planetClicked.locSurface, 20,30);
}
if (inSpace)
ctxHUD.fillStyle = "#fff";
ctxHUD.fillText("Speed: " + Player1.speed, 60,60);
ctxHUD.fillText("drawX: " + Player1.drawX, 600,40);
ctxHUD.fillText("drawY: " + Player1.drawY, 600,30);
// ctxHUD.fillText("Planet Clicked: " + Planet1.isClicked, 600,50);
}
//----------------------------------------------------------- Objects
/************************************************************************************/
//--------------------------------- SPACE SHIP --------------------------------------
function Ship(){
this.srcX = 0;
this.srcY = 0;
this.srcW = 60;
this.srcH = 60;
this.drawX = 20 ;
this.drawY = 50 ;
this.speed = 0;
this.surfaceSpeed = 10;
this.drift = 0.45;
this.w = 16;
this.h = 16;
this.isUpKey = false;
this.isDownKey = false;
this.isLeftKey = false;
this.isRightKey = false;
this.isSpacebar = false;
this.direction = "n/a";
this.isMoving = false;
this.isClicked = false;
this.surfX = 350;
this.surfY = 200;
};
Ship.prototype.draw = function() {
if(inSpace)
ctxShip.drawImage(imgSprite,this.srcX,this.srcY,this.srcW,this.srcH,this.drawX,this.drawY,this.w,this.h);
if(onSurface)
ctxShip.drawImage(imgSprite,this.srcX,this.srcY,this.srcW,this.srcH,this.surfX,this.surfY,this.w,this.h);
this.checkPos(planetClicked);
};
Ship.prototype.checkPos = function (PlanetX){
if(inSpace){
this.srcY = 0;
this.srcW = 60;
this.w = 16;
this.h = 16;
this.speed = 0.1;
//----------------------------- Move Ship and Map based on the speed of the ship.
if(this.isLeftKey){
this.drawX -= this.speed;
if(SpaceMapX >= 1){
SpaceMapX -= this.speed;
}
}
if(this.isRightKey){
this.drawX += this.speed;
if(SpaceMapX <= 2190)SpaceMapX += this.speed;
}
if(this.isDownKey){
this.drawY += this.speed;
if(SpaceMapY <= 2490)SpaceMapY += this.speed;
}
if (this.isUpKey) {
this.drawY -= this.speed;
if(SpaceMapY >= 1){
SpaceMapY -= this.speed;
}
}
if (SpaceMapY < 0) {SpaceMapY = 0;}
if (SpaceMapX < 0 ) {SpaceMapX = 0}
//--------------------------------------------------------------------END
//-----------------------------------Change Ship Graphic based on direction and map boundaries.
if(this.isUpKey) this.srcX = 360;
if(this.isDownKey) this.srcX = 120;
if(this.isLeftKey) this.srcX = 240;
if(this.isRightKey) this.srcX = 0;
if(this.isUpKey && this.isLeftKey) this.srcX = 300;
if(this.isUpKey && this.isRightKey) this.srcX = 420;
if(this.isDownKey && this.isLeftKey) this.srcX = 180;
if(this.isDownKey && this.isRightKey) this.srcX = 60;
if (this.drawX <= 5) this.drawX = 5;
if (this.drawY <= 5) {this.drawY = 5};
if (this.drawY >= 480) {this.drawY = 480};
if (this.drawX >= 780) {this.drawX = 780};
//----------------------------------------------------------------END
ctxMain.drawImage(imgMap,SpaceMapX,SpaceMapY,gameWidth,gameHeight,0,0,gameWidth,gameHeight);
}
if (onSurface) {
this.srcY = 240;
this.srcW = 92;
this.w = 93;
this.h = 60;
if(this.isLeftKey){
PlanetX.locSurface -= this.surfaceSpeed;
SurfaceMap += this.surfaceSpeed;
SurfaceMap2 += this.surfaceSpeed;
PlanetX.MapDirection = -1;
this.srcX = 93;
}
if(this.isRightKey){
PlanetX.locSurface += this.surfaceSpeed;
SurfaceMap -= this.surfaceSpeed;
SurfaceMap2 -= this.surfaceSpeed;
PlanetX.MapDirection = 1;
this.srcX = 0;
}
}
};
//------------------------------END OF SPACE SHIP ------------------------------------
//----------------------------- PLANET OBJECT INFO ------------------------------------
function Planet(){
this.srcX = 0;
this.srcY = 0;
this.srcW = 100;
this.srcH = 100;
this.w = 50;
this.h = 50;
this.coordX = 100;
this.coordY = 100;
this.planetType = "Small Earthlike Planet."
this.drawX = this.coordX - SpaceMapX;
this.drawY = this.coordY - SpaceMapY;
this.surfaceIMG = imgBluesky1;
this.isClicked = false;
this.locSurface = 0;
};
Planet.prototype.draw = function(){
this.drawX = this.coordX - SpaceMapX;
this.drawY = this.coordY - SpaceMapY;
ifClicked(this);
if(this.isClicked){
PlanetDiv.style.display = "block";
var LandPlanetDivButton = '<button id="LandPlanetDivButton" type="button" onclick="landOnSurface();">Land On Surface</button>';
var ClosePlanetDivButton = '<button id="ClosePlanetDivButton" type="button" onclick="ClosePlanetDiv();">Close (x)</button><br/><p id="PlanetDivText">' ;
PlanetDiv.style.zIndex = "2";
HideCanvas();
planetClicked = this;
PlanetDiv.innerHTML = LandPlanetDivButton + ClosePlanetDivButton + this.planetType; + '</p>';
}
ctxPlanets.drawImage(imgPlanets,this.srcX,this.srcY,this.srcW,this.srcH,this.drawX,this.drawY,this.w,this.h);
};
Planet.prototype.drawSurfaceImg = function(){
if(SurfaceMap2 >= 0) SurfaceMap2 = -1600;
if(SurfaceMap2 < -1600) SurfaceMap2 = -1;
if(SurfaceMap >= 1600) SurfaceMap = 0;
if(SurfaceMap < 0) SurfaceMap = 1599;
ctxSurface.drawImage(this.surfaceIMG, 0, 0, 1600, gameHeight, SurfaceMap, 0, 1600, gameHeight);
ctxSurface.drawImage(this.surfaceIMG, 0, 0, 1600, gameHeight, SurfaceMap2, 0, 1600, gameHeight);
};
//----------------------------- END OF PLANET OBJECT -----------------------------------
//-----end Objects
function randomFromTo(from,to){
return Math.floor(Math.random()*(to-from+1)+from);
};
function closestNum(Num, a){
var num = Num + (gameWidth/2);
var closest = a[0];
var difference = Math.abs (num - closest);
for (var i = 0; i < a.length; i++) {
var difference2 = Math.abs (num - a[i]);
if (difference2 < difference) {
difference = difference2;
closest = a[i];
}
}
return closest;
};
function sortNum(a)
{
var swapped;
do{
swapped = false;
for (var i=0; i < a.length-1; i++) {
if (a[i] > a[i+1]) {
var temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
swapped = true;
}
}
}while (swapped);
};
function ifClicked(obj){
if(mouseX >= obj.drawX && mouseX <= obj.drawX + obj.w){
if(mouseY >= obj.drawY && mouseY <= obj.drawY + obj.h){
obj.isClicked = true;
}
}
else{
obj.isClicked = false;
}
};
function clearCtx() {
ctxMain.clearRect(0,0,gameWidth,gameHeight);
ctxShip.clearRect(0,0,gameWidth,gameHeight);
ctxPlanets.clearRect(0,0,gameWidth,gameHeight);
ctxHUD.clearRect(0,0,gameWidth,gameHeight);
ctxSurface.clearRect(0,0,gameWidth,gameHeight);
};
function checkKeyDown(e){
var keyID = e.keyCode || e.which;
if(keyID === 38 || keyID === 87){ // up arrow or W key
Player1.isUpKey = true;
Player1.direction = "North";
Player1.isMoving = true;
e.preventDefault();
}
if(keyID === 39|| keyID === 68){ // right arrow or D key
Player1.isRightKey = true;
Player1.direction = "East"
Player1.isMoving = true;
e.preventDefault();
}
if(keyID === 40 || keyID === 83){ // down arrow or S key
Player1.isDownKey = true;
Player1.direction = "South";
Player1.isMoving = true;
e.preventDefault();
}
if(keyID === 37 || keyID === 65){ // left arrow or A key
Player1.isLeftKey = true;
Player1.direction = "West";
Player1.isMoving = true;
e.preventDefault();
}
};
function checkKeyUp(e){
var keyID = e.keyCode || e.which;
if(keyID === 38 || keyID === 87){ // up arrow or W key
Player1.isUpKey = false;
Player1.isMoving = false;
e.preventDefault();
}
if(keyID === 39|| keyID === 68){ // right arrow or D key
Player1.isRightKey = false;
Player1.isMoving = false;
e.preventDefault();
}
if(keyID === 40 || keyID === 83){ // down arrow or S key
Player1.isDownKey = false;
Player1.isMoving = false;
e.preventDefault();
}
if(keyID === 37 || keyID === 65){ // left arrow or A key
Player1.isLeftKey = false;
Player1.isMoving = false;
e.preventDefault();
}
};
function clearMouse(){
mouseX = 10000;
mouseY = 10000;
};
function checkMouseDown(e) {
var mX = (e.clientX - (canvasMS.offsetLeft - canvasMS.scrollLeft));
var mY = (e.clientY - (canvasMS.offsetTop - canvasMS.scrollTop));
if(mX <= gameWidth && mX >= 0) mouseX = mX;
if(mY <= gameHeight && mY >= 0) mouseY = mY;
//mouseIsDown = true;
};
function checkMouseUp(e){
//mouseIsDown = false;
clearMouse();
};
function ClosePlanetDiv (){
PlanetDiv.style.zIndex = "-2";
PlanetDiv.innerHTML = "";
PlanetDiv.style.display = "none";
ShowCanvas();
};
function HideCanvas(){
canvasShip.style.marginTop = "-10000px";
canvasPlanets.style.marginTop = "-10000px";
};
function ShowCanvas(){
canvasShip.style.marginTop = "-500px";
canvasPlanets.style.marginTop = "-500px";
};
function landOnSurface(){
ClosePlanetDiv();
inSpace = false;
onSurface = true;
Player1.srcX = 0;
planetSurface();
canvasSurface.style.display = "block";
OrbitReturn.style.display = "block";
};
function ReturntoOrbit(){
OrbitReturn.style.display = "none";
canvasSurface.style.display = "none";
inSpace = true;
onSurface = false;
Loop();
};
<!doctype html>
<html lang='en'>
<head>
<meta charset="utf-8">
<title>Space Explorer</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="Functions.js"></script>
</head>
<body>
<canvas id="MainScreen_cvs" width="800" height="500"></canvas>
<div id="PlanetDiv"></div>
<canvas id="Surface_cvs" width="800" height="500"></canvas>
<canvas id="Ship_cvs" width="800" height="500"></canvas>
<canvas id="Planets_cvs" width="800" height="500"></canvas>
<canvas id="HUD_cvs" width="800" height="500"></canvas>
<div id="OrbitReturn"></div>
<div id="log">
<h1 style="color:blue;">Recent updates will be logged here when made live.</h1>
<hr />
<h3> Wednesday August 3, 2016 </h3>
<ul>
<li> HTML file completed. Working on getting JS files started.</li>
<li> JS files created. </li>
<li>Basic ship & flight functions in place. Basic star map initialized.</li>
</ul>
<hr />
</div>
<script type="text/javascript" src="game.js"></script>
</body>
</html>
body {
background: #303030;
}
#MainScreen_cvs {
position: relative;
display: block;
background: #777777 ;
margin: 30px auto 0px;
z-index: 1;
}
#Surface_cvs{
position: relative;
display: none;
z-index: 1;
margin: -500px auto 0px;
}
#Ship_cvs, #Planets_cvs, #HUD_cvs {
display: block;
position: relative;
margin: -500px auto 0px;
z-index: 1;
}
#log {
display: block;
position: absolute;
top: 560px;
left: 233px;
background: #ffffff;
overflow: scroll;
width: 800px;
height: 300px;
z-index: 3;
}
#OrbitReturn{
display: block;
position: relative;
width: 800px;
height: 500px;
z-index: 3;
margin: -500px auto 0px;
}
#PlanetDiv {
display: block;
position: relative;
width: 800px;
height: 500px;
background-image: url("images/Sky.jpg");
z-index: -2;
margin: -500px auto 0px;
}
#ClosePlanetDivButton{
float: right;
}
#LandPlanetDivButton{
position: absolute;
top: 400px;
left: 325px;
font-size: 20px;
}
#PlanetDivText{
text-indent: 50px;
font-size: 20px;
}
Something is happening with Ship.prototype.checkPos. Each time you land on the planet, it looks like the checking pauses, then starts up when you enter orbit. But this time he checkPos is getting called faster.
I could keep staring at it but you might be able to figure it out from there. I put a console.log('checkPos') at the top of that function and watched it pause and restart.
Ship.prototype.checkPos = function(PlanetX) {
console.log('checkPos');
if (inSpace) {
...
I think it might be here
function ReturntoOrbit() {
OrbitReturn.style.display = "none";
canvasSurface.style.display = "none";
inSpace = true;
onSurface = false;
//Loop(); <--- this little guy. Get rid of him.
};

JavaScript: change object path if collision detected

I have to create some falling snowflakes in javascript, but they have change current path if they get collision with other flake. Something like on this image:
Here is my current code: http://codepen.io/wojtek1150/pen/QyaYdY
var flakePositions = [[]];
var temp = 0;
// snowflake proto
function Snowflake() {
this.pos = new Physics();
// snowflake guid
this.id = '';
// inits
this.MAX_X_START_POS = 100;
this.X_START_OFFSET = 0;
this.MAX_Y_START_POS = 50;
this.Y_START_OFFSET = -50;
this.MAX_X_SPEED = 4;
this.MAX_Y_SPEED = 1.2;
// use to get sin && cos
this.animationStepsCounter = 0
this.fallFactor = 100;
// snowflake html
this.getId = function () {
if (this.id == '') {
this.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
function (c) {
var r = crypto.getRandomValues(new Uint8Array(1))[0] % 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
return this.id;
}
this.initalize = function () {
temp++;
//var size = 5 + Math.random() * 20;
var size = 20;
this.flakeDOM.style.width = size + "px";
this.flakeDOM.style.height = size + "px";
this.flakeDOM.style.opacity = Math.random();
this.pos.x = (Math.random() * this.MAX_X_START_POS);
this.pos.y = this.Y_START_OFFSET+(Math.random() * this.MAX_Y_START_POS);
this.pos.xSpeed = Math.random() * this.MAX_X_SPEED* Math.sign(-0.5 + Math.random());
this.pos.ySpeed = Math.random() * this.MAX_Y_SPEED;
//create array
flakePositions[temp] = [];
flakePositions[temp]['id'] = this.id;
flakePositions[temp]['x'] = this.flakeDOM.style.top;
flakePositions[temp]['y'] = this.flakeDOM.style.left;
flakePositions[temp]['width'] = this.flakeDOM.style.width;
flakePositions[temp]['height'] = this.flakeDOM.style.height;
flakePositions[temp]['xspeed'] = this.pos.xSpeed;
flakePositions[temp]['yspeed'] = this.pos.ySpeed
}
this.move = function () {
this.flakeDOM.style.top = (this.pos.y+=this.pos.ySpeed) + "px";
this.flakeDOM.style.left = (this.pos.x += Math.sin(this.animationStepsCounter/this.fallFactor) * this.pos.xSpeed) + "px";
this.animationStepsCounter += this.pos.ySpeed;
//update array
flakePositions[temp]['x'] = this.flakeDOM.style.top;
flakePositions[temp]['y'] = this.flakeDOM.style.left;
//check position with rest
for (var i = 1, len = flakePositions.length; i < len-1; i++) {
var rect1 = flakePositions[i];
var rect1d = rect1['id'];
var rect1sx = rect1['xspeed'];
var rect1sy = rect1['yspeed'];
var rect1x = parseInt(rect1['x']);
var rect1y = parseInt(rect1['y']);
for (var j = 2, len = flakePositions.length; j < len; j++) {
var rect2 = flakePositions[j];
var rect2d = rect2['id'];
var rect2x = parseInt(rect2['x']);
var rect2y = parseInt(rect2['y']);
//if(rect1x == rect2x && rect1y == rect2y)
if(rect1x < rect2x + 10 && rect1x + 10 > rect2x &&
rect1y < rect2y + 10 && 10 + rect1y > rect2y )
{
console.log('collision detected');
var t = document.getElementById(rect1d);
t.style.top = t.style.top+rect1sx+10;
t.style.left = t.style.left+rect1sy-10;
}
}
}
}
}
function Physics() {
// pos
this.x = 0;
this.y = 0;
this.z = 0;
// speed
this.xSpeed = 0;
this.ySpeed = 0;
this.zSpeed = 0;
// acceleration
this.xAccel = 1;
this.yAccel = 1;
this.zAccel = 1;
}
snowflakes = new Array();
var interval = 0;
function makeThisBoom() {
// snowflakes container
snowfield = document.getElementById('snow');
// snowflakes count
snoflakesCount = 20;
for (var i = 0; i < snoflakesCount; i++) {
snowflakes[i] = new Snowflake();
var flake = document.createElement('div');
snowflakes[i].flakeDOM = flake;
flake.id = snowflakes[i].getId();
flake.classList.add('sf');
snow.appendChild(flake);
snowflakes[i].initalize();
snowflakes[i].move();
}
interval = setInterval(anime,50);
}
function anime() {
for (var flake of snowflakes) {
flake.move();
}
}
function setInterface() {
document.getElementById('startstop').onclick = function () {
if (interval != 0) {
clearInterval(interval);
interval = 0;
} else interval = setInterval(anime, 50);
}
}
document.addEventListener("DOMContentLoaded", makeThisBoom);
document.addEventListener("DOMContentLoaded", setInterface);
.sf{
position:absolute;
z-index:9999999;
/*background: -moz-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
background: radial-gradient(ellipse at center, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=1 );
*/
border-radius:50%;
display:block;
width:20px; height:20px;
/* FOR DEV ONLY */
background:#FFF;
opacity:1!important;
}
body{
background:#222;
overflow:hidden;
}
#snow {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
#startstop{
width:100px;
height:30px;
border:0;
background:rgb(61, 95, 123);
color:#FFF;
outline:none;
}
<button id="startstop">Start/stop</button>
<div id="snow"> </div>
I already know how to get positions and I got if statement to check if there is any collision. But I don't know how to change path in proper way, even just bounce it :(
Any suggestions?
One of the key things for you to think about when doing this, are the cost/reward of implementing this kind of feature. I don't think these collisions will help you create the illusion of snowfalling. When the snowflakes in your current iteration miss each other it gives the illusion of 3d. If they were to hit each other and bounce it might give the incorrect illusion of balls falling in a 2d plane.
That being said, to implement what you are asking without using a library would be a huge time sync. I would recommend taking a look at PhysicsJS or matter-js.
Below you can see the function I personally use in a library I was working on. You can adapt most of it for your use. The truth is this a complicated ask.
define( 'detect/detectCircleCircleCollision' , [ 'lib/underscore' ] , function ( _ ) {
return function detectCircleCircleCollision ( circlePositionA,
circleRadiusA,
circleDisplacementA,
circlePositionB,
circleRadiusB,
circleDisplacementB,
boolean,
normalBody ) {
boolean = _.isUndefined( boolean ) || boolean ? true : false;
normalBody = _.isUndefined( normalBody ) || normalBody ? true : false;
var
relativePosition = circlePositionA.subtract.new( circlePositionB ),
combineRadius = circleRadiusA + circleRadiusB,
relativePositionDotProduct = relativePosition.lengthSqr(),
relativeDisplacement = circleDisplacementA.subtract.new( circleDisplacementB ),
a,b,c,r,t,newCircleOnePosition,newCircleTwoPosition,newCirclePositionDifference,collisionPoint;
if ( relativePositionDotProduct < combineRadius * combineRadius ) {
if ( boolean ) return true;
return collision( 0,//Time
circlePositionB.add.new( vector( relativePosition ).magnitude.set( circleRadiusA ) ),//point
relativePosition.normalize(),//Normal
normalBody,//normalbody
vector( relativePosition ).magnitude.set( circleRadiusA + circleRadiusB - relativePosition.magnitude() ) );//intersection
}
a = relativeDisplacement.dotProduct( relativeDisplacement );
b = relativePosition.dotProduct( relativePosition );
c = relativePositionDotProduct - combineRadius * combineRadius;
r = b * b - a * c;
if ( r < 0 ) return false;
t = -b - r * r / a;
if ( t > 1 || t < 0 ) return false;
else if ( boolean ) return true;
newCircleOnePosition = circleDisplacementA.scale.new( t ).add( circlePositionA );
newCircleTwoPosition = circleDisplacementB.scale.new( t ).add( circlePositionB );
newCirclePositionDifference = newCircleTwoPosition.subtract.new( newCircleOnePosition ).normalize();
collisionPoint = newCirclePositionDifference.scale.new( circleRadiusA );
return collision( t,
collisionPoint.add( newCircleOnePosition ),
newCirclePositionDifference,
normalBody,
false );
};
} );

Categories

Resources