Math 在二维平面上旋转三维矢量

Math 在二维平面上旋转三维矢量,math,vector,lua,rotation,Math,Vector,Lua,Rotation,我有两个Vec3,摄像机前方和炮塔前方。这两个矢量都位于不同的平面上,其中“向前摄影机”基于自由视摄影机,而“向前炮塔”由其所在的坦克、坦克所在的地形等决定。向上炮塔和向上摄影机很少匹配 我的问题如下:我希望炮塔能够以固定速度(每秒44度)旋转,以便它始终与相机指向的方向会聚。如果坦克处于一个奇怪的角度,根本无法与摄像机会聚,它应该找到最近的位置,坐在那里,而不是无限期地抖动 我一辈子也解决不了这个问题。我在网上找到了几种方法,这些方法总是产生奇怪的结果 local forward

我有两个Vec3,摄像机前方和炮塔前方。这两个矢量都位于不同的平面上,其中“向前摄影机”基于自由视摄影机,而“向前炮塔”由其所在的坦克、坦克所在的地形等决定。向上炮塔和向上摄影机很少匹配

我的问题如下:我希望炮塔能够以固定速度(每秒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
local forward=玩家。方向:旋转(玩家。炮塔,玩家。向上)
本地侧=前锋:传中(球员向上)
局部投影=自拍照。方向:点(向前)*向前+自拍照。方向:点(侧面)*侧面
局部角度=数学atan2(向前.y,向前.x)-数学atan2(投影.y,投影.x)
如果角度~=0,则
local dt=love.timer.getDelta()
如果角度=-炮塔速度*dt,则
player.tarta\u速度=-tarta\u速度
其他的
player.tarta_速度=0
玩家。炮塔=玩家。炮塔+角度
终止
终止

我会做不同的事

  • 获取GCS(全球坐标系)中的摄像机方向向量
    c

    • 我使用Z轴作为观察轴,所以只需从变换矩阵中提取Z轴
    • 有关更多信息,请查看此处
  • 在地面军事系统中获取炮塔方向向量
    t

    • 与子弹1相同
  • 计算展位方向上旋转的转台方向向量

    • t0=旋转(-44.0度/秒)*t
    • t1=旋转(+44.0度/秒)*t
  • 现在计算点积

    • a=dot(c,t)
    • a0=dot(c,t0)
    • a1=dot(c,t1)
  • 确定转台旋转

    • 如果最大值(a0,a,a1)=a0旋转(-44.0度/秒)`
    • 如果最大值(a0,a,a1)=a1旋转(+44.0度/秒)`
  • [附注]

    • 这应该收敛到所需的方向
    • 应调整角度步长的大小,以匹配用于此更新的时间间隔
    • 您可以为项目符号1,2使用任何通用坐标系,而不仅仅是GCS
    • 在这种情况下,点积是
      cos(向量之间的角度)
      ,因为
      c,t
      都是单位向量(如果取自标准变换矩阵)
    • 所以如果cos(angle)==1,那么方向是相同的
    • 但是你的相机可以在不同的轴上旋转,所以只需找到最大cos(角度)

    经过更多的研究和测试,我最终得到了以下解决方案。它工作得很顺利

    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
    
    功能游戏:向右移动(操纵杆,x,y)
    如果不是self.manager.id,则返回end
    本地炮塔速度=数学弧度(44)
    本地棒=每分钟向量2(-x,-y)
    本地玩家=self.players[self.manager.id]
    --鼠标和轴控制摄影机视图
    自拍照:可旋转(斗杆.x*18,斗杆.y*9)
    --获取摄像机前方和炮塔前方之间的角度
    本地fwd=cpml.vec2(0,1):旋转(玩家方向z+玩家炮塔)
    局部cam=cpml.vec2(1,0):旋转(数学atan2(self.camera.direction.y,self.camera.direction.x))
    局部角度=前进:角度(凸轮)
    --如果炮塔不正确,调整它
    如果math.abs(角度)>0,则
    局部功能新角度(方向)
    local dt=love.timer.getDelta()
    局部速度=方向*转台速度*dt
    返回cpml.vec2(0,1):旋转(游戏者方向z+游戏者转台+速度):角度U到(凸轮)
    终止
    --将转台旋转到正确的方向
    如果新角度(1)小于0,则
    player.cartart\u速度=炮塔速度
    如果新角度(-1)>0,则
    player.tarta\u速度=-tarta\u速度
    其他的
    --如果旋转转台一整帧将超调,则将转台设置为摄像机位置
    --atan2从左侧开始,我们还需要添加半个旋转。减去玩家方向以转换为本地空间。
    player.tarta=math.atan2(self.camera.direction.y,self.camera.direction.x)+(math.pi*1.5)-player.orientation.z
    player.tarta_速度=0
    终止
    终止
    局部方向=cpml.mat4():旋转(player.tartate,{0,0,1})*cpml.mat4():旋转(player.orientation.z,{0,0,1})
    player.tarta_direction=cpml.vec3(方向*{0,1,0,1})
    终止