讲师中心 微信公众号
AI工具推荐 视频效率加速

p5.js 中多对象碰撞检测的策略与实践

大磊大大_4943

大磊大大_4943

发布时间:2025-08-04 17:24:15

|

633人浏览过

|

来源于php中文网

原创

p5.js 中多对象碰撞检测的策略与实践

本文深入探讨了在p5.js游戏开发中使用p5.collide2d库时,当存在多个同类型对象(如多个球和多个挡板)时,如何实现正确的全方位碰撞检测。核心问题在于原始设计将不同游戏实体的状态混淆在一个类中,导致碰撞检测仅限于“一对一”关系。解决方案是采用清晰的面向对象设计,将不同实体分离为独立的类,并通过嵌套循环实现“多对多”的碰撞检测,确保所有对象间的交互都能被正确处理,从而提升游戏逻辑的健壮性和可扩展性。

1. 问题背景:多对象碰撞检测的挑战

在开发基于p5.js的Pong类游戏时,当游戏引入多于一套的“球-挡板”组合(例如,达到一定分数后增加额外的球和挡板)时,开发者可能会遇到一个常见的问题:碰撞检测不再按预期工作。具体表现为,初始的球只能与初始的挡板发生碰撞,而新增的球或挡板无法与原有的对象进行碰撞,反之亦然。

究其原因,这通常不是p5.collide2d库本身的问题,而是游戏对象设计和碰撞检测逻辑实现方式的问题。原始代码中,一个名为Thing的类被用来同时封装了玩家挡板、对手挡板和球的状态及行为。在Thing类的collide()方法中,碰撞检测仅针对该Thing实例所“拥有”的挡板和球进行,即:

// 原始代码中的碰撞检测片段
hit = collideRectRect(30, this.y, 1, this.f, this.w, this.h, 10, 10);

这里的this.y代表当前Thing实例的玩家挡板Y坐标,this.w和this.h代表当前Thing实例的球坐标。这意味着每个Thing实例只负责检查“自己的”挡板与“自己的”球之间的碰撞。当游戏中存在多个Thing实例时,它们各自独立地进行检测,导致了不同实例所代表的球和挡板之间无法互相检测到碰撞。

2. 核心问题分析:混合实体与局部检测

问题的根源在于对游戏实体的抽象不够清晰。一个Thing实例不应该同时代表一个玩家挡板、一个对手挡板和一个球。这种混合的设计模式(也称为“上帝对象”或“多重职责”)导致了以下问题:

  • 职责混淆: Thing类承担了过多的职责,包括玩家输入处理、AI移动、球的物理行为以及碰撞检测,使得代码难以理解和维护。
  • 状态耦合: 玩家挡板、对手挡板和球的状态紧密耦合在一个Thing实例中,使得独立管理和交互变得复杂。
  • 局部碰撞检测: collide()方法仅基于当前实例的内部状态进行检测,无法实现全局的、所有对象之间的碰撞检测。例如,当有ThingA和ThingB两个实例时,ThingA.collide()只检测ThingA的挡板和ThingA的球,而ThingB.collide()只检测ThingB的挡板和ThingB的球,它们之间没有交叉检测。

为了解决这个问题,我们需要重新设计游戏实体的结构,并调整碰撞检测的逻辑。

3. 解决方案:分离实体与全局碰撞检测

解决多对象碰撞检测问题的关键在于以下两点:

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载
  1. 分离游戏实体: 将不同类型的游戏对象(如挡板和球)定义为独立的类,每个类只负责管理自身的状态和行为。
  2. 实现全局碰撞检测: 在主游戏循环中,通过嵌套循环遍历所有可能的碰撞对,进行全面的碰撞检测。

3.1 重新设计游戏实体类

我们将原始的Thing类拆分为Paddle(挡板)类和Ball(球)类。

Paddle 类:

负责挡板的位置、尺寸、颜色和移动逻辑。可以添加一个isPlayer属性来区分玩家挡板和对手挡板。

class Paddle {
  constructor(x, y, w, h, isPlayer = false) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.isPlayer = isPlayer; // 标识是否为玩家挡板
    this.speed = 5; // 挡板移动速度
  }

  show() {
    noStroke();
    fill(255, 229, 236); // 挡板颜色
    rect(this.x, this.y, this.w, this.h);
  }

  move(ballY = null) { // ballY 用于对手AI的跟踪
    if (this.isPlayer) {
      // 玩家挡板通过键盘控制
      if (keyIsDown(DOWN_ARROW)) this.y += this.speed;
      if (keyIsDown(UP_ARROW)) this.y -= this.speed;
      this.y = constrain(this.y, 0, height - this.h); // 限制在画布内
    } else {
      // 对手挡板(AI)跟随球移动
      if (ballY !== null) {
        this.y = ballY - this.h / 2; // 简单AI:挡板中心对准球
        this.y = constrain(this.y, 0, height - this.h); // 限制在画布内
      }
    }
  }
}

