英文:
How can I have collision detection between array and object so they cannot pass through each other?
问题
以下是代码的相关部分的翻译:
function collisionWall(obj, array) {
for (var i = 0; i < array.length; i++) {
if (obj != array[i]) {
collisionIntWall(obj, array[i]);
}
}
}
function collisionIntWall(obj, array) {
if (obj.x + obj.r > array.x - array.w / 2 && move.keyCode == 37) {
obj.speed = 0;
}
}
请继续阅读代码,如果您有其他问题或需要进一步的帮助,请告诉我。
英文:
I am new to coding, and especially new to trying to make a game. This is for a class project. Using JavaScript, HTML, and CSS, I am trying to make a game in which, with limited visibility, the user must search for the exit (a white rectangle on the edges of the screen) while avoiding ghosts and interior walls (for the sake of ease of use, the canvas is completely visible so you can see what I am referring to). I am having trouble creating collision detection between the randomly generated interior walls and the player so that the player is unable to pass through these walls.
To be honest I am quite new to all of this, so I do not have a lot of trial and error experience since it is built off of guesses. I was trying to use if statements to detect collision before moving and repeat it for each side (see javascript lines of code 174-199). So essentially using an if statement for each side of the array shape, and if the circle is moving in the direction of collision, they won't be able to move. This hasn't worked though obviously, and I am feeling quite lost as to the correct step to accomplish the desired effect. My code is posted here:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
var canvas;
var ctx;
var w = window.innerWidth;
var h = window.innerHeight;
var startScreen = document.querySelector("#startScreen");
var allGhosts = []
var allWalls = []
var oneDegree = 2 * Math.PI / 360;
var generate = true;
var win = false;
var lose = false;
var o1 = {
x: w / 2,
y: h / 2,
w: w * 2,
h: h * 2,
r: w / 6,
c: 200,
a: 1,
distance: 4,
angle: rand(360),
changeX: randn(5),
changeY: randn(5),
}
var o2 = {
x: rand(w),
y: rand(h),
w: rand(50) + 10,
h: rand(50) + 10,
r: w / 6,
c: 200,
a: 0.75,
distance: rand(10),
angle: rand(360),
}
var o3 = {
x: w / 2,
y: h / 2,
r: 10,
a: 1,
speed: 7,
}
var o4 = {
x: w / 2,
y: h / 2,
w: w * 2,
h: h * 2,
r: w / 1.90,
c: 200,
a: 1,
}
var exitLoc = {
x: parseInt(Math.random() * 2) ? 0 : 1500,
y: rand(680),
w: 10,
h: 50,
}
if (win != true) {
winScreen.style.display = 'none';
}
if (lose != true) {
loseScreen.style.display = 'none';
}
window.addEventListener("keydown", ev => {
if (ev.keyCode === 32 && generate === true) {
startScreen.style.display = 'none';
generate = false;
createGhosts(30, allGhosts);
createGhosts(50, allWalls);
setUpCanvas();
animationLoop();
document.onkeydown = move;
function animationLoop() {
clear();
extWalls();
collisionStop(o3);
collisionStop(o4);
exit(exitLoc);
for (var i = 0; i < allGhosts.length; i++) {
ghosts(allGhosts[i]);
bounce(allGhosts[i]);
forward(allGhosts[i]);
updateData(o1);
}
for (var i = 0; i < allWalls.length; i++) {
walls(allWalls[i]);
}
// circle(o4);
player(o3);
collisionWin(exitLoc, o3);
collisionLose(o3, allGhosts);
if (lose != true && win != true) {
requestAnimationFrame(animationLoop);
}
}
}
})
function collisionWin(obj1, obj2) {
var differencex = Math.abs(obj1.x - obj2.x);
var differencey = Math.abs(obj1.y - obj2.y);
var hdif = Math.sqrt((differencex * differencex) + (differencey * differencey));
if (hdif < obj1.w + obj2.r) {
clear();
rect();
win = true;
}
if (win === true) {
winScreen.style.display = 'block';
loseScreen.style.display = 'none'
}
}
function rect() {
x = 0;
y = 0;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + w, y);
ctx.lineTo(x + w, y + h);
ctx.lineTo(x, y + h);
ctx.closePath();
ctx.fillStyle = "black";
ctx.fill();
}
function collisionLose(obj, array) {
for (var i = 0; i < array.length; i++) {
if (obj != array[i]) {
collisionGhost(obj, array[i]);
}
}
}
function collisionGhost(obj, array) {
var differencex = Math.abs(obj.x - array.x);
var differencey = Math.abs(obj.y - array.y);
var hd = Math.sqrt((differencex * differencex) + (differencey * differencey));
if (hd < obj.r + array.r) {
clear();
rect();
lose = true;
}
if (lose === true) {
loseScreen.style.display = 'block'
}
}
function collisionStop(o) {
if (o.x > w - 50) {
o.x = w - 50;
}
// right of screen
if (o.x < 15) {
o.x = 15;
}
// left of screen
if (o.y > h - 50) {
o.y = h - 50;
}
// bottom of screen
if (o.y < 15) {
o.y = 15;
}
// top of screen
}
function collisionWall(obj, array) {
for (var i = 0; i < array.length; i++) {
if (obj != array[i]) {
collisionIntWall(obj, array[i]);
}
}
}
function collisionIntWall(obj, array) {
if (obj.x + obj.r > array.x - array.w / 2 && move.keyCode == 37) {
obj.speed = 0
}
// right side of obj1 > left side of array AND
// if(obj.x - obj.r < array.x + array.w/2 && move() == 39){
// obj.speed = 0
// }
// // left side of obj1 < right side of array AND
// if(obj.y + obj.r > array.y - array.h/2 && move() == 40){
// obj.speed = 0
// }
// // bottom of obj1 > top of array AND
// if(obj.y - obj.r < array.y + array.h/2 && move() == 38){
// obj.speed = 0
// }
// top of obj1 < bottom of array
}
function createGhosts(num, array) {
for (var i = 0; i < num; i++) {
array.push({
x: rand(w),
y: rand(h),
w: rand(50) + 10,
h: rand(50) + 10,
r: 10,
c: 0,
a: 1,
distance: rand(1) + 0.5,
angle: rand(360),
})
}
}
function bounce(o) {
if (o.x > w) {
o.x = w - 5;
o.angle += 180 - 2 * o.angle;
}
if (o.x < 0) {
o.x = 5;
o.angle += 180 - 2 * o.angle;
}
if (o.y > h) {
o.y = h - 5;
o.angle += 360 - 2 * o.angle;
}
if (o.y < 0) {
o.y = 5;
o.angle += 360 - 2 * o.angle;
}
}
function updateData(o) {
o.x += o.changeX;
o.y += o.changeY;
}
function forward(o) {
var cx;
var cy;
cx = o.distance * Math.cos(o.angle * oneDegree);
cy = o.distance * Math.sin(o.angle * oneDegree);
o.x += cx;
o.y += cy;
}
function circle(o) {
ctx.beginPath();
ctx.arc(o.x, o.y, o.r, 0, 2 * Math.PI);
ctx.strokeStyle = "black";
ctx.lineWidth = 2000;
ctx.stroke();
}
function player(o) {
ctx.beginPath();
ctx.arc(o.x, o.y, o.r, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}
function ghosts(o) {
ctx.beginPath();
ctx.arc(o.x, o.y, o.r, 0, 2 * Math.PI);
ctx.fillStyle = "rgb(189, 9, 9)";
ctx.fill();
}
function walls(o) {
o.x = o.x - o.w / 2;
o.y = o.y - o.h / 2;
ctx.beginPath();
ctx.moveTo(o.x, o.y);
ctx.lineTo(o.x + o.w, o.y);
ctx.lineTo(o.x + o.w, o.y + o.h);
ctx.lineTo(o.x, o.y + o.h);
ctx.closePath();
ctx.fillStyle = "grey";
ctx.fill();
o.x = o.x + o.w / 2;
o.y = o.y + o.h / 2;
}
function exit(o) {
o.x = o.x - o.w / 2;
o.y = o.y - o.h / 2;
ctx.beginPath();
ctx.moveTo(o.x, o.y);
ctx.lineTo(o.x + o.w, o.y);
ctx.lineTo(o.x + o.w, o.y + o.h);
ctx.lineTo(o.x, o.y + o.h);
ctx.closePath();
ctx.fillStyle = "white";
ctx.fill();
o.x = o.x + o.w / 2;
o.y = o.y + o.h / 2;
}
function extWalls() {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, 680);
ctx.lineTo(1500, 680);
ctx.lineTo(1500, 0);
ctx.closePath();
ctx.strokeStyle = "grey";
ctx.lineWidth = 10;
ctx.stroke();
}
function clear() {
ctx.clearRect(0, 0, w, h);
}
function randn(r) {
var result = Math.random() * r - r / 2;
return result;
}
function randi(r) {
var result = Math.floor(Math.random() * r);
return result;
}
function rand(r) {
var result = Math.random() * r;
return result;
}
function setUpCanvas() {
canvas = document.querySelector("#myCanvas");
ctx = canvas.getContext("2d");
canvas.width = 1500;
canvas.height = 680;
}
function move(e) {
if (e.keyCode == 37) {
o3.x -= o3.speed;
o4.x -= o3.speed;
}
if (e.keyCode == 39) {
o3.x += o3.speed;
o4.x += o3.speed;
}
if (e.keyCode == 40) {
o3.y += o3.speed;
o4.y += o3.speed;
}
if (e.keyCode == 38) {
o3.y -= o3.speed;
o4.y -= o3.speed;
}
}
<!-- language: lang-css -->
#myCanvas {
background-color: rgb(76, 76, 76);
}
h1 {
color: rgb(189, 9, 9);
text-align: center;
font-family: Creepster, monospace;
}
h6 {
color: rgb(189, 9, 9);
text-align: center;
font-family: Creepster, monospace;
}
p {
color: rgb(189, 9, 9);
text-align: center;
font-family: Creepster, monospace;
}
body {
display: flex;
justify-content: center;
background-color: black;
}
#startBox {
position: fixed;
width: 600px;
height: 600px;
box-shadow: 0 1px 64px rgb(189, 9, 9);
background-color: black;
left: 50%;
transform: translateX(-50%);
}
#startBox h1 {
font-size: 100px;
}
#startBox p {
width: 90%;
margin: auto;
font-size: 25px;
}
#winBox {
position: fixed;
width: 600px;
height: 600px;
box-shadow: 0 1px 64px rgb(189, 9, 9);
background-color: black;
left: 50%;
transform: translateX(-50%);
}
#winBox h1 {
font-size: 85px;
}
#winBox p {
width: 90%;
margin: auto;
font-size: 25px;
}
#loseBox {
position: fixed;
width: 600px;
height: 600px;
box-shadow: 0 1px 64px rgb(189, 9, 9);
background-color: black;
left: 50%;
transform: translateX(-50%);
}
#loseBox h1 {
font-size: 100px;
}
#loseBox p {
width: 90%;
margin: auto;
font-size: 25px;
}
<!-- language: lang-html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ghost house</title>
<link rel="stylesheet" type="text/css" href="test.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Creepster&display=swap" rel="stylesheet">
</head>
<body>
<div id="startScreen">
<div id="startBox">
<h1>ghost house</h1>
<p>The lights went out… <br> Your flashlight can only last so long. <br> Find your way out of the haunted house but beware, ghosts haunt these halls. <br><br> They do not take kindly to visitors... <br><br> use the arrow keys to control the player
and search for a way out. the exit looks like a white rectangle somewhere along the exterior walls of the home. press the spacebar to start</p>
</div>
</div>
<div id="winScreen">
<div id="winBox">
<h1>congratulations</h1>
<p>you found your way out of the ghost house <br> refresh the page and try to escape again <br><br><br><br> if you dare</p>
</div>
</div>
<div id="loseScreen">
<div id="loseBox">
<h1>game over</h1>
<p>looks like there will be another ghost haunting this house <br> refresh the page and try to escape again <br><br><br><br> if you dare</p>
</div>
</div>
<div id="canvasContainer">
<canvas id="myCanvas"></canvas>
</div>
<script src="test.js"></script>
</body>
</html>
<!-- end snippet -->
The issue code in question starts from lines 174-199. Thank you for taking the time to read through this and help me out! I really appreciate it
答案1
得分: 1
我认为你的方向是正确的。但是在你的动画循环中似乎从未调用collisionWall
,所以你的逻辑从未运行。此外,你的逻辑引用了move.keyCode
,但move
是一个函数,你从未在其上设置过keyCode属性。你的逻辑应该更像是:
function collisionIntWall(obj, array) {
const left = obj.x - obj.r
const right = obj.x + obj.r
const top = obj.y - obj.r
const bottom = obj.y + obj.r
const wleft = array.x - array.w / 2
const wright = array.x + array.w / 2
const wtop = array.y - array.h / 2
const wbottom = array.y + array.h / 2
// 检查玩家侧面是否与墙壁侧面重叠
const isHorizontalHit = (left < wright) && (right > wleft)
// 检查玩家顶部/底部是否与墙壁顶部/底部重叠
const isVerticalHit = (top < wtop) && (bottom > wbottom)
// 如果两者都重叠,则发生碰撞
if (isHorizontalHit && isVerticalHit) {
obj.speed = 0
}
}
另外,你可能会发现这种碰撞测试会导致玩家部分进入墙壁。当发生碰撞时,你可能希望将对象移回其先前位置,然后将速度设置为0。
我建议在动画循环中渲染之前进行所有的移动/碰撞测试逻辑。结构应该类似于:
function animationLoop() {
// 首先处理游戏逻辑
moveGhosts()
movePlayer()
checkForOutOfBounds()
checkForWallCollisions() // 如果玩家撞墙,将其移回先前位置
checkIfPlayerReachedExit()
checkIfGhostHitPlayer()
// 渲染逻辑
clear()
drawEdges()
drawExit()
drawWalls()
drawGhosts()
drawPlayer()
}
这样,你可以在逻辑中移动玩家,将其移回,等等,然后渲染最终结果。
英文:
I think you are on the right path. But it does not look like you ever call collisionWall
in your animation loop so your logic never runs. Also your logic is referring to move.keyCode
, but move is a function and you never set a keyCode property on it. Your logic should look more like:
function collisionIntWall(obj, array) {
const left = obj.x - obj.r
const right = obj.x + obj.r
const top = obj.y - obj.r
const bottom = obj.y + obj.r
const wleft = array.x - array.w / 2
const wright = array.x + array.w / 2
const wtop = array.y - array.h / 2
const wbottom = array.y + array.h / 2
// check if player sides overlap wall sides
const isHorizontalHit = (left < wright) && (right > wleft)
// check if player top/bottom overlap wall top/bottom
const isVerticalHit = (top < wtop) && (bottom > wbottom)
// if both overlap then it is a collision
if (isHorizontalHit && isVerticalHit) {
obj.speed = 0
}
}
PS you will likely discover this collision test is leaving the player partially inside the wall. when you collide, you likely want to move the object to its previous position and then set speed to 0.
And I'd suggest doing all of the move/collision test logic in before rendering in your animation loop. Structure it something like:
function animationLoop() {
// game logic first
moveGhosts()
movePlayer()
checkForOutOfBounds()
checkForWallCollisions() // move player back to previous position if they hit a wall
checkIfPlayerReachedExit()
checkIfGhostHitPlayer()
// render logic
clear()
drawEdges()
drawExit()
drawWalls()
drawGhosts()
drawPlayer()
}
this way, you can move the player, move them back, etc in your logic and then render the final result.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论