首页 > 编程问答 >如何检测吃豆子游戏中的碰撞?

如何检测吃豆子游戏中的碰撞?

时间:2024-07-23 10:33:17浏览次数:13  
标签:javascript python web game-development

以下是我需要完成的 Pacman 游戏的样板。我在检测玩家何时会与墙壁碰撞时遇到问题。问题是我不知道玩家相对于迷宫的位置。就像如果玩家按下按键时从迷宫数组中移动一个图块一样,我可以计算出位置并检查相邻图块是否发生碰撞。但由于玩家在按键时以“1px”增量移动,因此我无法跟踪它当前所在的图块。

我尝试过的:

我创建了两个变量来存储玩家当前的行和列。每当玩家移动时,我都会使用 getBoundingClientRect() 方法检查玩家是否完全位于块内,当玩家完全位于块内时,我会更新行和列跟踪器。但我遇到了瓶颈,因为玩家也可以在两个图块之间移动。

请给我一些建议。

let upPressed = false;
let downPressed = false;
let leftPressed = false;
let rightPressed = false;

const main = document.querySelector("main");

//Player = 2, Wall = 1, Enemy = 3, Point = 0
let maze = [
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 2, 0, 1, 0, 0, 0, 0, 3, 1],
  [1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
  [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
  [1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
  [1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
  [1, 0, 0, 1, 0, 3, 0, 0, 0, 1],
  [1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
  [1, 3, 1, 0, 0, 0, 0, 0, 0, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];

//Populates the maze in the HTML
for (let y of maze) {
  for (let x of y) {
    let block = document.createElement("div");
    block.classList.add("block");

    switch (x) {
      case 1:
        block.classList.add("wall");
        break;
      case 2:
        block.id = "player";
        let mouth = document.createElement("div");
        mouth.classList.add("mouth");
        block.appendChild(mouth);
        break;
      case 3:
        block.classList.add("enemy");
        break;
      default:
        block.classList.add("point");
        block.style.height = "1vh";
        block.style.width = "1vh";
    }

    main.appendChild(block);
  }
}

//Player movement

const lbutton = document.querySelector("#lbttn");
const rbutton = document.querySelector("#rbttn");
const ubutton = document.querySelector("#ubttn");
const dbutton = document.querySelector("#dbttn");

function keyUp(event) {
  if (event.key === "ArrowUp") {
    upPressed = false;
    ubutton.style.borderStyle = "outset";
  } else if (event.key === "ArrowDown") {
    downPressed = false;
    dbutton.style.borderStyle = "outset";
  } else if (event.key === "ArrowLeft") {
    leftPressed = false;
    lbutton.style.borderStyle = "outset";
  } else if (event.key === "ArrowRight") {
    rightPressed = false;
    rbutton.style.borderStyle = "outset";
  }
}

function keyDown(event) {
  if (event.key === "ArrowUp") {
    upPressed = true;
    ubutton.style.borderStyle = "inset";
  } else if (event.key === "ArrowDown") {
    downPressed = true;
    dbutton.style.borderStyle = "inset";
  } else if (event.key === "ArrowLeft") {
    leftPressed = true;
    lbutton.style.borderStyle = "inset";
  } else if (event.key === "ArrowRight") {
    rightPressed = true;
    rbutton.style.borderStyle = "inset";
  }
}

const player = document.querySelector("#player");
const playerMouth = player.querySelector(".mouth");
let playerTop = 0;
let playerLeft = 0;

setInterval(function () {
  if (downPressed) {
    playerTop++;
    player.style.top = playerTop + "px";
    playerMouth.classList = "down";
  } else if (upPressed) {
    playerTop--;
    player.style.top = playerTop + "px";
    playerMouth.classList = "up";
  } else if (leftPressed) {
    playerLeft--;
    player.style.left = playerLeft + "px";
    playerMouth.classList = "left";
  } else if (rightPressed) {
    playerLeft++;
    player.style.left = playerLeft + "px";
    playerMouth.classList = "right";
  }
}, 10);

function startGame() {
  document.addEventListener("keydown", keyDown);
  document.addEventListener("keyup", keyUp);
  startBtn.style.display = "none";
}

const startBtn = document.querySelector(".start");
startBtn.addEventListener("click", startGame);

你遇到的问题很常见于用更细粒度的移动来处理基于网格的碰撞。你绝对走对了路,用网格的行和列来跟踪玩家的位置!问题在于你只在玩家完全位于一个图块内时才更新行和列,而这个条件不一定成立。

以下是如何改进吃豆人碰撞检测的方法:

  1. 根据玩家的速度/大小计算“下一个”位置: 与其直接在每个帧更新玩家位置,不如先计算玩家 将要 移动到的位置。
  2. 使用“下一个”位置检查与网格的碰撞:
  3. 根据玩家的“下一个”位置,确定它将占据哪些网格图块(由于玩家的大小,可能不止一个)。
  4. 检查这些图块中是否有任何图块代表墙壁。
  5. 如果发生碰撞,则停止移动: 如果玩家的“下一个”位置会导致与墙壁碰撞,则不要更新玩家的位置。否则,允许玩家移动到计算出的“下一个”位置。

应用于你的代码示例:

const playerSize = 20; // 假设玩家大小为 20px x 20px
const gridSize = 20; // 假设每个网格图块大小为 20px x 20px

setInterval(function () {
  let nextPlayerTop = playerTop;
  let nextPlayerLeft = playerLeft;

  if (downPressed) {
    nextPlayerTop++;
  } else if (upPressed) {
    nextPlayerTop--;
  } else if (leftPressed) {
    nextPlayerLeft--;
  } else if (rightPressed) {
    nextPlayerLeft++;
  }

  // 计算玩家将占据的网格图块
  let nextGridRow = Math.floor(nextPlayerTop / gridSize);
  let nextGridCol = Math.floor(nextPlayerLeft / gridSize);

  // 边界检查:确保网格行和列在迷宫数组的范围内
  if (
    nextGridRow >= 0 &&
    nextGridRow < maze.length &&
    nextGridCol >= 0 &&
    nextGridCol < maze[0].length
  ) {
    // 检查与墙壁的碰撞
    if (maze[nextGridRow][nextGridCol] !== 1) {
      // 如果没有碰撞,则更新玩家位置
      playerTop = nextPlayerTop;
      playerLeft = nextPlayerLeft;
      player.style.top = playerTop + "px";
      player.style.left = playerLeft + "px";
    }
  }

  // ...更新玩家嘴巴方向的代码...
}, 10);

在这个改进后的代码中,我们首先计算玩家的“下一个”位置。然后,我们使用这个“下一个”位置来确定玩家将占据哪些网格图块。最后,我们只在确认“下一个”位置没有导致与墙壁碰撞的情况下更新玩家的位置。

这应该可以解决你遇到的碰撞检测问题,并提供更健壮的吃豆人移动体验!

标签:javascript,python,web,game-development
From: 78781345

相关文章

  • 类型错误:无法将函数返回值转换为 Python 类型!签名是 () -> 处理 anaconda spider
    这是代码:importosimportrandomimportnumpyasnpimportpandasaspdimporttensorflowastffromtensorflow.kerasimportbackendasKfromtensorflow.keras.layersimportDense,Dropout,Flatten,Conv2D,MaxPool2D,Input......
  • python进阶---闭包与装饰器
    一、闭包        在Python中,闭包是指一个函数内部定义的函数,这个内部函数可以访问并修改其外部函数的局部变量,即使外部函数已经执行完毕。闭包可以通过多层函数嵌套来实现。    闭包的三要素:    1、外部函数嵌套内部函数    2、外部函数返......
  • 强制从当前包自动导入的 Python 以此包的名称为前缀
    我在VSCode中使用Python和Pylance扩展。在我正在编辑的自己的包中,自动添加的导入(设置“导入格式:绝对”)如下所示:frommydirectory.myfileimportmyclass但是,我的Python包正在被被一个(非常愚蠢且不可协商的)外部系统消耗,该系统拒绝正确解释它,除非导入的格式特别......
  • Python语言-面向对象
    知识代码classJobSalary(object):job=''def__init__(self,city):self.jobname="数据分析师"self.exp=''self.city=city#方法defdata_normalize(self,data):print(f'正在规范化......
  • 需要帮助使用 Selenium Python 单击 Microsoft Teams 按钮
    我将Python与Selenium结合使用,并自动登录MicrosoftTeams。进入后,弹出窗口显示我需要单击“立即切换”以切换到V2版本。我似乎无法使用SeleniumPython成功单击此按钮。谁能帮我自动点击这个按钮?这是我不成功的尝试:self.driver.find_element(By.CLASS_NAME,......
  • python接口自动化(四十)- logger 日志 - 下(超详解)
    宏哥微信粉丝群:https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介按照上一篇的计划,这一篇给小伙伴们讲解一下:(1)多模块使用logging,(2)通过文件配置logging模块,(3)自己封装一个日志(logging)类。可能有的小伙伴在这里会有个疑问一个logging为什么分两篇的篇幅......
  • Python面试题:使用NumPy进行高效数组运算
    NumPy是Python中进行高效数组运算的基础库。以下是一些示例,展示了如何使用NumPy进行高效的数组运算,包括创建数组、数组操作、数学运算以及一些高级操作。安装NumPy如果你还没有安装NumPy,可以通过以下命令进行安装:pipinstallnumpy示例代码1.创建数组import......
  • Python面试题:使用Matplotlib和Seaborn进行数据可视化
    使用Matplotlib和Seaborn进行数据可视化是数据分析中非常重要的一部分。以下示例展示了如何使用这两个库来创建各种图表,包括基本的线图、柱状图、散点图和高级的分类数据可视化图表。安装Matplotlib和Seaborn如果你还没有安装这两个库,可以使用以下命令进行安装:pipins......
  • 20、Python之容器:红楼主角都有谁?10行代码生成《红楼梦》词云图
    引言Python系列前面的文章中,我们介绍了Python中容器的基本使用,上一篇中,我们又重点介绍了Counter计数器的使用。这些介绍,应该足以应付日常的工作需求了。在今天的文章中,我想以词云图的生成这个综合案例,巩固一下前面关于容器、字典推导式、Counter的使用。同时,介绍两个比较好......
  • 使用snowflake.connector 3.0.3上的密钥对从python连接到snowflake
    我正在尝试使用Snowflake.connector包从我的Python代码连接到Snowflake。不幸的是,由于遗留代码,我只能在python3.7.3上使用Snowflake连接器版本3.0.3,并且无法升级我确实设法从我自己的计算机进行连接,使用:con=Snowflake.connector。连接(...私有密钥文件=......