Refers to a global variable while encapsulated one is available - javascript

In case below "player" referring to global variables, that breaks OOP principle of encapsulation. Whatever I need from the global scope pass to the class instance via constructor. How can I did it?
If I create parent variable (some Character), than code will not work. Maybe this is a nube question, but I don't know, how it works.
p.s. - when I take "player" in "Enemy" thought "this", all construction breaks down.
var Enemy = function(x, y, speed, player) {
this.x = x;
this.y = y;
this.speed = speed;
this.sprite = 'images/enemy-bug.png';
this.player = player;
};
Enemy.prototype.update = function(dt) {
this.x += +(this.speed * dt);
if (this.x > canvasWidth) {
this.x = borgSpawn;
this.speed = Math.floor(100 + Math.random() * 200);
}
if (
player.x > this.x - enemySize &&
player.x < this.x + enemySize &&
player.y > this.y - enemySize &&
player.y < this.y + enemySize
) {
player.x = playerSpawnX;
player.y = playerSpawnY;
}
};
Enemy.prototype.render = function() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
};
var enemyLocation = [60, 140, 220];
var allEnemies = enemyLocation.map(y => new Enemy(0, y, 300, player));
var Player = function(x, y) {
this.x = x;
this.y = y;
this.sprite = 'images/char-boy.png';
};
Player.prototype.update = function() {
if (this.y < 60) {
this.x = playerSpawnX;
this.y = playerSpawnY;
}
};
Player.prototype.render = function() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
};
Player.prototype.handleInput = function(key) {
if (key === 'left' && this.x > 0) {
this.x = this.x - horizontalStep;
} else if (key === 'right' && this.x != stepZone) {
this.x = this.x + horizontalStep;
} else if (key === 'up') {
this.y = this.y - verticalStep;
} else if (key === 'down' && this.y != stepZone) {
this.y = this.y + verticalStep;
}
};
var player = new Player(playerSpawnX, playerSpawnY);
document.addEventListener('keyup', function(e) {
var allowedKeys = {
37: 'left',
38: 'up',
39: 'right',
40: 'down'
};
player.handleInput(allowedKeys[e.keyCode]);
});

No need to pass "player" parameter to "enemy" object. Your Enemy.prototype.update function will work fine without this reference.

Related

Property in JS class is not changing the value, always is undefined

