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

Java中椭圆碰撞检测实现——类Smash Bros游戏碰撞箱开发

椭圆碰撞箱实现问题:仅部分场景生效的原因与解决方案

问题描述

我正在开发一款类似Smash Bros的游戏,计划用椭圆作为碰撞箱,以此更清晰地定位碰撞位置,实现更精准的方向移动。我基于椭圆公式 ((x-h)²)/a² + ((y-k)²)/b² = 1 实现,但功能无法正常运行。

我尝试的思路是:遍历第一个椭圆定义域内的每个x值,和第二个椭圆的定义域x值做对比,若x值相近则检查两者是否共享y值。但这个方法只在部分场景下有效,想搞清楚原因。

初始实现代码:

public double[] intersects(Hitbox h1)
{
    double d[] = {-1, -1};
    for(double q = h-(a/2.0); q <= h+(a/2.0); q += 10)
    {
        for(double w = h1.getH()-(h1.getA()/2.0); w <= h1.getH()+(h1.getA()/2.0); w += 10)
        {
            int ha = h1.getA();
            int hb = h1.getB();
            int hh = h1.getH();
            int hk = h1.getK();
            
            if(Math.abs(q-w) < 10)
            {
                long ab = (a*a)*(b*b);
                System.out.println( q + " 	" + w);
                System.out.println("ab	" + ab);
                long bb = (int)((b*b)*((q-h)*(q-h)));
                System.out.println("bb	" + bb);
                double y1 = k + Math.sqrt((ab-bb)/(a*a));
                System.out.println("y1	" + y1);
                double negy1 = k - Math.sqrt((ab-bb)/(a*a));
                System.out.println("negy1	" + negy1);

                ab = (ha*ha)*(hb*hb);
                System.out.println("ab2	" + ab);
                bb = (int)((hb*hb)*((w-hh)*(w-hh)));
                System.out.println("bb2	" + bb);
                double y2 = hk + Math.sqrt((ab-bb)/(ha*ha));
                System.out.println("y2	" + y2);
                double negy2 = hk - Math.sqrt((ab-bb)/(ha*ha));
                System.out.println("negy2	" + negy2);
                
                if(Math.abs(y2-y1) < 30 ||Math.abs(negy2-y1) < 30)
                {
                    d[0] = q;
                    d[1] = y1;
                    latestIntersection = d;
                    System.out.println("fadonbhfabdfbnaodf");
                    return d;
                }
                if(Math.abs(y2-negy1) < 30 ||Math.abs(negy2-negy1) < 30)
                {
                    d[0] = q;
                    d[1] = negy1;
                    latestIntersection = d;
                    System.out.println("fadonbhfabdfbnaodf");
                    return d;
                }
            }
        }
    }
    latestIntersection = d;
    return d;
}

初始方案失效的原因

  • 采样精度不足:遍历x值时步长设为10,会直接跳过大量潜在交点,尤其是椭圆倾斜或交点不在采样点附近时,完全无法检测到碰撞。
  • x值匹配逻辑粗糙:用Math.abs(q-w) < 10判断x相近,范围太宽,会引入大量无效的y值对比,同时也可能漏掉刚好超出阈值的真实交点。
  • 椭圆公式与遍历范围不匹配:遍历x的范围是h-(a/2.0)到h+(a/2.0),这仅适用于水平半轴为a的椭圆,若椭圆的长轴是垂直方向(b>a),这个范围会完全错误,导致漏检。
  • 返回逻辑过于草率:找到一个近似匹配就直接返回,既可能错过更精准的交点,也没考虑椭圆可能存在多个交点的情况。

解决方案代码

public class Hitbox {

    private int h, k, a, b, startx, starty;
    private double[] latestIntersection = {-1,-1};
    
    public Hitbox(int h, int k, int a, int b)
    {
        this.a = a;
        this.b = b;
        this.h = h;
        this.k = k*-1;
    }
    
    public double[] intersects(Hitbox h1)
    {
        double d[] = {-1, -1};
        for(double q = 1; q <= 360; q+=.5)
        {
            double f = 0;
            if(a > b)
                f = Math.sqrt((a*a)-(b*b));
            else
                f = Math.sqrt((b*b)-(a*a));
            
            double e = f/a;
            
            double top = (a*a)*(1-(e*e));
            double bottom = 1-((e*e)*(Math.cos(q)*Math.cos(q)));
            
            double r1 = Math.sqrt(top/bottom);
            
            double x1 = r1*Math.cos(q);
            double y1 = r1*Math.sin(q);
            x1 += h;
            y1 += k;
            
            for(double w = 0; w <= 360; w+=.5)
            {
                double a2 = h1.getA();
                double b2 = h1.getB();
                
                double f2 = 0;
                
                if(a2 > b2)
                    f2 = Math.sqrt((a2*a2)-(b2*b2));
                else
                    f2 = Math.sqrt((b2*b2)-(a2*a2));
                
                double e2 = f2/a2;
                
                double top2 = (a2*a2)*(1-(e2*e2));
                double bottom2 = 1-((e2*e2)*(Math.cos(w)*Math.cos(w)));
                
                double r2 = Math.sqrt(top2/bottom2);
                
                double x2 = r2*Math.cos(w);
                double y2 = r2*Math.sin(w);
                x2 += h1.getH();
                y2 += h1.getK();
                
                if(Math.abs(x2-x1) < 1 && Math.abs(y2-y1) < 1)
                {
                    d[0] = x1;
                    d[1] = y2;
                    latestIntersection = d;
                    return d;
                }
            }
        }
        latestIntersection = d;
        return d;
    }
    
    
    public double[] getOppositeInteresection()
    {
        double d[] = {-1,-1};
        if(latestIntersection != d)
        {
            if(latestIntersection[0]-h < 0)
            {
                d[0] = latestIntersection[0]-h + (((latestIntersection[0]-h)*2)*-1);
                System.out.println(d[0]);
                d[0] += h;
            }
            else if(latestIntersection[0]-h > 0)
            {
                d[0] = latestIntersection[0]-h - ((latestIntersection[0]-h)*2);
                System.out.println(d[0]);
                d[0] += h;
            }
            if(latestIntersection[1]-k < 0)
            {
                d[1] = latestIntersection[1]-k + (((latestIntersection[1]-k)*2)*-1);
                System.out.println(d[1]);
                d[1] += k;
                return d;
            }
            else if(latestIntersection[1]-k > 0)
            {
                d[1] = latestIntersection[1]-k - ((latestIntersection[1]-k)*2);
                System.out.println(d[1]);
                d[1] += k;
                return d;
            }
        }
        
        return d;
    }
    
    

    public int getA()
    {
        return a;
    }
    public int getB()
    {
        return b;
    }

    public int getH()
    {
        return this.h;
    }

    public int getK()
    {
        return this.k;
    }

    public void setH(int h)
    {
        this.h = h;
    }

    public void setK(int k)
    {
        this.k = k;
    }

    public void setA(int a)
    {
        this.a = a;
    }

    public void setB(int b)
    {
        this.b = b;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:45:08