- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我使用 vanilla Javascript 和 HTML canvas 制作了一款游戏。将 gem 对象添加到游戏板后,我添加了代码,当它与它们碰撞时会增加玩家得分,这是一个 if
语句在一个原型(prototype)方法中,用 ES6 类语法格式化(在 class gem
)。 (该方法在单独的 JavaScript 文件中调用。)
我正在使用的 if
语句似乎应该在玩家从任何一侧与其发生碰撞时增加分数(并在新的随机位置生成 gem ),但它仅适用于底端。我做错了什么?
这是包含它的原型(prototype)方法中的 if 语句:
update() {
// Not sure why this if statement only works when player approaches gem from below (a higher y value)
if ((Math.abs(player.x - this.x) < 30) && (Math.abs(player.y - this.y)) < 30) {
// Generates new gem of random color and random x and y value from arrays
this.x = gemX[Math.floor(Math.random() * gemX.length)];
this.y = gemY[Math.floor(Math.random() * gemY.length)];
this.sprite = collectibles[Math.floor(Math.random() * 3)];
score += 5;
sidebarScore.innerHTML = score;
}
}
真人游戏(此代码已上传):https://nataliecardot.com/arcade-game/index.html
包含所有文件的 repo :https://github.com/nataliecardot/nataliecardot.github.io/tree/master/arcade-game
完整的JS文件代码如下:
'use strict'; // Enables strict mode to catch common bloopers
const playAgainButton = document.querySelector('.play-again');
const restartButton = document.querySelector('.restart');
// Calls playAgain() function when user clicks reset icon in sidebar
restartButton.addEventListener('click', playAgain);
// Starts lives at 3
let lives = 3;
// 1 of 5 x (horizontal) values: (start at left) 17, 119, 220, 321, 422;
let gemX = [17, 119, 220, 321, 422];
// Selects random index from gemX. Math.random returns a random number between 0 inclusive to 1 exclusive. So if were to produced 0.55, that would be multiplied by 5 (in this case), which equals 2.75, then Math.floor would round it down to the nearest integer (unless it's already an integer, which can only be 0), which equals 2, resulting in randomGemX equaling the third index, 220.
let randomGemX = gemX[Math.floor(Math.random() * gemX.length)];
// 1 of 3 y (vertical) values (start at top): 124, 208, 292
let gemY = [124, 208, 290];
let randomGemY = gemY[Math.floor(Math.random() * gemY.length)];
// Used to disable arrow keys while modal opened
let isDead = false;
let sidebarLives = document.querySelector('.lives-left');
sidebarLives.innerHTML = lives;
// Sets an initial player score of 0
let score = 0;
// Sets score shown in sidebar
let sidebarScore = document.querySelector('.score');
sidebarScore.innerHTML = score;
let modalScore = document.querySelector('.modal-score');
modalScore.innerHTML = score;
// Called when user clicks restart button in sidebar or play again button in modal. Sets modal to display: none, resets lives and score
function playAgain() {
isDead = false;
// Hides modal if present (if opened by game ending)
modal.classList.remove('modal-visible');
lives = 3;
sidebarLives.innerHTML = lives;
score = 0;
sidebarScore.innerHTML = score;
// Generates new gem of random color and random x and y value from arrays
gem.x = gemX[Math.floor(Math.random() * gemX.length)];
gem.y = gemY[Math.floor(Math.random() * gemY.length)];
gem.sprite = collectibles[Math.floor(Math.random() * 3)];
}
// Note: In a constructor function "this" does not have a value. It is a substitute for the new object. The value of this will become the new object when a new object is created
// Note commas not used to separate methods and properties in a class
class Player {
// Constructor function, a special function just for initializing new objects, will automatically run when a new object is constructed (with keyword "new") from this class. Contains data needed to create it
constructor(x, y, speed) {
this.sprite = 'images/char-boy.png';
this.x = x;
this.y = y;
this.speed = speed;
}
// Methods that all objects created from class will inherit. Would exist on prototype in pre-class way of writing it, but effect is the same (the following methods still exist on Player prototype [for example would be Player.prototype.update = function(dt)...])
// When player reaches water, moves player back to starting position, and increase score by 1
update() {
if (this.y === 25) {
this.x = 200;
this.y = 400;
score++;
sidebarScore.innerHTML = score;
}
}
// Draws player on screen
render() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y)
}
// If isDead is false (so it doesn't work when the modal is opened), connects keyboard input to player movement. If statements prevent player movement off screen
handleInput(allowedKeys) {
if (isDead) {
return;
}
if (allowedKeys === 'down' && this.y < 425) {
this.y += 25;
}
if (allowedKeys === 'up') {
this.y -= 25;
}
if (allowedKeys === 'left' && this.x > 0) {
this.x -= 25;
}
if (allowedKeys === 'right' && this.x < 400) {
this.x += 25;
}
}
}
class Enemy {
// Sets enemy's initial location
constructor(x, y, speed) {
this.x = x;
this.y = y;
// Sets speed of enemy
this.speed = speed;
// The image/sprite for our enemies
this.sprite = 'images/enemy-bug.png';
}
update(dt) {
// Multiplies enemy's movement by time delta to ensure game runs at same speed for all computers
this.x += this.speed * dt;
// Once enemy finished moving across screen, moves it back so it can cross screen again and randomizes its speed
if (this.x > 500) {
this.x = -75;
// Math.random() function returns random number between 0 (inclusive) and 1 (exclusive). Math.floor() returns the largest integer less than or equal to a given number
this.speed = 70 + Math.floor(Math.random() * 450);
}
// When collision occurs, subtracts a life, updates lives displayed in sidebar, and updates score that will be displayed in modal if no lives remaining
if ((player.x < (this.x + 70)) && ((player.x + 17) > this.x) && (player.y < (this.y + 45)) && ((30 + player.y) > this.y)) {
player.x = 200;
player.y = 400;
lives--;
sidebarLives.innerHTML = lives;
modalScore.innerHTML = score;
if (lives === 0) {
isDead = true;
// Calls function that adds class that sets modal to display: block
showModal();
}
}
}
// Draws enemy on screen
render() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
}
};
class Gem {
constructor(x, y) {
// Math.random() function returns random number between 0 (inclusive) and 1 (exclusive). Math.floor() returns the largest integer less than or equal to a given number. Since collectibles is an array, starts at 0, so we want index 0, 1, or 2. (If Math.random were 0.99, it would would become 2.99 after being multiplied by 3, then Math.floor would make it 2)
this.sprite = collectibles[Math.floor(Math.random() * 3)];
this.x = randomGemX;
this.y = randomGemY;
}
// Draws gem on screen
render() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y)
}
update() {
// Not sure why this if statement only works when player approaches gem from below (a higher y value)
if ((Math.abs(player.x - this.x) < 30) && (Math.abs(player.y - this.y)) < 30) {
// Generates new gem of random color and random x and y value from arrays
this.x = gemX[Math.floor(Math.random() * gemX.length)];
this.y = gemY[Math.floor(Math.random() * gemY.length)];
this.sprite = collectibles[Math.floor(Math.random() * 3)];
score += 5;
sidebarScore.innerHTML = score;
}
}
}
let collectibles = [
'images/Gem Blue Sm.png',
'images/Gem Orange Sm.png',
'images/Gem Green Sm.png',
];
// ENEMY/PLAYER/GEM OBJECT INSTANTIATION
let gem = new Gem(randomGemX, randomGemY);
let enemyPosition = [60, 140, 220];
let allEnemies = [];
let player = new Player(200, 400, 50);
enemyPosition.forEach(function(posY) {
// x position of 0, y of whatever is passed in, and random speed
let enemy = new Enemy(0, posY, 70 + Math.floor(Math.random() * 450));
allEnemies.push(enemy);
});
// MODAL
const modal = document.getElementById('myModal');
const closeIcon = document.querySelector('.close');
// When called, adds class that sets modal to display: block when player reaches water
function showModal() {
modal.classList.add('modal-visible');
// Calls playAgain() function when user clicks play again button in modal
playAgainButton.addEventListener('click', playAgain);
// If esc is pressed, closes modal and restarts game (note: keydown used instead of keypress because keypress only works for keys that produce a character value)
document.addEventListener('keydown', function(e) {
let keyCode = e.keyCode;
if (keyCode === 27) {
modal.classList.remove('modal-visible');
playAgain();
}
});
// If enter is pressed, closes modal and restarts game
document.addEventListener('keydown', function(e) {
let keyCode = e.keyCode;
if (keyCode === 13) {
modal.classList.remove('modal-visible');
playAgain()
}
});
// If user clicks modal's close icon, closes modal and restarts game
closeIcon.addEventListener('click', function() {
modal.classList.remove('modal-visible');
playAgain();
});
}
// Listens for keydown event (fired when a key is pressed down [regardless of whether it produces a character, unlike keypress]) and sends the keys to Player.handleInput() method
document.addEventListener('keydown', function(e) {
let allowedKeys = {
37: 'left',
38: 'up',
39: 'right',
40: 'down'
};
// "player" needs to be lowercase because an instance of the class is needed
player.handleInput(allowedKeys[e.keyCode]);
});
最佳答案
我看到您有与 gem 和敌人碰撞的自定义代码...
来自 Gem 类:
if ( ((Math.abs( (player.imgWidth + player.x) - (this.x + this.imgWidth) ) < 55)) && ...
来自敌人类:
if ((player.x < (this.x + 50)) && ((player.x + 17) > this.x) && ...
我强烈建议您将所有这些标准化...
以下是您要比较的两点:
为了将其可视化,我在 GitHub 上 fork 了您的存储库并进行了一些更改以准确查看您使用了哪些点来检测碰撞并且您正在使用 Sprite 的左上角: https://raw.githack.com/heldersepu/nataliecardot.github.io/master/arcade-game/index.html
你必须得到你的元素的中心,从那是一个简单的毕达哥拉斯计算来检测与其他元素的碰撞
关于javascript - 对象碰撞条件仅从一侧工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51373545/
这可能是一个愚蠢的问题,但是要求图中顶点的最小集合的规范问题是什么,以便从这些顶点开始,所有其他顶点都可以通过“旅行”不超过一条边到达? 现实生活中的应用是:我需要认识哪些人,才能与地球上的其他人仅通
当浏览器在伪元素溢出并导致问题后调整绝对定位大小时。我正在寻找解决此问题的方法。只需调整浏览器大小,直到出现标题文本。 这是问题的演示:http://codepen.io/anon/pen/grKNo
我编写的 java 应用程序遇到了导致硬件性能问题的问题。问题(我相当确定)是我运行该应用程序的一些机器只有 1GB 内存。当我启动 java 应用程序时,我将堆大小设置为 -Xms 512m -Xm
Article 与 Medium 具有单向 ManyToOne 关系,它与下面的代码配合良好:保存和删除文章成功。 我想知道 JPA 是否有一种优雅的方式来删除最后一个子实体(在本例中为medium)
我想弄清楚如何在我的预约表格中将医生列表作为radio_buttons。现在,如果我使用“f.input :physician_id, :as => :radio_buttons”,我会得到一个“是/
我是一名优秀的程序员,十分优秀!