在 2D 平面上旋转 3D 矢量
Rotate 3D vectors on 2D plane
我有两个 Vec3,Camera Forward 和 Turret Forward。这两个向量都在不同的平面上,其中 Camera Forward 基于自由视角相机,而 Turret Forward 由其所在的坦克、坦克所在的地形等决定。Turret Up 和 Camera Up 很少使用匹配。
我的问题如下:我希望炮塔能够以固定速度(每秒 44 度)旋转,以便它始终与相机指向的方向会聚。如果坦克处于一个奇怪的角度,根本无法与相机会聚,它应该找到最近的地方并坐在那里,而不是无限期地抖动。
我这辈子都解决不了这个问题。我尝试了几种在网上找到的方法,但总是产生奇怪的结果。
local forward = player.direction:rotate(player.turret, player.up)
local side = forward:cross(player.up)
local projection = self.camera.direction:dot(forward) * forward + self.camera.direction:dot(side) * side
local angle = math.atan2(forward.y, forward.x) - math.atan2(projection.y, projection.x)
if angle ~= 0 then
local dt = love.timer.getDelta()
if angle <= turret_speed * dt then
player.turret_velocity = turret_speed
elseif angle >= -turret_speed * dt then
player.turret_velocity = -turret_speed
else
player.turret_velocity = 0
player.turret = player.turret + angle
end
end
我会做不同的事
获取GCS(全局坐标系)中的相机方向向量c
- 我使用 Z 轴作为观察轴,所以只需从变换矩阵中提取 z 轴
- 更多信息看这里
在GCS中获取炮塔方向矢量t
- 与项目符号 1 相同。
计算展台方向的旋转转台方向向量
t0=rotation(-44.0deg/s)*t
t1=rotation(+44.0deg/s)*t
现在计算点积
a =dot(c,t)
a0=dot(c,t0)
a1=dot(c,t1)
确定炮塔旋转
- if max(a0,a,a1)==a0 旋转(-44.0deg/s)`
- if max(a0,a,a1)==a1 旋转(+44.0deg/s)`
[备注]
- 这应该会聚到期望的方向
- 应调整角度步长以匹配用于更新此的时间间隔
- 您可以对项目符号 1,2 使用任何通用坐标系,而不仅仅是 GCS
- 在这种情况下,点积是
cos(angle between vectors)
因为两个 c,t
都是单位向量(如果取自标准变换矩阵)
- 所以如果 cos(angle)==1 则方向相同
- 但是你的相机可以在不同的轴上旋转,所以只要找到 cos(angle) 的最大值
经过更多的研究和测试,我得出了以下解决方案。运行顺畅!
function Gameplay:moved_axisright(joystick, x, y)
if not self.manager.id then return end
local turret_speed = math.rad(44)
local stick = cpml.vec2(-x, -y)
local player = self.players[self.manager.id]
-- Mouse and axis control camera view
self.camera:rotateXY(stick.x * 18, stick.y * 9)
-- Get angle between Camera Forward and Turret Forward
local fwd = cpml.vec2(0, 1):rotate(player.orientation.z + player.turret)
local cam = cpml.vec2(1, 0):rotate(math.atan2(self.camera.direction.y, self.camera.direction.x))
local angle = fwd:angle_to(cam)
-- If the turret is not true, adjust it
if math.abs(angle) > 0 then
local function new_angle(direction)
local dt = love.timer.getDelta()
local velocity = direction * turret_speed * dt
return cpml.vec2(0, 1):rotate(player.orientation.z + player.turret + velocity):angle_to(cam)
end
-- Rotate turret into the correct direction
if new_angle(1) < 0 then
player.turret_velocity = turret_speed
elseif new_angle(-1) > 0 then
player.turret_velocity = -turret_speed
else
-- If rotating the turret a full frame will overshoot, set turret to camera position
-- atan2 starts from the left and we need to also add half a rotation. subtract player orientation to convert to local space.
player.turret = math.atan2(self.camera.direction.y, self.camera.direction.x) + (math.pi * 1.5) - player.orientation.z
player.turret_velocity = 0
end
end
local direction = cpml.mat4():rotate(player.turret, { 0, 0, 1 }) * cpml.mat4():rotate(player.orientation.z, { 0, 0, 1 })
player.turret_direction = cpml.vec3(direction * { 0, 1, 0, 1 })
end
我有两个 Vec3,Camera Forward 和 Turret Forward。这两个向量都在不同的平面上,其中 Camera Forward 基于自由视角相机,而 Turret Forward 由其所在的坦克、坦克所在的地形等决定。Turret Up 和 Camera Up 很少使用匹配。
我的问题如下:我希望炮塔能够以固定速度(每秒 44 度)旋转,以便它始终与相机指向的方向会聚。如果坦克处于一个奇怪的角度,根本无法与相机会聚,它应该找到最近的地方并坐在那里,而不是无限期地抖动。
我这辈子都解决不了这个问题。我尝试了几种在网上找到的方法,但总是产生奇怪的结果。
local forward = player.direction:rotate(player.turret, player.up)
local side = forward:cross(player.up)
local projection = self.camera.direction:dot(forward) * forward + self.camera.direction:dot(side) * side
local angle = math.atan2(forward.y, forward.x) - math.atan2(projection.y, projection.x)
if angle ~= 0 then
local dt = love.timer.getDelta()
if angle <= turret_speed * dt then
player.turret_velocity = turret_speed
elseif angle >= -turret_speed * dt then
player.turret_velocity = -turret_speed
else
player.turret_velocity = 0
player.turret = player.turret + angle
end
end
我会做不同的事
获取GCS(全局坐标系)中的相机方向向量
c
- 我使用 Z 轴作为观察轴,所以只需从变换矩阵中提取 z 轴
- 更多信息看这里
在GCS中获取炮塔方向矢量
t
- 与项目符号 1 相同。
计算展台方向的旋转转台方向向量
t0=rotation(-44.0deg/s)*t
t1=rotation(+44.0deg/s)*t
现在计算点积
a =dot(c,t)
a0=dot(c,t0)
a1=dot(c,t1)
确定炮塔旋转
- if max(a0,a,a1)==a0 旋转(-44.0deg/s)`
- if max(a0,a,a1)==a1 旋转(+44.0deg/s)`
[备注]
- 这应该会聚到期望的方向
- 应调整角度步长以匹配用于更新此的时间间隔
- 您可以对项目符号 1,2 使用任何通用坐标系,而不仅仅是 GCS
- 在这种情况下,点积是
cos(angle between vectors)
因为两个c,t
都是单位向量(如果取自标准变换矩阵) - 所以如果 cos(angle)==1 则方向相同
- 但是你的相机可以在不同的轴上旋转,所以只要找到 cos(angle) 的最大值
经过更多的研究和测试,我得出了以下解决方案。运行顺畅!
function Gameplay:moved_axisright(joystick, x, y)
if not self.manager.id then return end
local turret_speed = math.rad(44)
local stick = cpml.vec2(-x, -y)
local player = self.players[self.manager.id]
-- Mouse and axis control camera view
self.camera:rotateXY(stick.x * 18, stick.y * 9)
-- Get angle between Camera Forward and Turret Forward
local fwd = cpml.vec2(0, 1):rotate(player.orientation.z + player.turret)
local cam = cpml.vec2(1, 0):rotate(math.atan2(self.camera.direction.y, self.camera.direction.x))
local angle = fwd:angle_to(cam)
-- If the turret is not true, adjust it
if math.abs(angle) > 0 then
local function new_angle(direction)
local dt = love.timer.getDelta()
local velocity = direction * turret_speed * dt
return cpml.vec2(0, 1):rotate(player.orientation.z + player.turret + velocity):angle_to(cam)
end
-- Rotate turret into the correct direction
if new_angle(1) < 0 then
player.turret_velocity = turret_speed
elseif new_angle(-1) > 0 then
player.turret_velocity = -turret_speed
else
-- If rotating the turret a full frame will overshoot, set turret to camera position
-- atan2 starts from the left and we need to also add half a rotation. subtract player orientation to convert to local space.
player.turret = math.atan2(self.camera.direction.y, self.camera.direction.x) + (math.pi * 1.5) - player.orientation.z
player.turret_velocity = 0
end
end
local direction = cpml.mat4():rotate(player.turret, { 0, 0, 1 }) * cpml.mat4():rotate(player.orientation.z, { 0, 0, 1 })
player.turret_direction = cpml.vec3(direction * { 0, 1, 0, 1 })
end