如何使数组和对象之间发生碰撞检测,以防它们相互穿过?

huangapple go评论68阅读模式
英文:

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(&quot;#startScreen&quot;);
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 = &#39;none&#39;;
}
if (lose != true) {
  loseScreen.style.display = &#39;none&#39;;
}
window.addEventListener(&quot;keydown&quot;, ev =&gt; {
  if (ev.keyCode === 32 &amp;&amp; generate === true) {
    startScreen.style.display = &#39;none&#39;;

    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 &lt; allGhosts.length; i++) {
        ghosts(allGhosts[i]);
        bounce(allGhosts[i]);
        forward(allGhosts[i]);
        updateData(o1);
      }
      for (var i = 0; i &lt; allWalls.length; i++) {
        walls(allWalls[i]);
      }
      // circle(o4);
      player(o3);
      collisionWin(exitLoc, o3);
      collisionLose(o3, allGhosts);
      if (lose != true &amp;&amp; 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 &lt; obj1.w + obj2.r) {
    clear();
    rect();
    win = true;
  }
  if (win === true) {
    winScreen.style.display = &#39;block&#39;;
    loseScreen.style.display = &#39;none&#39;
  }
}

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 = &quot;black&quot;;
  ctx.fill();
}

function collisionLose(obj, array) {
  for (var i = 0; i &lt; 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 &lt; obj.r + array.r) {
    clear();
    rect();
    lose = true;
  }
  if (lose === true) {
    loseScreen.style.display = &#39;block&#39;
  }
}

function collisionStop(o) {
  if (o.x &gt; w - 50) {
    o.x = w - 50;
  }
  // right of screen
  if (o.x &lt; 15) {
    o.x = 15;
  }
  // left of screen
  if (o.y &gt; h - 50) {
    o.y = h - 50;
  }
  // bottom of screen
  if (o.y &lt; 15) {
    o.y = 15;
  }
  // top of screen
}

function collisionWall(obj, array) {
  for (var i = 0; i &lt; array.length; i++) {
    if (obj != array[i]) {
      collisionIntWall(obj, array[i]);
    }
  }
}

function collisionIntWall(obj, array) {
  if (obj.x + obj.r &gt; array.x - array.w / 2 &amp;&amp; move.keyCode == 37) {
    obj.speed = 0
  }
  // right side of obj1 &gt; left side of array AND
  // if(obj.x - obj.r    &lt; array.x + array.w/2 &amp;&amp; move() == 39){
  //     obj.speed = 0
  // }   
  // // left side of obj1 &lt; right side of array AND
  // if(obj.y + obj.r    &gt; array.y - array.h/2 &amp;&amp; move() == 40){
  //     obj.speed = 0
  // }   
  // // bottom of obj1 &gt; top of array AND
  // if(obj.y - obj.r    &lt; array.y + array.h/2 &amp;&amp; move() == 38){
  //     obj.speed = 0
  // }      
  // top of obj1 &lt; bottom of array
}

function createGhosts(num, array) {
  for (var i = 0; i &lt; 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 &gt; w) {
    o.x = w - 5;
    o.angle += 180 - 2 * o.angle;
  }
  if (o.x &lt; 0) {
    o.x = 5;
    o.angle += 180 - 2 * o.angle;
  }
  if (o.y &gt; h) {
    o.y = h - 5;
    o.angle += 360 - 2 * o.angle;
  }
  if (o.y &lt; 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 = &quot;black&quot;;
  ctx.lineWidth = 2000;
  ctx.stroke();
}

function player(o) {
  ctx.beginPath();
  ctx.arc(o.x, o.y, o.r, 0, 2 * Math.PI);
  ctx.fillStyle = &quot;white&quot;;
  ctx.fill();
}

function ghosts(o) {
  ctx.beginPath();
  ctx.arc(o.x, o.y, o.r, 0, 2 * Math.PI);
  ctx.fillStyle = &quot;rgb(189, 9, 9)&quot;;
  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 = &quot;grey&quot;;
  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 = &quot;white&quot;;
  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 = &quot;grey&quot;;
  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(&quot;#myCanvas&quot;);
  ctx = canvas.getContext(&quot;2d&quot;);
  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 -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot;&gt;
  &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
  &lt;title&gt;ghost house&lt;/title&gt;
  &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;test.css&quot;&gt;
  &lt;link rel=&quot;preconnect&quot; href=&quot;https://fonts.googleapis.com&quot;&gt;
  &lt;link rel=&quot;preconnect&quot; href=&quot;https://fonts.gstatic.com&quot; crossorigin&gt;
  &lt;link href=&quot;https://fonts.googleapis.com/css2?family=Creepster&amp;display=swap&quot; rel=&quot;stylesheet&quot;&gt;

&lt;/head&gt;

&lt;body&gt;
  &lt;div id=&quot;startScreen&quot;&gt;
    &lt;div id=&quot;startBox&quot;&gt;
      &lt;h1&gt;ghost house&lt;/h1&gt;
      &lt;p&gt;The lights went out… &lt;br&gt; Your flashlight can only last so long. &lt;br&gt; Find your way out of the haunted house but beware, ghosts haunt these halls. &lt;br&gt;&lt;br&gt; They do not take kindly to visitors... &lt;br&gt;&lt;br&gt; 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&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;


  &lt;div id=&quot;winScreen&quot;&gt;
    &lt;div id=&quot;winBox&quot;&gt;
      &lt;h1&gt;congratulations&lt;/h1&gt;
      &lt;p&gt;you found your way out of the ghost house &lt;br&gt; refresh the page and try to escape again &lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt; if you dare&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;


  &lt;div id=&quot;loseScreen&quot;&gt;
    &lt;div id=&quot;loseBox&quot;&gt;
      &lt;h1&gt;game over&lt;/h1&gt;
      &lt;p&gt;looks like there will be another ghost haunting this house &lt;br&gt; refresh the page and try to escape again &lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt; if you dare&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;


  &lt;div id=&quot;canvasContainer&quot;&gt;
    &lt;canvas id=&quot;myCanvas&quot;&gt;&lt;/canvas&gt;
  &lt;/div&gt;



  &lt;script src=&quot;test.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;

<!-- 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 &lt; wright) &amp;&amp; (right &gt; wleft)
  // check if player top/bottom overlap wall top/bottom
  const isVerticalHit = (top &lt; wtop) &amp;&amp; (bottom &gt; wbottom)

  // if both overlap then it is a collision
  if (isHorizontalHit &amp;&amp; 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.

huangapple
  • 本文由 发表于 2023年4月11日 09:04:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75981736.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定