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
相关产品推荐
相关产品推荐

