You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现基于像素接触的游戏引擎触摸系统?支持可旋转Hit Box

实现带旋转效果的可见碰撞盒触摸系统

你当前的碰撞盒是轴对齐矩形,无法适配旋转场景,且缺少可视化反馈。以下是针对需求的完整解决方案:

核心思路

实现旋转碰撞盒的触摸检测,关键是将触摸点从全局坐标系转换到碰撞盒的局部坐标系(以碰撞盒中心为原点、旋转角度归零的坐标系),之后就能用简单的轴对齐逻辑判断命中。同时给碰撞盒添加可视化元素,让用户清晰看到触发区域。

具体实现步骤

1. 扩展碰撞盒数据结构

给triggerBox对象新增旋转角度、宽高存储,以及可视化DOM元素引用:

function createTriggerBox(name, x, y, width, height, rot = 0) {
  // 创建可视化碰撞盒DOM
  const boxEl = document.createElement('div');
  boxEl.id = `trigger-${name}`;
  boxEl.className = 'trigger-box';
  boxEl.style.position = 'absolute';
  boxEl.style.width = `${width}px`;
  boxEl.style.height = `${height}px`;
  boxEl.style.left = `${x - width/2}px`;
  boxEl.style.bottom = `${y - height/2}px`;
  boxEl.style.transform = `rotate(${rot}deg)`;
  boxEl.style.transformOrigin = 'center center';
  boxEl.style.border = '2px dashed rgba(255, 0, 0, 0.7)';
  boxEl.style.pointerEvents = 'none'; // 不干扰实体的触摸交互
  document.getElementById('project').appendChild(boxEl);

  triggerBox.push({
    title: name,
    posX: x,
    posY: y,
    width: width,
    height: height,
    rot: rot, // 存储旋转角度(度数)
    enabled: true,
    element: boxEl // 存储DOM元素用于后续更新
  });
}

2. 旋转矩形的触摸检测逻辑

编写工具函数,通过坐标变换实现精准碰撞判断:

// 角度转弧度
function degToRad(deg) {
  return deg * Math.PI / 180;
}

// 判断点是否在旋转矩形内
function isPointInRotatedBox(pointX, pointY, box) {
  // 计算点相对于矩形中心的偏移量
  const dx = pointX - box.posX;
  const dy = pointY - box.posY;

  // 将偏移量反向旋转,转换到矩形的局部坐标系
  const rad = degToRad(-box.rot);
  const rotatedX = dx * Math.cos(rad) - dy * Math.sin(rad);
  const rotatedY = dx * Math.sin(rad) + dy * Math.cos(rad);

  // 局部坐标系下的轴对齐碰撞检测
  const halfWidth = box.width / 2;
  const halfHeight = box.height / 2;
  return Math.abs(rotatedX) <= halfWidth && Math.abs(rotatedY) <= halfHeight;
}

3. 替换原有触发检测函数

更新TriggeredBox,使用旋转碰撞判断逻辑:

function TriggeredBox(name, action) {
  const box = triggerBox.find(b => b.title === name);
  if (!box || !box.enabled) return;

  // 绑定点击/触摸事件(根据游戏需求替换为触摸事件)
  document.addEventListener('click', (e) => {
    // 转换为游戏坐标系(与实体的偏移逻辑对应)
    const gameX = e.clientX - window.screen.width / 2;
    const gameY = e.clientY - window.screen.height / 2;

    if (isPointInRotatedBox(gameX, gameY, box)) {
      last.boxTriggerer = name;
      action();
    }
  });
}

4. 同步碰撞盒与实体的状态

当实体移动或旋转时,同步更新碰撞盒的位置和角度:

function updateTriggerBox(name, x, y, rot) {
  const box = triggerBox.find(b => b.title === name);
  if (!box) return;

  box.posX = x;
  box.posY = y;
  box.rot = rot;

  // 更新可视化元素
  box.element.style.left = `${x - box.width/2}px`;
  box.element.style.bottom = `${y - box.height/2}px`;
  box.element.style.transform = `rotate(${rot}deg)`;
}

5. 修复原代码的主线程阻塞问题

原代码的while(true)会卡死页面,改用requestAnimationFrame实现游戏循环:

function gameLoop() {
  for(let c = 0; c < entities.length; c++){
    const entity = entities[c];
    const el = document.getElementById(entity.name);
    if (!el) continue;
    
    el.style.left = `${(window.screen.width / 2) + entity.x}px`;
    el.style.bottom = `${(window.screen.height / 2) + entity.y}px`;
    el.style.width = `${entity.width}px`;
    el.style.height = `${entity.height}px`;
    el.style.transform = `rotate(${entity.rotDeg}deg)`;

    // 同步关联的碰撞盒状态
    updateTriggerBox(entity.name, entity.x, entity.y, entity.rotDeg);
  }
  requestAnimationFrame(gameLoop);
}

// 页面加载完成后启动循环
document.onreadystatechange = function() {
  if (document.readyState === "complete") {
    document.querySelector("#loader").style.display = "none";
    document.querySelector("body").style.visibility = "visible";
    gameLoop();
  }
};

使用示例

// 创建带旋转的实体
create.entity("player", 100, 100, 0, 0, 45, "player.png");
// 创建对应旋转碰撞盒
createTriggerBox("player-box", 0, 0, 100, 100, 45);
// 设置触发动作
TriggeredBox("player-box", () => {
  console.log("Player碰撞盒被触发!");
});

实现后的碰撞盒:

  • 支持任意角度旋转
  • 可视化红色虚线框,用户可直观看到触发区域
  • 触摸检测精准匹配旋转后的区域
  • 与实体的位置、旋转状态实时同步

内容的提问来源于stack exchange,提问作者RagDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:01:01