This is a part of code for make an Snake Game in JS, the problem is that I want to set oldDirection the actual direction to make an animation with the sprites in the U-turns but this.direction is having new value but oldDirection is always undefined.
class SnakeBody {
constructor(x, y, ctx) {
this.x = x
this.y = y
this.oldX
this.oldY
this.ctx = ctx
this.direction
this.oldDirection
this.sprite = new Image()
}
// Draws Players Body taking direction from head to set the direction of sprite
draw(headDirection) {
this.direction = headDirection
this.sprite.onload = () => {
this.ctx.drawImage(this.sprite, this.x, this.y, 20, 20)
}
}
// Update Player Body to new X and Y, and this must receive direction to change sprite
update(newX, newY, newDirection) {
// Problem here OldDirection doesn't get the address value
this.oldDirection = this.direction
this.direction = newDirection
this.ctx.clearRect(this.x, this.y, 20, 20)
this.oldX = this.x
this.oldY = this.y
this.x = newX;
this.y = newY;
this.sprite.onload = () => {
this.ctx.drawImage(this.sprite, this.x, this.y, 20, 20)
}
}
setDirection(nDirection) {
this.oldDirection = this.direction
this.direction = nDirection
}
getDirection() {
return this.direction
}
// Sets the sprite for the snake body
setSnakeSpriteDirection(newDirection) {
switch (true) {
case this.direction == 'DOWN' && newDirection == 'LEFT':
this.sprite.src = 'src/assets/img/snakeBody_rotateDownLeft.png'
break
case this.direction == 'UP' || this.direction == 'UP' &&
newDirection == 'DOWN' || this.direction == 'DOWN':
this.sprite.src = 'src/assets/img/snakeBody_down.png'
break;
case this.direction == 'RIGHT' || this.direction == 'RIGHT' &&
newDirection == 'LEFT' || this.direction == 'LEFT':
this.sprite.src = 'src/assets/img/snakeBody_left.png'
break;
default:
this.sprite.src = 'src/assets/img/snakeBody_left.png'
break;
}
this.draw()
}
}
EDIT:
SnakeBody:
class SnakeBody {
constructor(x, y, ctx) {
this.x = x
this.y = y
this.oldX
this.oldY
this.ctx = ctx
this.direction
this.oldDirection
this.sprite = new Image()
}
draw(headDirection) {
this.direction = headDirection
this.sprite.onload = () => {
this.ctx.drawImage(this.sprite, this.x, this.y, 20, 20)
}
}
update(newX, newY, newDirection) {
this.oldDirection = this.direction
this.direction = newDirection
this.ctx.clearRect(this.x, this.y, 20, 20)
console.log('This.Direction:', this.direction, 'This.oldDirection:', this.oldDirection);
this.oldX = this.x
this.oldY = this.y
this.x = newX;
this.y = newY;
this.sprite.onload = () => {
this.ctx.drawImage(this.sprite, this.x, this.y, 20, 20)
}
}
setDirection(nDirection) {
this.oldDirection = this.direction
this.direction = nDirection
}
getDirection() {
return this.direction
}
setSnakeSpriteDirection(newDirection) {
switch(true) {
case this.direction == 'DOWN' && newDirection == 'LEFT':
this.sprite.src = 'src/assets/img/snakeBody_rotateDownLeft.png'
break
case this.direction == 'UP' || this.direction == 'UP'
&& newDirection == 'DOWN' || this.direction == 'DOWN':
this.sprite.src = 'src/assets/img/snakeBody_down.png'
break;
case this.direction == 'RIGHT' || this.direction == 'RIGHT'
&& newDirection == 'LEFT' || this.direction == 'LEFT':
this.sprite.src = 'src/assets/img/snakeBody_left.png'
break;
default:
this.sprite.src = 'src/assets/img/snakeBody_left.png'
break;
}
this.draw()
}
}
Player Class:
class Player {
constructor(x, y, name, ctx) {
this.x = x;
this.y = y;
this.oldX;
this.oldY;
this.name = name;
this.ctx = ctx;
this.direction
this.oldDirection
this.body = []
this.sprite = new Image()
this.counter = 0;
}
draw() {
switch (this.getDirection()) {
case 'UP':
this.sprite.src = 'src/assets/img/snakeHead.png'
break;
case 'DOWN':
this.sprite.src = 'src/assets/img/snakeHead_down.png'
break;
case 'RIGHT':
this.sprite.src = 'src/assets/img/snakeHead_right.png'
break;
case 'LEFT':
this.sprite.src = 'src/assets/img/snakeHead_left.png'
break;
default:
this.sprite.src = 'src/assets/img/snakeHead.png'
break;
}
this.sprite.onload = () => {
this.ctx.drawImage(this.sprite, this.x, this.y, 20, 20)
}
}
update(newX, newY) {
this.ctx.clearRect(this.x, this.y, 20, 20)
this.x = newX;
this.y = newY;
}
updateBody() {
if (this.body.length > 0) {
this.body.forEach((bodyPart, index) => {
if( index == 0 ) {
bodyPart.update(this.x, this.y, this.oldDirection)
bodyPart.setSnakeSpriteDirection(this.direction)
} else {
bodyPart.update(
this.body[index - 1].oldX,
this.body[index - 1].oldY,
this.body[index - 1].oldDirection
)
bodyPart.setSnakeSpriteDirection(this.body[index - 1].getDirection())
}
})
}
}
movement() {
this.updateBody()
this.oldY = this.y
this.oldX = this.x
switch (this.direction) {
case 'DOWN':
this.y != 380
? this.update(this.x, this.y + 20)
: this.update(this.x, 0)
break;
case 'UP':
this.y == 0
? this.update(this.x, 380)
: this.update(this.x, this.y - 20)
break;
case 'LEFT':
this.x == 0
? this.update(380, this.y)
: this.update(this.x - 20, this.y)
break;
case 'RIGHT':
this.x != 380
? this.update(this.x + 20, this.y)
: this.update(0, this.y)
break;
default:
break;
}
this.oldDirection = this.direction
}
setDirection(direction) {
this.oldDirection = this.direction
this.direction = direction
}
getDirection() {
return this.direction
}
checkCollision(block) {
if (
this.x < block.x + 20 &&
this.x + 20 > block.x &&
this.y < block.y + 20 &&
this.y + 20 > block.y
) {
let newBody = new SnakeBody(this.x, this.y, this.ctx)
newBody.setDirection()
this.body.push(newBody)
}
}
}
Game: (Where I set the Player direction):
document.addEventListener('DOMContentLoaded', (e) => {
const canvasGame = document.querySelector('canvas')
const ctx = canvasGame.getContext('2d')
const
cHeight = canvasGame.height = 400,
cWidth = canvasGame.width = 400;
let cells = 20;
const drawGrid = () => {
ctx.lineWidth = 1.1
ctx.strokeStyle = '#232332'
ctx.shadowBlur = 0
for (let i = 0; i < cells; i++) {
let f = (cHeight / cells) * i
ctx.beginPath()
ctx.moveTo(f, 0);
ctx.lineTo(f, cHeight)
ctx.stroke()
ctx.beginPath();
ctx.moveTo(0, f)
ctx.lineTo(cHeight, f)
ctx.stroke()
ctx.closePath()
}
}
const drawPlayer = () => {
let playerX = Math.floor(Math.random() * 19 - 0 + 1) * 20
let foodX = Math.floor(Math.random() * 19 - 0 + 1) * 20
let foodY = Math.floor(Math.random() * 19 - 0 + 1) * 20
let playerY = Math.floor(Math.random() * 19 - 0 + 1) * 20
let food = new Food(foodX, foodY, 'red', ctx)
let player = new Player(playerX, playerY, 'Moises', ctx)
player.draw()
food.draw()
document.addEventListener('keydown', (e) => {
switch (e.keyCode) {
case 37:
player.setDirection('LEFT')
break;
case 39:
player.setDirection('RIGHT')
break
case 38:
player.setDirection('UP')
break
case 40:
player.setDirection('DOWN')
break;
}
})
setInterval(() => {
player.movement()
player.checkCollision(food)
food.collision(player)
player.draw()
food.draw()
drawGrid()
}, 450)
}
drawGrid()
drawPlayer()
})
<canvas></canvas>
Food Class:
class Food {
constructor(x, y, color, ctx) {
this.x = x
this.y = y
this.color = color
this.ctx = ctx
}
draw() {
let img = new Image()
img.src = 'src/assets/img/apple.png'
img.onload = () => {
this.ctx.drawImage(img, this.x, this.y, 20, 20);
// this.ctx.fillStyle = this.color
// this.ctx.fillRect(this.x, this.y, 20, 20)
}
}
update(newX, newY) {
this.ctx.clearRect(this.x, this.y, 20, 20)
this.x = newX;
this.y = newY;
// this.ctx.fillRect(newX, newY, 20, 20)
}
collision(player) {
let newX = (Math.floor(Math.random() * 19 - 0 + 1)) * 20;
let newY = (Math.floor(Math.random() * 19 - 0 + 1)) * 20;
if (
player.x < this.x + 20 &&
player.x + 20 > this.x &&
player.y < this.y + 20 &&
player.y + 20 > this.y
) {
this.update(newX, newY)
}
}
}
Seems to work fine for me.
class SnakeBody {
constructor() {
this.direction
this.oldDirection
}
test( newDirection ) {
this.oldDirection = this.direction;
this.direction = newDirection;
}
}
const snake = new SnakeBody();
snake.test( 1 );
snake.test( 2 );
console.log( snake.oldDirection );
EDIT:
i'm guessing your problem is here, in your player class:
index == 0
? (
bodyPart.update(this.x, this.y, this.oldDirection),
bodyPart.setSnakeSpriteDirection(this.direction)
)
: (
bodyPart.update(
this.body[index - 1].oldX,
this.body[index - 1].oldY,
this.body[index - 1].oldDirection
),
bodyPart.setSnakeSpriteDirection(this.body[index - 1].getDirection())
)
Does this code not give you errors? it feels like you are using a different language here. you are putting commas in between lines of code. you are using a ternary operator as if it were just an if statement, or something like that. you're putting parenthesis around lines of code. i'm surprised its not giving errors in the console.
Edit 2:
Try replacing this strange ternary operator with this code instead:
if( index == 0 ) {
bodyPart.update(this.x, this.y, this.oldDirection)
bodyPart.setSnakeSpriteDirection(this.direction)
} else {
bodyPart.update(
this.body[index - 1].oldX,
this.body[index - 1].oldY,
this.body[index - 1].oldDirection
)
bodyPart.setSnakeSpriteDirection(this.body[index - 1].getDirection())
}

Collision detection in my js platformer game. Can't stop the object, but detection works

I'm creating a platformer game, and I'm adding collision, but I'm not sure how to stop the object after collision is detected. This is my javascript, and I have a basic html document with a tag. Could someone help me out with stopping an object after I detect collision? I feel like my solutions I've come up with are much to complicated.
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const characterImage = document.getElementById('character')
const level = 1
canvas.width = document.body.scrollWidth
canvas.height = document.body.scrollHeight
let time; // Current time
let prevTime = Date.now(); // Store previous time
let isGrounded; // Check if player is on the ground
class Main {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lives = 3;
this.speedX = 0;
this.speedY = 0;
this.gravity = .01;
// this.gravitySpeed = 0;
this.jumpSpeed = -1.5; // How fast to jump upwards
this.dx = 0;
this.dy = 0;
this.centerX = canvas.width / 2;
this.centerY = canvas.height / 2;
}
draw() {
if (this.x <= -0) {
this.x = -0
}
if (this.x >= canvas.width - 50) {
this.x = canvas.width - 50
}
const player = {
image: characterImage,
x: this.x,
y: this.y,
w: 50,
h: 50
}
const obstacle1 = {
x: this.centerX,
y: canvas.height - 100,
w: 50,
h: 50
}
const lava = {
}
const ground = {
x: 0,
y: canvas.height - 50,
w: canvas.width,
h: 50
}
//collision detection
if (player.x < obstacle1.x + obstacle1.w &&
player.x + obstacle1.w > obstacle1.x &&
player.y < obstacle1.y + obstacle1.h &&
player.y + player.h > obstacle1.y) {
}
ctx.beginPath();
ctx.fillStyle = '#9b7653'
ctx.fillRect(ground.x, ground.y, ground.w, ground.h)
ctx.closePath();
ctx.beginPath();
ctx.drawImage(player.image, player.x, player.y, player.w, player.h);
ctx.closePath();
//obstacles
ctx.beginPath();
ctx.fillStyle = '#df4759'
ctx.fillRect(obstacle1.x, obstacle1.y, obstacle1.w, obstacle1.h);
ctx.closePath();
}
newPos() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
}
update() {
// Calculate how much time has passed since last update
time = Date.now();
const deltaTime = time - prevTime;
// Update y-position based speed in y-direction
// If we jump this.speed will be set to this.jumpSpeed
this.y += this.speedY * deltaTime;
// Gravity should always affect the player!
// The ground check will make sure we don't fall through the floor
this.y += this.gravity * deltaTime;
// Make sure to reduce our player's speed in y by gravity!
this.speedY += this.gravity * deltaTime;
// Only allow the player to jump if he is on the ground
if (controller1.up && isGrounded) {
// Set the player y-speed to jump speed
this.speedY = this.jumpSpeed;
};
if (controller1.right) {
this.dx += 0.7
};
if (controller1.left) {
this.dx -= 0.7
};
this.x += this.dx;
// this.y += this.dy;
this.dx *= 0.9;
this.dy *= 0.9;
// Ground check
if (this.y >= canvas.height - 100) {
this.y = canvas.height - 100;
isGrounded = true;
} else {
isGrounded = false;
}
// Store the current time to use for calculation in next update
prevTime = Date.now();
}
}
class Controller {
constructor() {
this.up = false;
this.right = false;
this.down = false;
this.left = false;
let keyEvent = (e) => {
if (e.code == "KeyW" || e.code == "ArrowUp" || e.code == "Space") {
this.up = e.type == 'keydown'
};
if (e.code == "KeyD" || e.code == "ArrowRight") {
this.right = e.type == 'keydown'
};
if (e.code == "KeyA" || e.code == "ArrowLeft") {
this.left = e.type == 'keydown'
};
}
addEventListener('keydown', keyEvent);
addEventListener('keyup', keyEvent);
addEventListener('mousemove', keyEvent)
}
}
let main1 = new Main(50, canvas.height - 150, 50, 50)
let controller1 = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
main1.update();
main1.draw();
requestAnimationFrame(animate)
}
function updatePos() {
main1.newPos();
}
animate()
setInterval(updatePos, 10)
<canvas id="canvas"></canvas>
<img src="http://placekitten.com/50/50" id="character" style="display: none">
It looks like your code has two sets of velocities for the character: this.speedY gives the vertical velocity from jumping, and this.dx gives the horizontal velocity from controller input, while this.dy isn't actually used. So if you want the character to stop from a collision, you could use something like this:
if (player.x < obstacle1.x + obstacle1.w &&
player.x + obstacle1.w > obstacle1.x &&
player.y < obstacle1.y + obstacle1.h &&
player.y + player.h > obstacle1.y) {
this.dx = this.speedY = 0;
}
You probably want to unify the velocities into one pair of values, e.g. this.dx and this.dy, and adjust either when the player's velocity changes (from any action or gravity).
If you want to disable certain actions while the player is colliding, you might want to set a variable that you can use in the update() function. For example:
this.isColliding =
player.x < obstacle1.x + obstacle1.w &&
player.x + obstacle1.w > obstacle1.x &&
player.y < obstacle1.y + obstacle1.h &&
player.y + player.h > obstacle1.y;
if (this.isColliding) {
this.dx = this.speedY = 0;
}
As another note, prevTime and isGrounded should really be properties of the class (this.prevTime, etc.), not global variables. And time could be declared local to update(), as it's not needed otherwise.
Collision detection in this way is not as simple as a single block of code. This is a good start to determine if the objects do collide but you will then need to pass that information to another function to handle the actions that should be taken based on the side of the collision. This is refered to as 'broad phase' and 'narrow phase'.
Note that there's is no, one perfect formula that handles every rectangle CD. You may find yourself needing to alter your main function to handle specific object within the game at some point because they have different properties.
Now I will provide you with a working example here using your code but I must highly recommend you don't continue piling all of your game objects into one class like you are doing. This is already making things difficult and will only make your game more complicated in the long run. You should break out you obstacles, lava, ground etc and give each their own class. I also notice that you are trying to put all of your CD into the class. This is sometimes ok but for what you are currently trying to accomplish I would have separate functions.
You'll notice in this snippet that the player actually enters into the ground a bit and the obstacles before going back to it's spot. The code overall is a bit finicky because of how it's written.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const characterImage = document.getElementById("character");
const level = 1;
canvas.width = document.body.scrollWidth;
canvas.height = 400//document.body.scrollHeight
let time; // Current time
let prevTime = Date.now(); // Store previous time
let player, obstacle1, obstacle2, ground;
class Main {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lives = 3;
this.speedX = 0;
this.speedY = 0;
this.gravity = 0.01;
this.jumping = false;
//this.gravitySpeed = 0;
this.jumpSpeed = -1.5; // How fast to jump upwards
this.dx = 0;
this.dy = 0;
this.centerX = canvas.width / 2;
this.centerY = canvas.height / 2;
}
draw() {
if (this.x <= -0) {
this.x = -0;
}
if (this.x >= canvas.width - 50) {
this.x = canvas.width - 50;
}
player = {
image: characterImage,
x: this.x,
y: this.y,
w: 50,
h: 50
};
obstacle1 = {
x: this.centerX,
y: canvas.height - 100,
w: 50,
h: 50
};
obstacle2 = {
x: this.centerX + 50,
y: canvas.height - 100,
w: 50,
h: 50
};
const lava = {};
ground = {
x: 0,
y: canvas.height - 50,
w: canvas.width,
h: 50
};
ctx.beginPath();
ctx.fillStyle = "#9b7653";
ctx.fillRect(ground.x, ground.y, ground.w, ground.h);
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = "pink";
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.closePath();
//obstacles
ctx.beginPath();
ctx.fillStyle = "#df4759";
ctx.fillRect(obstacle1.x, obstacle1.y, obstacle1.w, obstacle1.h);
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = "#df4759";
ctx.fillRect(obstacle2.x, obstacle2.y, obstacle2.w, obstacle2.h);
ctx.closePath();
}
newPos() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
}
update() {
// Calculate how much time has passed since last update
time = Date.now();
const deltaTime = time - prevTime;
if (controller1.up && !this.jumping) {
// Set the player y-speed to jump speed
this.speedY = this.jumpSpeed;
this.jumping = true; //prevents jumping while already in air
}
if (controller1.right) {
this.dx += 0.7;
}
if (controller1.left) {
this.dx -= 0.7;
}
this.y += this.speedY * deltaTime;
this.y += this.gravity * deltaTime;
this.speedY += this.gravity * deltaTime;
this.x += this.dx;
// this.y += this.dy;
this.dx *= 0.9;
this.dy *= 0.9;
// Store the current time to use for calculation in next update
prevTime = Date.now();
}
}
class Controller {
constructor() {
this.up = false;
this.right = false;
this.down = false;
this.left = false;
let keyEvent = (e) => {
if (e.code == "KeyW" || e.code == "ArrowUp" || e.code == "Space") {
this.up = e.type == "keydown";
}
if (e.code == "KeyD" || e.code == "ArrowRight") {
this.right = e.type == "keydown";
}
if (e.code == "KeyA" || e.code == "ArrowLeft") {
this.left = e.type == "keydown";
}
};
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
addEventListener("mousemove", keyEvent);
}
}
function collision(player, obj) {
if (
player.x + player.w < obj.x ||
player.x > obj.x + obj.w ||
player.y + player.h < obj.y ||
player.y > obj.y + obj.h
) {
return;
}
this.narrowPhase(player, obj);
}
function narrowPhase(player, obj) {
let playerTop_ObjBottom = Math.abs(player.y - (obj.y + obj.h));
let playerRight_ObjLeft = Math.abs(player.x + player.w - obj.x);
let playerLeft_ObjRight = Math.abs(player.x - (obj.x + obj.w));
let playerBottom_ObjTop = Math.abs(player.y + player.h - obj.y);
if (
player.y <= obj.y + obj.h &&
player.y + player.h > obj.y + obj.h &&
playerTop_ObjBottom < playerRight_ObjLeft &&
playerTop_ObjBottom < playerLeft_ObjRight
) {
main1.y = obj.y + obj.h;
main1.speedY = 0;
}
if (
player.y + player.h >= obj.y &&
player.y < obj.y &&
playerBottom_ObjTop < playerRight_ObjLeft &&
playerBottom_ObjTop < playerLeft_ObjRight
) {
main1.y = obj.y - player.h - 0.05;
main1.speedY = 0;
main1.jumping = false;
}
if (
player.x + player.w >= obj.x &&
player.x < obj.x &&
playerRight_ObjLeft < playerTop_ObjBottom &&
playerRight_ObjLeft < playerBottom_ObjTop
) {
main1.x = obj.x - obj.w - 0.05;
main1.dx = 0;
}
if (
player.x <= obj.x + obj.w &&
player.x + player.w > obj.x + obj.w &&
playerLeft_ObjRight < playerTop_ObjBottom &&
playerLeft_ObjRight < playerBottom_ObjTop
) {
main1.x = obj.x + obj.w + 0.05;
main1.dx = 0;
}
}
let main1 = new Main(50, canvas.height - 250, 50, 50);
let controller1 = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
main1.draw();
main1.update();
collision(player, obstacle2);
collision(player, obstacle1);
collision(player, ground);
requestAnimationFrame(animate);
}
function updatePos() {
main1.newPos();
}
animate();
setInterval(updatePos, 10);
<canvas id="canvas"></canvas>
I'm going to provide a second example where I've removed the objects from the main class and made each there own. I also removed the dx and dy as I think there was confusion about those and mixing them with speedX and speedY. On another note in your newPos() function you have this.x += this.speedX; but you also have that in the update function so in reality your are doubling it. I don't think that;s what you wanted.
You'll see in this example the collision is much smoother
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const characterImage = document.getElementById("character");
const level = 1;
canvas.width = document.body.scrollWidth;
canvas.height = 400//document.body.scrollHeight
let time; // Current time
let prevTime = Date.now(); // Store previous time
class Entity {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lives = 3;
this.speedX = 0;
this.speedY = 0;
this.gravity = 0.01;
this.jump = false;
this.jumpSpeed = -1.5; // How fast to jump upwards
this.centerX = canvas.width / 2;
this.centerY = canvas.height / 2;
}
draw() {
ctx.beginPath();
ctx.fillStyle = "pink";
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.closePath();
}
newPos() {
this.gravitySpeed += this.gravity;
//this.x += this.speedX;
}
collision() {
if (this.x <= -0) {
this.x = -0;
}
if (this.x >= canvas.width - 50) {
this.x = canvas.width - 50;
}
}
update() {
// Calculate how much time has passed since last update
time = Date.now();
const deltaTime = time - prevTime;
if (controller1.up && !this.jump) {
this.speedY = this.jumpSpeed;
this.jump = true;
}
if (controller1.right) {
this.speedX += 0.7;
}
if (controller1.left) {
this.speedX -= 0.7;
}
this.y += this.speedY * deltaTime;
this.y += this.gravity * deltaTime;
this.speedY += this.gravity * deltaTime;
this.x += this.speedX;
this.speedX *= 0.9;
this.speedY *= 0.9;
// Store the current time to use for calculation in next update
prevTime = Date.now();
}
}
class Obstacle {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = '#df4759';
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
/*
class Lava {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = 'red';
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
*/
class Ground {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = '#8a6c4e';
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
class Controller {
constructor() {
this.up = false;
this.right = false;
this.down = false;
this.left = false;
let keyEvent = (e) => {
if (e.code == "KeyW" || e.code == "ArrowUp" || e.code == "Space") {
this.up = e.type == "keydown";
}
if (e.code == "KeyD" || e.code == "ArrowRight") {
this.right = e.type == "keydown";
}
if (e.code == "KeyA" || e.code == "ArrowLeft") {
this.left = e.type == "keydown";
}
};
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
addEventListener("mousemove", keyEvent);
}
}
function collision(player, obj) {
if (
player.x + player.w < obj.x ||
player.x > obj.x + obj.w ||
player.y + player.h < obj.y ||
player.y > obj.y + obj.h
) {
return;
}
this.narrowPhase(player, obj);
}
function narrowPhase(player, obj) {
let playerTop_ObjBottom = Math.abs(player.y - (obj.y + obj.h));
let playerRight_ObjLeft = Math.abs(player.x + player.w - obj.x);
let playerLeft_ObjRight = Math.abs(player.x - (obj.x + obj.w));
let playerBottom_ObjTop = Math.abs(player.y + player.h - obj.y);
if (
player.y <= obj.y + obj.h &&
player.y + player.h > obj.y + obj.h &&
playerTop_ObjBottom < playerRight_ObjLeft &&
playerTop_ObjBottom < playerLeft_ObjRight
) {
player.y = obj.y + obj.h;
player.speedY = 0;
}
if (
player.y + player.h >= obj.y &&
player.y < obj.y &&
playerBottom_ObjTop < playerRight_ObjLeft &&
playerBottom_ObjTop < playerLeft_ObjRight
) {
player.y = obj.y - player.h;
// player.dy = 0;
player.speedY = 0;
player.jump = false;
}
if (
player.x + player.w >= obj.x &&
player.x < obj.x &&
playerRight_ObjLeft < playerTop_ObjBottom &&
playerRight_ObjLeft < playerBottom_ObjTop
) {
player.x = obj.x - obj.w;
player.speedX = 0;
}
if (
player.x <= obj.x + obj.w &&
player.x + player.w > obj.x + obj.w &&
playerLeft_ObjRight < playerTop_ObjBottom &&
playerLeft_ObjRight < playerBottom_ObjTop
) {
player.x = obj.x + obj.w;
player.speedX = 0;
}
}
let obstacle1 = new Obstacle(canvas.width/2, canvas.height - 100, 50, 50)
let obstacle2 = new Obstacle(canvas.width/2 + 50, canvas.height - 100, 50, 50)
let ground = new Ground(0, canvas.height - 50, canvas.width, 50)
let player = new Entity(50, canvas.height - 250, 50, 50);
let controller1 = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
collision(player, obstacle2);
collision(player, obstacle1);
collision(player, ground);
player.draw();
player.update();
ground.draw();
obstacle1.draw();
obstacle2.draw();
requestAnimationFrame(animate);
}
function updatePos() {
player.newPos();
}
animate();
setInterval(updatePos, 10);
<canvas id="canvas"></canvas>

Prototype function calling an object undefined

I am trying to make a collision function for my new platformer game and I came across an error when making my collision prototype function.
platforms = [];
var newPlatform = function(x, y) {
this.x = x,
this.y = y,
this.width = 50,
this.height
}
var platform1 = new newPlatform(10, 100);
var platform2 = new newPlatform(50, 50);
platforms.push(platform1);
platforms.push(platform2);
var player = {
x:100,
y:305,
width:80,
height:120,
velocity_y:4
}
platforms.prototype.collision = function(player) {
return (this.x < player.x + player.width && this.x + this.width > player.x && this.y
< player.y + player.height && this.y + this.height > player.y);
}
for (let i = 0; i < platforms.length; i++) {
if (platforms[i].collision(player)) {
player.velocity_y = 0 ;
}
}
This gives out an error that reads "Cannot set property 'collision' of undefined" does anyone know how to fix this?

p5.js classes (class "example" is not changing my this.x value)

I want to move an ellipse but I am stuck because my this.x value isn't changing... print("hit this.y = 30"); is working just fine but this--; is not working. Is anything blocking it? Thank you in advance for your time!
let redBubble = 0;
let x = false;
let y = 0;
let d = 0;
function setup() {
createCanvas(400, 400);
redBubble = new Bubble(300, 100, 10);
}
function draw() {
background(56, 23, 56);
redBubble.show();
redBubble.toRight();
redBubble.toLeft();
redBubble.example();
};
class Bubble {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
fill(255);
ellipse(this.x, this.y, this.d * 2);
}
toRight() {
if (this.x < 350) {
this.x = this.x + 1;
} else if (this.x === 350) {
this.y = this.y - 1;
}
}
toLeft() {
if (this.y <= 30) {
x = true;
} else if (this.y === 30) {
x = false;
}
}
example() {
if (x) {
this.x--; // not working
print("hit this.y = 30"); // working
}
}
}
Your code is running, but it sounds like it's not doing what you want it to.
All of your functions show(), toRight(), toLeft(), example(), are running every single draw. So every single time, toRight() is incrementing this.x by 1 (unless this.x is exactly 350). So when example() decrements it by 1, they are in effect canceling each other out and not changing this.x, when x is true and this.x is 349.
If you wanted the net effect to be this.x to decrease, you could replace this.x--; with this.x = this.x - 2;. But this is not a great solution.
Using states
It looks like you want to do different things at different stages of your animation, i.e. go right, then go up, then go left. As you've discovered, it can be pretty tricky to coordinate all of these different changes when all of your functions are running every cycle.
Instead, it's a lot easier to conditionally run only the functions you want, depending on what stage of your animation you're in. For this you can use an internal variable to store what it should be doing (I'll use this.direction for this example). Then in your draw function, you'd use if/then statements, like this:
function draw() {
background(56, 23, 56);
redBubble.show();
if (redBubble.direction === 'right') {
redBubble.goRight();
} else if (redBubble.direction === 'up') {
redBubble.goUp();
} else if (redBubble.direction === 'left') {
redBubble.goLeft();
}
};
And then in your individual functions, you'd change direction based on some condition. For example:
toRight() {
this.x = this.x + 1;
if (this.x === 350) {
this.direction = 'up';
}
}
Here's a working snippet of the above technique:
let redBubble;
function setup() {
createCanvas(400, 400);
redBubble = new Bubble(300, 100, 10);
}
function draw() {
background(56, 23, 56);
redBubble.show();
if (redBubble.direction === 'right') {
redBubble.goRight();
} else if (redBubble.direction === 'up') {
redBubble.goUp();
} else if (redBubble.direction === 'left') {
redBubble.goLeft();
} else if (redBubble.direction === 'down') {
redBubble.goDown();
}
};
class Bubble {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
this.direction = 'right';
}
show() {
fill(255);
ellipse(this.x, this.y, this.d * 2);
}
goRight() {
this.x = this.x + 1;
if (this.x === 350) {
this.direction = 'up';
}
}
goLeft() {
this.x = this.x - 1;
if (this.x === 30) {
this.direction = 'down';
}
}
goUp() {
this.y = this.y - 1;
if (this.y === 30) {
this.direction = 'left';
}
}
goDown() {
this.y = this.y + 1;
if (this.y === 300) {
this.direction = 'right';
}
}
}
<script src="https://unpkg.com/p5#0.6.1/lib/p5.min.js"></script>

