How to detect collision? - javascript

I am building a video game where a spaceship moves with controllers and it must avoid some fireballs in order to win. However now I would like to setup a collision system: when a fireball touches the spaceship, game is over (alert("game over")). Any help with this? Thanks!!!
let spaceship = document.querySelector("#icon")
//Fireball script
const fireballArray = []
function generateFireBallWithAttributes(el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key])
};
return el
}
function createFireBalls(amount) {
for (let i = 0; i <= amount; i++) {
fireballArray.push(generateFireBallWithAttributes(document.createElement("img"), {
src: "Photo/fireball.png",
width: "40"
}))
}
}
createFireBalls(10)
fireballArray.forEach((fireballElement) => {
const fallStartInterval = setInterval(function() {})
document.querySelector("body").appendChild(fireballElement);
const fireballMovement = {
x: fireballRandom(fireballElement.offsetWidth),
y: 0
}
const fireLoop = function() {
fireballMovement.y += 2;
fireballElement.style.top = fireballMovement.y + "px";
if (fireballMovement.y > window.innerHeight) {
fireballMovement.x = fireballRandom(fireballElement.offsetWidth);
fireballElement.style.left = fireballMovement.x + "px";
fireballMovement.y = 0
}
}
fireballElement.style.left = fireballMovement.x + "px";
let fireInterval = setInterval(fireLoop, 1000 / ((Math.random() * (50)) + 75))
})
function fireballRandom(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
//Spaceship moves into space + prevent going out borders
let hits = 0
let pos = {
top: 1000,
left: 570
}
const keys = {}
window.addEventListener("keydown", function(e) {
keys[e.keyCode] = true
})
window.addEventListener("keyup", function(e) {
keys[e.keyCode] = false
})
const loop = function() {
if (keys[37] || keys[81]) {pos.left -= 5}
if (keys[39] || keys[68]) {pos.left += 5}
if (keys[38] || keys[90]) {pos.top -= 4}
if (keys[40] || keys[83]) {pos.top += 4}
if (pos.left < 0) { pos.left = -1}
if (pos.top < 0) {pos.top = -1}
if (pos.left + spaceship.offsetWidth >= body.offsetWidth) {
pos.left = body.offsetWidth - spaceship.offsetWidth
}
if (pos.top + spaceship.offsetHeight >= body.offsetHeight) {
pos.top = body.offsetHeight - spaceship.offsetHeight
}
spaceship.setAttribute("data", body.offsetWidth + ":" + body.offsetHeight)
spaceship.style.left = pos.left + "px";
spaceship.style.top = pos.top + "px"
}
let sensibilty = setInterval(loop, 8)
<body>
<img src="Photo/Spaceship1.png" id="icon">
</body>
Check this out (hitboxes)
Also this

Your code is missing complete information. That said, as your game progresses with explicit and independent setIntervals on fireballs and spaceship, and you dont want to make bigger changes I suggest you call the checkCollision() function any time fireball moves or spaceship moves (that is in the loop and fireloop "animation functions").
The collision itself is done with the following:
function detectOverlap(fireball) {
const shipRect = spaceship.getBoundingClientRect();
const fireballRect = fireball.getBoundingClientRect();
//these two variables are of following type
//DOMRect {x: 570, y: 836, width: 104.484375, height: 112.828125, ...}
// this will test if the ship collides with specific fireball
if (
(shipRect.x + shipRect.width > fireballRect.x && shipRect.x < fireballRect.x + fireball.width)
&& (shipRect.y + shipRect.height > fireballRect.y && shipRect.y < fireballRect.y + fireball.height)
) {
return true;
} else {
return false;
}
};
function checkCollision() {
let collision = false;
fireballArray.forEach(fireball => {
if (detectOverlap(fireball)) { collision = true };
});
// this is satisfied if the ship collides with any fireball
if (collision === true) {
// do some logic on collision
console.log('collision');
}
};
This will do rectangle vs rectangle collision detection. This might not fit the best, but it is a good first approximation.
NOTE: You can check the devtools console (F12) to assess the correct collision detection.

Related

Customised scroll isnt working on smartphones

i have an issue concerning a JS scrolling function.
const scroll = document.querySelectorAll(".scroll");
const maxIndex = 3; //NB DE PAGES
let index = 0;
let animationEnd = true;
let boolUp = 0;
const start = {
x: 0,
y: 0
};
function touchStart(event) {
event.preventDefault();
start.x = event.touches[0].pageX;
start.y = event.touches[0].pageY;
}
function touchMove(event) {
event.preventDefault();
const offset = {};
offset.x = start.x - event.touches[0].pageX;
offset.y = start.y - event.touches[0].pageY;
scrollHandler({
deltaY: offset.y
});
}
function scrollHandler(e) {
if(e.preventDefault) e.preventDefault();
if (animationEnd) {
if (e.deltaY > 0) index++;
else index--;
if (index < 0) index = 0;
if (index > scroll.length - 1) index = scroll.length - 1;
scroll[0].style.marginTop = "-" + index * 100 + "vh";
animationEnd = false;
setTimeout(() => animationEnd = true, 450)
}
}
function keyScroll(e) {
if (e.key == "ArrowUp") {
index--;
if (index < 0) {
index = 0;
}
scroll[0].style.marginTop = "-" + index * 100 + "vh";
animationEnd = false;
setTimeout(() => animationEnd = true, 450);
} else if (e.key == "ArrowDown") {
if (index < maxIndex) {
index++;
scroll[0].style.marginTop = "-" + index * 100 + "vh";
animationEnd = false;
setTimeout(() => animationEnd = true, 450);
}
}
}
I'm calling those by body event listeners :
document.addEventListener("scroll", (e) => e.preventDefault())
document.body.addEventListener("keydown", keyScroll);
document.body.addEventListener("wheel", scrollHandler,);
document.body.addEventListener("touchstart", touchStart, false);
document.body.addEventListener("touchmove", touchMove, false);
It's working perfectly => example at https://darleanow.github.io .
But when i'm on smartphone (Iphone), scroll might bug and the height of divs would appear different, you can try it on your own smartphone to see what i'm talking about, try scrolling a bit fast etc...
If anybody has an idea :)
This isn't a solution necessarily, but you might try printing scroll[0].stlye.marginTop to the console every time it changes.
If that value is never erroneous, I'd guess the issue is with the animation (maybe scrolling while it's animating, or something like that).

