JavaScript:检测圆的区域

问题描述 投票:0回答:1

我正在为用 JavaScript/TypeScript 编写的游戏客户端开发软件操纵杆实现。我需要检测圆半径内发生 4 向移动(上、下、左、右)事件的不同区域。目前我的映射如下所示(因为我只知道如何使用四边多边形):

current

但是我想要实现的是这样的:

desired

紫色代表横向移动的区域。绿色为垂直。灰色和黑色是死区(游戏),不应进行定向移动。

我正在使用的圆的半径是 75 像素。内部灰色死区半径为 24 像素。

以下代码是我当前如何检测方向的示例(不准确,因为我只包含与此问题相关的内容):

    /**
     * Retrieves the interpreted direction of joystick.
     *
     * Returned directions are represented using the following integer values:
     * - 0: no movement (stop)
     * - 1: up
     * - 2: right
     * - 3: down
     * - 4: left
     *
     * @param relX {number}
     *   Point of event on X axis relative to joystick center.
     * @param relY {number}
     *   Point of event on Y axis relative to joystick center.
     * @return {number}
     *   Facing direction the joystick's state represents.
     */
    private getPressedDirection(relX: number, relY: number): number {
        let dir = 0;
        const playThreshold = 24;
        if (relX < -playThreshold) {
            dir = 4;
        } else if (relX > playThreshold) {
            dir = 2;
        } else if (relY < -playThreshold) {
            dir = 1;
        } else if (relY > playThreshold) {
            dir = 3;
        }
        return dir;
    }

正如您可能看到的,它存在多个问题。检测区域的大小不相等。死区区域是正方形而不是圆形。并且它允许在主圆半径之外检测方向。

如果我理解正确,我需要使用毕达哥拉斯定理来检测圆半径的边界,如https://stackoverflow.com/a/7227057/4677917以及我发现的许多其他答案中的建议,执行如下操作:

    private getPressedDirection(relX: number, relY: number): number {
        let dir = 0;
        const radius = 75;
        const playThreshold = 24;
        const eventResult = Math.pow(relX, 2) + Math.pow(relY, 2);
        const hit = eventResult < Math.pow(radius, 2) && eventResult > Math.pow(playThreshold, 2);
        ...

如果这是正确的,它会告诉我事件是否发生在操纵杆圆圈的边界内。但我仍然不确定如何检测事件发生的区域。

注意:https://www.w3resource.com/javascript-exercises/javascript-math-exercise-35.php建议使用毕达哥拉斯定理作为

Math.sqrt(Math.pow(relX, 2) + Math.pow(relY, 2))
。但据我了解,平方根是为了求三角形第三条边的长度。从其他答案来看,我似乎想简单地使用
Math.pow(relX, 2) + Math.pow(relY, 2)

javascript typescript collision-detection joystick pythagorean
1个回答
0
投票

感谢@CBroe指出我需要寻找什么。

我可以使用如下函数找到相对于操纵杆中心的角度:

    private pointToDeg(relX: number, relY: number): number {
        // convert coordinates to radians
        const rad = Math.atan2(relY, relX);
        // convert radians to angle of degrees
        return rad * 180 / Math.PI;
    }

我将角度区域映射到操纵杆:

    // format: [direction, angleMin, angleMax]
    private readonly sectors: number[][] = [
        // NOTE: 0 degrees represents right on joystick plane
        [1, 225, 314.9],
        [2, 315, 44.9],
        [3, 45, 134.9],
        [4, 135, 224.9]
    ];

然后我可以迭代映射:

    private getPressedDirection(relX: number, relY: number): number {
        const angle = this.pointToDeg(relX, relY);
        for (const s of this.sectors) {
            if (this.sectorContains(s[1], s[2], angle)) {
                return s[0];
            }
        }
        return 0;
    }

    /**
     * Checks if an angle is within a specified sector region.
     */
    private sectorContains(min: number, max: number, angle: number): boolean {
        // ensure values are within boundaries of 360 degrees
        min %= 360;
        max %= 360;
        angle %= 360;
        // normalize to positive degrees values
        min = min < 0 ? min + 360 : min;
        max = max < 0 ? max + 360 : max;
        angle = angle < 0 ? angle + 360 : angle;
        // check if within range
        if (min > max) {
            return angle >= min || angle <= max;
        }
        return angle >= min && angle <= max;
    }

检查点是否在非死区区域内的第二个问题可以通过如下函数解决:

    private isHit(relX: number, relY: number): boolean {
        const dist = Math.pow(Math.abs(relX), 2) + Math.pow(Math.abs(relY), 2);
        return dist < Math.pow(this.radius, 2) && dist > Math.pow(this.playThreshold, 2);
    }

    private getPressedDirection(relX: number, relY: number): number {
        if (!this.isHit(relX, relY)) {
                return 0;
        }
        const angle = this.pointToDeg(relX, relY);
        for (const s of this.sectors) {
            if (this.sectorContains(s[1], s[2], angle)) {
                return s[0];
            }
        }
        return 0;
    }

如果我犯了任何错误,我会尽力改正。它不是完全从实际代码复制的。

© www.soinside.com 2019 - 2024. All rights reserved.