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

子弹碰撞检测异常:本应无交集时Area对象却非空的问题排查

子弹碰撞检测异常:未靠近玩家就触发伤害判定的问题分析与解决

问题描述

开发子弹碰撞玩家造成伤害的功能时出现异常:子弹尚未靠近玩家就触发了伤害判定。玩家的hitbox位置设置正确,hit()方法负责对玩家造成伤害并缩小子弹尺寸。

碰撞检测代码

Area a = new Area(AffineTransform.getRotateInstance(r, getX() + getWidth() / 2, getY() + getHeight() / 2)
        .createTransformedShape(new Rectangle(getX() + (int)(getWidth()-sx) / 2, getY() + (int)(getHeight()-sy) / 2, (int)sx, (int)sy)));
a.intersect(new Area(Root.game.player.hitbox));
if (!a.isEmpty())
    hit(Root.game.player);

子弹绘制代码

public void paint(Graphics g){
    Graphics2D g2 = (Graphics2D)g;
    AffineTransform a = g2.getTransform();
    a.rotate(Math.toRadians(r), getWidth() / 2, getHeight() / 2);
    int sx = (int)Math.round(this.sx), sy = (int)Math.round(this.sy);
    g2.setTransform(a);
    int dx = (getWidth()-sx) / 2, dy = (getHeight()-sy) / 2;
    g2.setPaint(new RadialGradientPaint(new Rectangle(sx / 6 + dx, sy / 6 + dy, Math.max(1, sx * 2 / 3), Math.max(1, sy * 2 / 3)), new float[]{0, 1}, new Color[]{Color.CYAN, new Color(0, 255, 255, 0)}, CycleMethod.NO_CYCLE));
    g2.setStroke(new BasicStroke(sy / 5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    g2.drawRect(sx / 3 + dx, sy / 3 + dy, sx / 3, sy / 3);
    g2.setPaint(new RadialGradientPaint(new Rectangle(sx / 6 + dx, sy / 6 + dy, Math.max(1, sx * 2 / 3), Math.max(1, sy * 2 / 3)), new float[]{0, 1}, new Color[]{c, Color.BLUE}, CycleMethod.NO_CYCLE));
    g2.fillRoundRect(sx / 3 + dx, sy / 3 + dy, sx / 3, sy / 3, sx / 20, sy / 15);
}

问题根源

1. 坐标空间与旋转逻辑不匹配

绘制时,旋转操作基于子弹自身的局部坐标中心(getWidth()/2, getHeight()/2),但碰撞检测时,直接在全局坐标下创建Rectangle并以全局坐标的子弹中心旋转,导致碰撞区域的位置计算出现偏移,和实际绘制的子弹位置不对应。

2. 碰撞区域范围远大于实际图形

实际绘制的子弹核心区域是sx/3 * sy/3的圆角矩形,但碰撞检测用的是sx * sy的完整Rectangle,范围比可视子弹大得多,自然会提前触发碰撞判定。

修复方案

统一碰撞检测与绘制的坐标逻辑,让碰撞区域严格匹配实际绘制的图形:

// 1. 按照绘制逻辑创建局部坐标下的碰撞形状(和fillRoundRect一致)
int sx = (int)Math.round(this.sx);
int sy = (int)Math.round(this.sy);
int dx = (getWidth() - sx) / 2;
int dy = (getHeight() - sy) / 2;
RoundRectangle2D localHitShape = new RoundRectangle2D.Double(
    sx/3 + dx, sy/3 + dy,
    sx/3, sy/3,
    sx/20, sy/15
);

// 2. 构建正确的变换:先平移到全局位置,再以子弹自身中心旋转
AffineTransform transform = new AffineTransform();
transform.translate(getX(), getY());
transform.rotate(Math.toRadians(r), getWidth()/2, getHeight()/2);

// 3. 转换为全局坐标的碰撞形状
Shape globalHitShape = transform.createTransformedShape(localHitShape);

// 4. 执行碰撞检测
Area bulletArea = new Area(globalHitShape);
Area playerArea = new Area(Root.game.player.hitbox);
bulletArea.intersect(playerArea);
if (!bulletArea.isEmpty()) {
    hit(Root.game.player);
}

关键调整点

  • 用RoundRectangle2D替代Rectangle,让碰撞区域和绘制的圆角矩形完全一致。
  • 先基于局部坐标创建碰撞形状,再通过平移+旋转转换到全局坐标,和绘制时的变换逻辑对齐。
  • 旋转中心保持为子弹自身的局部中心,避免坐标空间混淆。

内容的提问来源于stack exchange,提问作者Tech of the Absence

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:55:53