Make movement more smooth on Java Script Game | JavaScript Canvas

I am working on very simple java script game. there's a falling random object (trash) and another object for catch the falling object (trash bin). everything seems fine but i wanted to make the movement of the trash bin more smooth. Do you have any idea to fix this? Thanks in Advance
this is my code
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var canvasBack = document.getElementById("backgroundCanvas");
var contextBack = canvasBack.getContext("2d");
var timer;
//mengatur hiscore
var hiscore = 0;
//Background image, musik and arrays musik.
var background = new Image();
background.src = 'assets/bgw2.jpg';
var catchSounds = [];
var catchSoundCounter = 0;
for(var i = 0; i < 5; i++)
{
var catchSound = new Audio();
catchSound.src = 'Audio/bleep.wav';
catchSounds.push(catchSound);
}
var music = new Audio();
music.src = 'Audio/MarimbaBoy.wav';
music.loop = true;
var smashSounds = [];
var smashCounter = 0;
for(var i = 0; i < 5; i++)
{
var smash = new Audio();
smash.src = 'Audio/smash.mp3';
smashSounds.push(smash);
}
var player;
var fruits = [];
var numberOfFruits = 15;
//Player constructor
function Player()
{
this.gameOver = false;
this.score = 0;
this.fruitsCollected = 0;
this.fruitsMissed = 0;
this.playerWidth = 150;
this.playerHeight = 90;
this.playerSpeed = 10;
this.x = canvas.width / 2;
this.y = canvas.height - this.playerHeight;
this.playerImage = new Image();
this.playerImage.src = 'assets/binn.png';
//Draws the player
this.render = function()
{
context.drawImage(this.playerImage, this.x, this.y);
}
//Moves the player left
this.moveLeft = function()
{
if(this.x > 0)
{
this.x -= this.playerSpeed;
}
}
//Moves the player right
this.moveRight = function()
{
if(this.x < canvas.width - this.playerWidth)
{
this.x += this.playerSpeed;
}
}
}
//Fruit constructor
function Fruit()
{
this.fruitNumber = Math.floor(Math.random() * 5);
this.fruitType = "";
this.fruitScore = 0;
this.fruitWidth = 50;
this.fruitHeight = 50;
this.fruitImage = new Image();
this.fruitSpeed = Math.floor(Math.random() * 2 + 1);
this.x = Math.random() * (canvas.width - this.fruitWidth);
this.y = Math.random() * -canvas.height - this.fruitHeight;
//Creates a different kind of fruit depending on the fruit number
//which is generated randomly
this.chooseFruit = function()
{
if(this.fruitNumber == 0)
{
this.fruitType = "pisang";
this.fruitScore = 5 * this.fruitSpeed;
this.fruitImage.src = 'assets/pisang.png';
}
else if(this.fruitNumber == 1)
{
this.fruitType = "rubbish";
this.fruitScore = 10 * this.fruitSpeed;
this.fruitImage.src = 'assets/rubbish.png';
}
else if(this.fruitNumber == 2)
{
this.fruitType = "botol";
this.fruitScore = 15 * this.fruitSpeed;
this.fruitImage.src = 'assets/botol.png';
}
else if(this.fruitNumber == 3)
{
this.fruitType = "coke";
this.fruitScore = 20 * this.fruitSpeed;
this.fruitImage.src = 'assets/coke.png';
}
else if(this.fruitNumber == 4)
{
this.fruitType = "apple";
this.fruitScore = 25 * this.fruitSpeed;
this.fruitImage.src = 'assets/apple.png';
}
else if(this.fruitNumber == 5)
{
this.fruitType = "papikra";
this.fruitScore = 30 * this.fruitSpeed;
this.fruitImage.src = 'assets/papikra.png';
}
}
//Makes the fruit descend.
//While falling checks if the fruit has been caught by the player
//Or if it hit the floor.
this.fall = function()
{
if(this.y < canvas.height - this.fruitHeight)
{
this.y += this.fruitSpeed;
}
else
{
smashSounds[smashCounter].play();
if(smashCounter == 4)
{
smashCounter = 0;
}
else
{
smashCounter++;
}
player.fruitsMissed += 1;
this.changeState();
this.chooseFruit();
}
this.checkIfCaught();
}
//Checks if the fruit has been caught by the player
//If it is caught, the player score and fruit counter is increased, and
//the current fruit changes its state and becomes a different fruit.
this.checkIfCaught = function()
{
if(this.y >= player.y)
{
if((this.x > player.x && this.x < (player.x + player.playerWidth)) ||
(this.x + this.fruitWidth > player.x && this.x + this.fruitWidth < (player.x + player.playerWidth)))
{
catchSounds[catchSoundCounter].play();
if(catchSoundCounter == 4)
{
catchSoundCounter = 0;
}
else
{
catchSoundCounter++;
}
player.score += this.fruitScore;
player.fruitsCollected += 1;
this.changeState();
this.chooseFruit();
}
}
}
//Randomly updates the fruit speed, fruit number, which defines the type of fruit
//And also changes its x and y position on the canvas.
this.changeState = function()
{
this.fruitNumber = Math.floor(Math.random() * 5);
this.fruitSpeed = Math.floor(Math.random() * 2 + 1);
this.x = Math.random() * (canvas.width - this.fruitWidth);
this.y = Math.random() * -canvas.height - this.fruitHeight;
}
//Draws the fruit.
this.render = function()
{
context.drawImage(this.fruitImage, this.x, this.y);
}
}
//Adds controls. Left arrow to move left, right arrow to move right.
//ENTER to restart only works at the game over screen.
window.addEventListener("keydown", function(e){
e.preventDefault();
if(e.keyCode == 37)
{
player.moveLeft();
}
else if(e.keyCode == 39)
{
player.moveRight();
}
else if(e.keyCode == 13 && player.gameOver == true)
{
main();
window.clearTimeout(timer);
}
});
main();
//Fills an array of fruits, creates a player and starts the game
function main()
{
contextBack.font = "bold 20px Dominique";
contextBack.fillStyle = "WHITE";
player = new Player();
fruits = [];
for(var i = 0; i < numberOfFruits; i++)
{
var fruit = new Fruit();
fruit.chooseFruit();
fruits.push(fruit);
}
startGame();
}
function startGame()
{
updateGame();
window.requestAnimationFrame(drawGame);
}
//Checks for gameOver and makes each fruit in the array fall down.
function updateGame()
{
music.play();
if(player.fruitsMissed >= 10)
{
player.gameOver = true;
}
for(var j = 0; j < fruits.length; j++)
{
fruits[j].fall();
}
timer = window.setTimeout(updateGame, 30);
}
//Draws the player and fruits on the screen as well as info in the HUD.
function drawGame()
{
if(player.gameOver == false)
{
context.clearRect(0, 0, canvas.width, canvas.height);
contextBack.clearRect(0, 0, canvasBack.width, canvasBack.height);
contextBack.drawImage(background, 0, 0);
player.render();
for(var j = 0; j < fruits.length; j++)
{
fruits[j].render();
}
contextBack.fillText("SCORE: " + player.score, 30, 50);
contextBack.fillText("HIGHEST SCORE: " + hiscore, 140, 50);
contextBack.fillText("FRUIT CAUGHT: " + player.fruitsCollected, 320, 50);
contextBack.fillText("FRUIT MISSED: " + player.fruitsMissed, 490, 50);
}
else
{
//Different screen for game over.
for(var i = 0; i < numberOfFruits; i++)
{
console.log("Speed was" + fruits[fruits.length - 1].fruitSpeed);
fruits.pop();
}
if(hiscore < player.score)
{
hiscore = player.score;
contextBack.fillText("NEW HI SCORE: " + hiscore, (canvas.width / 2) - 80, canvas.height / 2);
}
contextBack.fillText("PRESS ENTER TO RESTART", (canvas.width / 2) - 100, canvas.height / 2 + 50);
context.clearRect(0, 0, canvas.width, canvas.height);
}
window.requestAnimationFrame(drawGame);
}
}
First of all, just use one requestAnimationFrame and no other setTimeout code. These can start to run out of sync with each other and it's hard to pause them all when the player wants to pause the game.
Instead, use a counter instead of timeout.
let fruitCounter = 0
function drawGame() {
// all your draw code here
...
// every 60 frames drop a new fruit
fruitCounter++
if(fruitCounter > 60){
fruitCounter = 0
dropNewFruit()
}
// request the new frame unless its game over
if(!gameOver) {
requestAnimationFrame(drawGame)
}
}
Also, if you use keyboard events like this, it will always be choppy because there is a delay when typing on the keyboard (just press a letter for a long time in a text field, you will see that it takes a while until more letters appear)
You can fix this by setting a variable once a key gets pressed.
window.addEventListener("keydown", function(e){
if(e.keyCode == 37){
moveLeft = 1
}
}
window.addEventListener("keyup", function(e){
if(e.keyCode == 37){
moveLeft = 0
}
}
Then, you can use that variable in your animation code
function drawGame() {
// all your draw code here
player.x += moveLeft
// request the new frame unless its game over
if(!gameOver) {
requestAnimationFrame(drawGame)
}
}
These are just a few tips! This has always worked for me when I build a javascript game.

Collision detection not working (Javascript)

I'm trying to build a simple game and having hard time with collision detecting. I want the alert to be popped up when the hero hits the virus and below is my js code. I'm not sure if my collision detect logic is wrong too. The viruses are moving randomly and I used CSS transition for them to move gradually.
var henryLocation = {
top: 700,
left: 700
}
document.onkeydown = function (evt) {
// console.log(evt)
if (evt.keyCode === 38 && henryLocation.top > 10) {
henryLocation.top = henryLocation.top - 25
} else if (evt.keyCode === 40 && henryLocation.top < 700) {
henryLocation.top = henryLocation.top + 25
} else if (evt.keyCode === 37 && henryLocation.left > 10) {
henryLocation.left = henryLocation.left - 25
} else if (evt.keyCode === 39 && henryLocation.left < 1360) {
henryLocation.left = henryLocation.left + 25
}
moveHenry()
}
function moveHenry () {
document.getElementById('henry').style.top = henryLocation.top + 'px'
document.getElementById('henry').style.left = henryLocation.left + 'px'
}
const startBtn = document.getElementById('btn-start')
startBtn.addEventListener("click", theGame)
function theGame () {
const startGame = setInterval(moveCorona, 1300)
function moveCorona () {
const theCorona = document.getElementById('corona')
const theCorona1 = document.getElementById('corona1')
const theCorona2 = document.getElementById('corona2')
const w = 1300, h = 600
theCorona.style.top = Math.floor(Math.random() * h) + 'px'
theCorona.style.left = Math.floor(Math.random() * w) + 'px'
theCorona1.style.top = Math.floor(Math.random() * h) + 'px'
theCorona1.style.left = Math.floor(Math.random() * w) + 'px'
theCorona2.style.top = Math.floor(Math.random() * h) + 'px'
theCorona2.style.left = Math.floor(Math.random() * w) + 'px'
function collisionDetect () {
var theHenry = henry.getBoundingClientRect();
var theCorona = corona.getBoundingClientRect();
if ((theCorona.top > theHenry.top && theCorona.top < theHenry.bottom) || (theCorona.bottom > theHenry.top && theCorona.bottom < theHenry.bottom)) {
let verticalCollision = true
} else {
let verticalCollision = false
}
if ((theCorona.right > theHenry.left && theCorona.right < theHenry.right) || (theCorona.left < theHenry.right && theCorona.left > theHenry.left)) {
let horizontalCollision = true
} else {
let horizontalCollision = false
}
if (verticalCollision && horizontalCollision) {
alert('collision detected')
} else {
console.log('Game goes on')
}
}
// collisionDetect()
}
}
function gameLoop () {
setTimeout(gameLoop, 1000)
}
gameLoop()

How to detect collisions for a complex object in JavaScript

How should I detect collisions for a sprite that can't have a rectangular hitbox? I am trying to make a platformer, and I'm currently working on collision detections for the level's platforms. Here's my physics:
function onTimerTick() {
if (moveY > 300) {
momentumY = momentumY - 2;
move(moveX, moveY);
} else if (moveY < 300) {
moveY = 300;
momentumY = 0;
move(moveX, moveY);
}
moveY = moveY + momentumY;
move(moveX, moveY);
if (moveY <= 300) {
if (momentumX > 0) {
right = true;
momentumX -= 1;
} else if (momentumX < 0) {
if (document.getElementById("spriteNotReal").getAttribute("scr") != "walkLeft.gif") {
document.getElementById("spriteNotReal").src = "walkLeft.gif";
}
right = false;
momentumX += 1;
} else {
if (right == true) {
document.getElementById("spriteNotReal").src = "amaincharacter.png";
} else {
document.getElementById("spriteNotReal").src = "maincharacterleft.png";
}
}
moveX = moveX + momentumX;
} else {
moveX += momentumX / 3 + 1;
document.getElementById("spriteNotReal").src = "jumpmain.gif";
}
document.getElementById("gold").innerHTML = momentumX;
}
I'm thinking of trying to break up the level into different rectangles, all in the same class, and then test for collisions within that class. Is there any way I could do this? Also, I think I should use boundingClientRect, but I don't know how to use this for collisions.

an if statement returns true, but the condition won't trigger

I have an if statement in my code as you can see below. When I check in console log when those two elements collide with each other, the if statement becomes true, but it won't stop my interval function. I wonder what would have caused it to not work as it should?
var animationTimer = setInterval(bladAnimation, 30);
document.onkeydown = function(event) {
if (event.keyCode == 37) {
// <--
fx = -15;
fy = 0;
froskAnimation()
}
if (event.keyCode == 38) {
// opp
fy = -15;
fx = 0;
froskAnimation()
}
if (event.keyCode == 39) {
// -->
fx = 15;
fy = 0;
froskAnimation()
}
if (event.keyCode == 40) {
// ned
fy = 15;
fx = 0;
froskAnimation()
}
}
function froskAnimation() {
Xfrosk = fx + Xfrosk;
Yfrosk = fy + Yfrosk;
if ((Yfrosk + 70 > maxY) || (Yfrosk < minY)) {
Yfrosk = Yfrosk - fy;
}
if ((Xfrosk + 70 > maxX) || (Xfrosk < minX)) {
Xfrosk = Xfrosk - fx;
}
frosk.style.left = Xfrosk + "px";
frosk.style.top = Yfrosk + "px";
console.log(Xfrosk)
}
function bladAnimation() {
Yblad = by + Yblad;
if ((Yblad + 70 > maxY) || (Yblad < minY)) {
by = by * -1;
}
blad.style.top = Yblad + "px";
}
if (blad.x < frosk.x + frosk.width && blad.x + blad.width > frosk.x
&& blad.y < frosk.y + frosk.height && blad.y + blad.height > frosk.y) {
clearInterval(animationTimer);
}
full code here: https://codepen.io/ctrlvi/pen/jXbJYG
That condition is executed only once, when your javascript is loaded. At that point the condition is probably false. You should move it at the end of bladAnimation (I suppose what you're trying to do is stop the self-moving block when it hits the other one).
function bladAnimation() {
Yblad = by + Yblad;
if ((Yblad + 70 > maxY) || (Yblad < minY)) {
by = by * -1;
}
blad.style.top = Yblad + "px";
if (blad.x < frosk.x + frosk.width && blad.x + blad.width > frosk.x
&& blad.y < frosk.y + frosk.height && blad.y + blad.height > frosk.y) {
clearInterval(animationTimer);
}
}

Categories

Resources