如何实现基于像素接触的游戏引擎触摸系统?支持可旋转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
相关产品推荐
相关产品推荐

