Conditions and Decisions
I find myself in a bit of a dilemma when it comes to answering these types of questions. As you're just starting out, I want to avoid overwhelming you with complex code and techniques, yet at the same time, I don't want to promote any incorrect practices.
The Simple Answer
First off, let's address your code snippet:
//This is the part where I need the Help!!!!!!!!!
// the test checks the center of the drop
// if drop is greater than > top of player (y) and (&&)
// drop x greater than > left side of player x and (&&) drop is less than <
// right side of player (x + rectWidth) then drop has hit player
if (yPos[i] > y && xPos[i] > x && xPos[i] < x + rectWidth ){
x = 0; // move player back
}
By the way, you're drawing the player's rectangle for each raindrop. It would be better to move that draw function outside the loop.
The Detailed Answer
I hope my explanation isn't too confusing, and I've included ample comments to clarify my decisions.
To maintain organization, I segment different elements into separate objects - the player
, rain
, and keyboard handler. These components are coordinated through the mainLoop
, which is triggered once per frame using requestAnimationFrame
and executes necessary functions.
The player
object encompasses all player-related data and functions for drawing and updating the player's position.
The rain
object contains an array called rain.drops
to store raindrops along with functions for drawing, updating, randomizing drops, and adding new ones.
In determining if rain hits the player, I handle this check within the rain.update
function while moving the raindrops. Since I'm unsure of the desired outcome upon impact, I simply reset the raindrop and increment a hit counter.
Initially, I inspect whether the raindrop's bottom (drop.y + drop.radius
) surpasses the player's top (player.y
), thus omitting unnecessary evaluations for rain above the player.
Next, I evaluate the rain horizontally. Assessing for non-collision scenarios simplifies logic. If the drop's right is left of the player's left or its left is right of the player's right, I determine the drop doesn't contact the player by putting the condition inside parentheses and preceding it with a negation operator if(! ( ... ) )
.
Given the complexity of the test, I break it into two lines for clarity.
// drop is a single rain drop player is the player
if (drop.y + drop.radius >= player.y) {
if (! (drop.x + drop.radius < player.x ||
drop.x - drop.radius > player.x + player.width)) {
// code for player hit by rain in here
}
}
Furthermore, the rain.update
function also checks if raindrops touch the canvas' bottom and resets them accordingly.
Live Demonstration
I've tweaked and incorporated your provided code into a functional setup.
addEventListener("load",function(){
var canvas = document.getElementById('gameCanvas');
var ctx = canvas.getContext('2d');
ctx.font = "20px arial";
var frameCount = 0;
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var currentMaxDrops = 5;
const numberFramesPerRainIncrease = 60 * 5;
const maxRainDrops = 150;
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
requestAnimationFrame(mainLoop);
const keys = {
ArrowLeft : false,
ArrowRight : false,
}
function keyEvent(event){
if(keys[event.code] !== undefined){
event.preventDefault();
keys[event.code] = event.type === "keydown";
}
}
const player = {
x : 0,
y : HEIGHT - 20,
width : 20,
height : 20,
speed : 4,
color : "red",
showHit : 0,
hitCount : 0,
status(){
if(player.hitCount === 0){
return "Dry as a bone.";
}
if(player.hitCount < 5){
return "A little damp.";
}
if(player.hitCount < 15){
return "Starting to get wet.";
}
return "Soaked to the core";
},
draw(){
if(player.showHit > 0){
player.showHit -= 1;
ctx.fillStyle = "blue";
}else{
ctx.fillStyle = player.color;
}
ctx.fillRect(player.x,player.y,player.width,player.height);
},
update(){
if(keys.ArrowLeft){
player.x -= player.speed;
keys.ArrowLeft = false;
if(player.x < 0){
player.x = 0;
}
}
if(keys.ArrowRight){
player.x += player.speed;
keys.ArrowRight = false;
if(player.x + player.width >= WIDTH){
player.x = WIDTH - player.width;
}
}
}
}
const rain = {
numberRainDrops : 50,
drops : [],
randomizeDrop(drop){
drop.x = Math.random() * WIDTH;
drop.y = -10;
drop.radius = Math.random() *3 + 1;
drop.speed = Math.random() * 4 + 1;
return drop;
},
createDrop(){
if(rain.drops.length < currentMaxDrops){
rain.drops.push(rain.randomizeDrop({}));
rain.numberRainDrops = rain.drops.length;
}
},
draw(){
ctx.beginPath();
ctx.fillStyle = 'blue';
for(var i = 0; i < rain.drops.length; i ++){
var drop = rain.drops[i];
ctx.arc(drop.x, drop.y, drop.radius, 0, 2 * Math.PI);
ctx.closePath();
}
ctx.fill();
},
update(){
for(var i = 0; i < rain.drops.length; i ++){
var drop = rain.drops[i];
drop.y += drop.speed;
if(drop.y + drop.radius >= player.y){
if(!(drop.x + drop.radius < player.x ||
drop.x - drop.radius > player.x + player.width)){
player.hitCount += 1;
player.showHit += 5;
rain.randomizeDrop(drop);
}
}
if(drop.y > HEIGHT + drop.radius){
rain.randomizeDrop(drop);
}
}
}
}
function mainLoop () {
requestAnimationFrame(mainLoop);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
frameCount += 1;
if(frameCount % numberFramesPerRainIncrease === 0){
if(currentMaxDrops < maxRainDrops){
currentMaxDrops += 1;
}
}
rain.createDrop();
rain.update();
player.update();
player.draw();
rain.draw();
ctx.fillStyle = "black";
ctx.fillText("Hit " + player.hitCount + " times.",5,20);
ctx.setTransform(0.75,0,0,0.75,5,34);
ctx.fillText(player.status(),0,0);
ctx.setTransform(1,0,0,1,0,0);
};
});
canvas {
border : 2px black solid;
}
<canvas id="gameCanvas" width=512 height=200></canvas>
I trust this information proves valuable to you.