Ball 类:

负责球的位置、尺寸、颜色、速度和移动逻辑。

class Ball {
  constructor(x, y, size, color) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.color = color;
    this.speedX = 5;
    this.speedY = 5;
  }

  show() {
    noStroke();
    fill(this.color); // 球的颜色
    rect(this.x, this.y, this.size, this.size);
  }

  move() {
    this.x += this.speedX;
    this.y += this.speedY;

    // 球与上下墙壁的碰撞反弹
    if (this.y < 0 || this.y > height - this.size) {
      this.speedY *= -1;
    }
  }

  // 检查球是否出界(未被挡住)
  isOutOfBounds() {
    return this.x < -this.size || this.x > width + this.size;
  }

  // 重置球的位置和速度
  reset() {
    this.x = width / 2;
    this.y = height / 2;
    this.speedX = 5;
    this.speedY = 5;
  }
}

3.2 实现全局碰撞检测逻辑

在setup()函数中,我们将创建独立的Paddle和Ball实例,并分别存储在paddles和balls数组中。在draw()函数中,我们将遍历这些数组,实现所有球与所有挡板之间的碰撞检测。

let paddles = []; // 存储所有挡板实例
let balls = [];   // 存储所有球实例
let s = 0;        // 游戏分数
let b = 0;        // 最高分数
let mode;         // 游戏模式
const STARTSCREEN = 1;
const GAME = 2;

function setup() {
  createCanvas(600, 600);
  rectMode(CORNER); // 设置矩形绘制模式为左上角

  // 创建玩家挡板 (x=20, y=居中, 宽10, 高60)
  paddles.push(new Paddle(20, height / 2 - 30, 10, 60, true));
  // 创建对手挡板 (x=width-30, y=居中, 宽10, 高60)
  paddles.push(new Paddle(width - 30, height / 2 - 30, 10, 60, false));

  // 创建初始球 (白色)
  balls.push(new Ball(width / 2, height / 2, 10, 'white'));

  mode = STARTSCREEN; // 初始游戏模式
}

function draw() {
  if (mode === STARTSCREEN) {
    startScreen();
  } else if (mode === GAME) {
    game();
  }
}

// 游戏开始画面(省略具体实现,与原代码类似)
function startScreen() {
  background(255, 194, 209);
  textAlign(CENTER);
  textSize(33);
  fill(255, 179, 198);
  text("CLICK TO START", width / 2, height / 2 + 10);
  fill(255, 229, 236);
  text("HIGH SCORE: " + b, width / 2, height / 6);
  textSize(50);
  text("PONG PERFECT", width / 2, height / 6 * 4.75);
}

function mousePressed() {
  if (mode === STARTSCREEN) {
    mode = GAME; // 点击开始游戏
  } else if (mode === GAME) {
    // 游戏中的点击事件(如果需要)
  }
}

function game() {
  background(255, 194, 209);
  // 绘制分数板和中线
  rectMode(CENTER);
  fill(255, 229, 236);
  rect(width/2, height / 8, 100

热门AI工具

更多
Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

7455

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

362

2025.11.27

C++ 面向对象编程与设计模式实践
C++ 面向对象编程与设计模式实践

本专题整合了 C++ 面向对象编程的核心机制,涵盖类的定义与访问控制、构造函数与析构函数、拷贝语义与移动语义、继承与虚函数多态、抽象类与接口、运算符重载、友元函数等关键知识点,同时结合单例模式、工厂模式、观察者模式、策略模式等常用设计模式的 C++ 实现,帮助开发者写出结构清晰、易于维护的面向对象代码。

384

2026.04.09

Java 面向对象与核心编程机制
Java 面向对象与核心编程机制

系统讲解 Java 面向对象编程的核心知识体系,涵盖类的定义与封装、构造方法与 this/super 关键字、继承与方法重写、多态与向上向下转型、抽象类与接口的区别与设计原则、异常处理机制(try-catch-finally / 自定义异常)、泛型的类型参数与通配符、集合框架(List/Set/Map)的使用与遍历方式,帮助开发者全面掌握 Java 程序设计的核心机制与面向对象思维。

263

2026.04.17

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

4836

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1435

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1878

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

10383

2023.08.17

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

0

2026.11.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn