计算给定进出方向所需的2D转弯运动



考虑一个2D方形平铺网格(类似棋盘(,它包含类似传送带的结构,可以弯曲和移动棋子。

我需要根据计算转弯运动(TURN_LEFTTURN_RIGHTSTAY(

  1. 工件将移动到场上的方向
  2. 下层带离开场的方向

示例:

1   2 
1 |>X>|>v |
2 |   | v |

皮带转动RIGHT圈。因此,calcTurn(LEFT, DOWN)的结果应该是TURN_RIGHT。这意味着X游戏棋子在(1,2)的曲线上移动时将向右旋转90°。

我已经实现了一个函数,但它只适用于我的一些测试用例。

enum class Direction {
NONE,
UP,
RIGHT,
DOWN,
LEFT;
fun isOpposite(other: Direction) = this == UP && other == DOWN
|| this == DOWN && other == UP
|| this == LEFT && other == RIGHT
|| this == RIGHT && other == LEFT
}
data class Vec2(val x: Float, val y: Float)
fun Direction.toVec2() = when (this) {
Direction.NONE -> Vec2(0f, 0f)
Direction.UP -> Vec2(0f, 1f)
Direction.RIGHT -> Vec2(1f, 0f)
Direction.DOWN -> Vec2(0f, -1f)
Direction.LEFT -> Vec2(-1f, 0f)
}
fun getTurnMovement(incomingDirection: Direction, outgoingDirection: Direction): Movement {
if (incomingDirection.isOpposite(outgoingDirection) || incomingDirection == outgoingDirection) {
return Movement.STAY
}
val incVec = incomingDirection.toVec2()
val outVec = outgoingDirection.toVec2()
val angle = atan2(
incVec.x * outVec.x - incVec.y * outVec.y,
incVec.x * outVec.x + incVec.y * outVec.y
)
return when {
angle < 0 -> Movement.TURN_RIGHT
angle > 0 -> Movement.TURN_LEFT
else -> Movement.STAY
}
}

我不太清楚这里出了什么问题,尤其不是因为一些测试用例可以工作(比如DOWN+LEFT=TURN_LEFT(,但其他测试用例不可以(比如DOWN+RIGHT=STAY而不是TURN_LEFT(

您试图计算两个二维向量之间的角度,但操作不正确。

在数学上,给定两个向量(x1,y1(和(x2、y2/x2(-arctan(y1/x1(。

将其翻译为Kotlin,您应该使用:

val angle = atan2(outVec.y, outVec.x) - atan2(incVec.y, incVec.x)

我注意到,您也可以通过在when语句中描述案例来实现您的总体目标,因为您只有少量可能的方向,但也许您想要一个更通用的解决方案。

这并不能回答你为什么代码不工作的问题,但这里有另一种可以用来包装有序数据的通用方法:

enum class Direction {

UP, RIGHT, DOWN, LEFT;

companion object {
// storing thing means you only need to generate the array once
private val directions = values()
private fun getPositionWrapped(pos: Int) = directions[(pos).mod(directions.size)]
}

// using getters here as a general example
val toLeft get() = getPositionWrapped(ordinal - 1)
val toRight get() = getPositionWrapped(ordinal + 1)
val opposite get() = getPositionWrapped(ordinal + 2)
}

它利用了枚举是有序的这一事实,使用ordinal属性来提取特定常数的位置。它还使用了(x).mod(y)技巧,如果x为负,将其放在括号中使其环绕

x|  6  5  4  3  2  1  0 -1 -2 -3 -4 -5
mod 4|  2  1  0  3  2  1  0  3  2  1  0  3

这使得获取下一个或上一个(或者无论您想跳多远(索引变得很容易,就像一个圆形数组一样。

由于您的示例中有一个NONE值(显然不符合此模式(,我可能会用nullDirection?来表示它,因为它更像是缺少值,而不是实际的类型方向。当然,这取决于你在做什么!

最新更新