Collision detection not working for canvas

I've been trying to make a game but there are obstacles. I have a (ball) player and a square (obstacle) and I can't figure out how to make a collision detection thing to work. Here's my code so far:
<!DOCTYPE html>
<html>
<head>
<title>Ball Race</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id="canvas" width="800" height="200"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var circle = function (x, y, radius, fillCircle) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fillCircle) {
ctx.fill();
} else {
ctx.stroke();
}
};
var drawRect = function (x, y) {
ctx.fillRect(x, y, 20, 20)
}
var Object = function () {
this.x = width / 4;
this.y = height / 4;
}
// The Ball constructor
var Ball = function () {
this.x = width / 2;
this.y = height / 2;
this.xSpeed = 5;
this.ySpeed = 0;
};
// Update the ball's position based on its speed
Ball.prototype.move = function () {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x < 11) {
this.x = 11;
} else if (this.x > width - 11) {
this.x = width - 11;
} else if (this.y < 11) {
this.y = 11;
} else if (this.y > height - 11) {
this.y = height - 11;
}
};
// Draw the ball at its current position
Ball.prototype.draw = function () {
circle(this.x, this.y, 10, true);
};
Object.prototype.draw = function () {
drawRect(this.x, this.y)
}
//collision types
Object.prototype.checkCollision = function () {
var col1 = this.x == ball.x && this.y == ball.y;
var col2 = this.x + 1 == ball.x && this.y == ball.y;
var col3 = this.x + 2 == ball.x && this.y == ball.y;
var col4 = this.x + 3 == ball.x && this.y == ball.y;
if (col1 || col2 || col3 || col4) {
alert("COLLISION!");
}
}
// Set the ball's direction based on a string
Ball.prototype.setDirection = function (direction) {
if (direction === "up") {
this.xSpeed = 0;
this.ySpeed = -5;
} else if (direction === "down") {
this.xSpeed = 0;
this.ySpeed = 5;
} else if (direction === "left") {
this.xSpeed = -5;
this.ySpeed = 0;
} else if (direction === "right") {
this.xSpeed = 5;
this.ySpeed = 0;
} else if (direction === "stop") {
this.xSpeed = 0;
this.ySpeed = 0;
}
};
// Create the ball object
var ball = new Ball();
var object = new Object();
// An object to convert keycodes into action names
var keyActions = {
32: "stop",
37: "left",
38: "up",
39: "right",
40: "down"
};
// The keydown handler that will be called for every keypress
$("body").keydown(function (event) {
var direction = keyActions[event.keyCode];
ball.setDirection(direction);
});
// The animation function, called every 30 ms
setInterval(function () {
ctx.clearRect(0, 0, width, height);
ball.draw();
ball.move();
object.draw();
ctx.strokeRect(0, 0, width, height);
}, 30);
setInterval(function () {
object.checkCollision();
}, 1)
</script>
</body>
</html>
How would you code this? Please give an example similar to mine.
It seems like most of the functionality is there you're just missing the correct logic in the checkCollision. I would change it to something like:
Object.prototype.checkCollision = function() {
var colx = (ball.x-ball.radius<this.x&&ball.x+ball.radius>this.x)||(ball.x-ball.radius<this.x+20&&ball.x+ball.radius>this.x+20);
var coly = (ball.y-ball.radius<this.y&&ball.y+ball.radius>this.y)||(ball.y-ball.radius<this.y+20&&ball.y+ball.radius>this.y+20);
if(colx&&coly){
console.log('collision');
}
}
colx checks the x collision, to see if the ball is in between the objects position. coly does the same thing but for the y variable. If both are true then there is a collision.
I added radius variable to Ball
var Ball = function() {
this.x = width / 2;
this.y = height / 2;
this.xSpeed = 5;
this.ySpeed = 0;
this.radius = 10;
};
Here is a fiddle

Categories

Resources