P5.js Image Collision

April 2024 ยท 6 minute read

I recently figured out that image collisions in p5.js are quite different than regular rectangle collisions. If you want to keep aspect ratio of an image without setting manual height and width parameters in the image() function, then you can scale the image with the scale() function. This, however, does not change the area of the image, so to detect collisions, I have to multiply the image by the image scale that I set. For example, if set up in a constructor, to get the player width would be playerImage.width * player.scale.

So I've done all of this, and even made a function to show the area/hitbox of the image. Then, when I set this up with my collision function, the bottom and right sides detect the collisions fine, but the top and left sides seem to act quite strangely.

You can see what's going on here: https://image-colliding.stcollier.repl.co/

Does anyone know why the collision is off for the left and top edges?

Here is my code for the issue:

let debug = true; function preload() { playerImg = loadImage("https://image-colliding.stcollier.repl.co/Player.png") brickImg = loadImage("https://image-colliding.stcollier.repl.co/brick.jpeg") } function setup() { createCanvas(windowWidth, windowHeight) player = new Player(windowWidth/2, windowHeight/2, 0.5) brick = new Brick(windowWidth/10, windowHeight/3, 0.1) speed = 3; } function draw() { background(200, 200, 200) push(); brick.display(); pop(); push(); player.display(); pop(); if (debug) { showRectCollider(player.x, player.y, playerImg.width * player.s, playerImg.height * player.s) showRectCollider(brick.x, brick.y, brickImg.width * brick.s, brickImg.height * brick.s) if (player.collidesImg(brick.x, brick.y, brickImg.width, brickImg.height, brick.s)) { collidingColor = 'green'; } else { collidingColor = 'red' } } if (keyIsPressed && keyCode === UP_ARROW) { moveY(player, -speed) } if (keyIsPressed && keyCode === DOWN_ARROW) { moveY(player, speed) } if (keyIsPressed && keyCode === LEFT_ARROW) { moveX(player, -speed) } if (keyIsPressed && keyCode === RIGHT_ARROW) { moveX(player, speed) } } class Player { constructor(x, y, s) { this.x = x; this.y = y; this.s = s; } display() { translate(this.x, this.y) scale(this.s) image(playerImg, 0, 0) } collidesFunc1(x, y, w, h) { if ( this.x - w/2 <= x + w/2 && this.x + w/2 >= x - w/2 && this.y - h/2 <= y + h/2 && this.y + h/2 >= y - h/2) { return true; } else { return false; } } collidesFunc2(x, y, w, h) { if ( this.x >= x - w && this.x <= x + w && this.y >= y - h && this.y <= y + h) { return true; } else { return false; } } collidesImg(x, y, w, h, s) { if ( this.x >= x - (w * s) && this.x <= x + (w * s) && this.y >= y - (h * s) && this.y <= y + (h * s)) { return true; } else { return false; } } } class Brick { constructor(x, y, s) { this.x = x; this.y = y; this.s = s; } display() { translate(this.x, this.y) scale(this.s) image(brickImg, 0, 0) } } //Other functions var collidingColor = 'red'; function showRectCollider(x, y, w, h) { noFill(); stroke(collidingColor); strokeWeight(3); rect(x, y, w, h) } function moveX(object, speed) { object.x += speed; } function moveY(object, speed) { object.y += speed; }
html, body { margin: 0; padding: 0; overflow: hidden; }
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>repl.it</title> <link href="style.css" rel="stylesheet" type="text/css" /> <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script> </head> <body> <script src="script.js"></script> </body> </html>

I have three functions used for detecting collisions, and currently I'm using the collidesImg() function that I made. All three of the functions can be found on lines 71-106.

Thanks for any help. I really appreciate it.

2

1 Answer

Your collision logic was a bit off, here it is with explanatory comments:

 collidesImg(x, y, w, h, s) { if ( // right edge of player to the right of left edge of brick this.x + playerImg.width * this.s >= x && // left edge of player to the left of right edge of brick this.x <= x + (w * s) && // bottom edge of player below top edge of brick this.y + playerImg.height * this.s >= y && // top edge of player above bottom edge of brick this.y <= y + (h * s)) { return true; } else { return false; } } 
let debug = true; function preload() { playerImg = loadImage("https://image-colliding.stcollier.repl.co/Player.png") brickImg = loadImage("https://image-colliding.stcollier.repl.co/brick.jpeg") } function setup() { createCanvas(windowWidth, windowHeight) player = new Player(windowWidth / 2, windowHeight / 2, 0.5) brick = new Brick(windowWidth / 10, windowHeight / 3, 0.1) speed = 3; } function draw() { background(200, 200, 200) push(); brick.display(); pop(); push(); player.display(); pop(); if (debug) { if (player.collidesImg(brick.x, brick.y, brickImg.width, brickImg.height, brick.s)) { collidingColor = 'green'; } else { collidingColor = 'red' } showRectCollider(player.x, player.y, playerImg.width * player.s, playerImg.height * player.s) showRectCollider(brick.x, brick.y, brickImg.width * brick.s, brickImg.height * brick.s) } if (keyIsPressed && keyCode === UP_ARROW) { moveY(player, -speed) } if (keyIsPressed && keyCode === DOWN_ARROW) { moveY(player, speed) } if (keyIsPressed && keyCode === LEFT_ARROW) { moveX(player, -speed) } if (keyIsPressed && keyCode === RIGHT_ARROW) { moveX(player, speed) } } class Player { constructor(x, y, s) { this.x = x; this.y = y; this.s = s; } display() { translate(this.x, this.y) scale(this.s) image(playerImg, 0, 0) } collidesFunc1(x, y, w, h) { if ( this.x - w / 2 <= x + w / 2 && this.x + w / 2 >= x - w / 2 && this.y - h / 2 <= y + h / 2 && this.y + h / 2 >= y - h / 2) { return true; } else { return false; } } collidesFunc2(x, y, w, h) { if ( this.x >= x - w && this.x <= x + w && this.y >= y - h && this.y <= y + h) { return true; } else { return false; } } collidesImg(x, y, w, h, s) { if ( // right edge of player to the right of left edge of brick this.x + playerImg.width * this.s >= x && // left edge of player to the left of right edge of brick this.x <= x + (w * s) && // bottom edge of player below top edge of brick this.y + playerImg.height * this.s >= y && // top edge of player above bottom edge of brick this.y <= y + (h * s)) { return true; } else { return false; } } } class Brick { constructor(x, y, s) { this.x = x; this.y = y; this.s = s; } display() { translate(this.x, this.y) scale(this.s) image(brickImg, 0, 0) } } //Other functions var collidingColor = 'red'; function showRectCollider(x, y, w, h) { noFill(); stroke(collidingColor); strokeWeight(3); rect(x, y, w, h) } function moveX(object, speed) { object.x += speed; } function moveY(object, speed) { object.y += speed; }
html, body { margin: 0; padding: 0; overflow: hidden; }
<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script> </head> <body> </body> </html>

ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJobXJxZm19eYKOqWxmoqNitq6txp5knKecoba0tc6n