Angles New

Pitch, yaw and head yaw of the cmd, the Source to Bedrock mapping, and silent angles with lock_angles.

A cmd carries the pitch and yaw of the view and the yaw of the head, in degrees: in createmove a change turns the camera and the player for real, in authinput it only changes the packet.

At a glance

Method Returns Purpose
get_view_angles() number, number, number pitch, yaw, roll (roll is always 0)
set_view_angles(pitch, yaw, roll) nothing sets pitch and yaw, and the head yaw to the same yaw
get_head_yaw() number the head yaw
set_head_yaw(yaw) nothing the head yaw alone
lock_angles() nothing make the angle changes of this cmd silent

Mapping from Source

Source QAngle Bedrock Notes
x pitch pitch degrees, -90 looks straight up, 90 straight down, 0 the horizon
y yaw yaw degrees, 0 faces +Z (south), 90 faces -X (west), -90 faces +X (east), 180 faces -Z (north); not wrapped, the game sends any value
z roll none Bedrock has no view roll: get_view_angles returns 0, set_view_angles refuses a roll other than 0 or nil
- head yaw yaw of the head, sent apart from the body; set_view_angles sets it to the new yaw

In authinput the angles are the pitch, yaw and Player Head Rotation fields of the packet. In createmove they are the player rotation (ActorRotationComponent, ActorHeadRotationComponent) and the first person camera (CameraDirectLookComponent).


Functions

cmd:get_view_angles(): number, number, number

cmd:get_view_angles(): number, number, number

Returns: pitch, yaw, 0, with the changes the callbacks made so far in this cmd.

local pitch, yaw = cmd:get_view_angles()

cmd:set_view_angles(pitch, yaw, roll)

cmd:set_view_angles(pitch: number, yaw: number, roll: number = 0)

Sets pitch and yaw, and the head yaw to yaw.

Name Type Description
pitch number degrees, from -90 to 90
yaw number degrees, any finite value
roll number must be 0 or omitted

In createmove the camera, the rotation and the head rotation are written after the callbacks: the client simulates the tick and builds the packet with the new angles, and the view stays there. When the camera of the player cannot be found (third person or a special camera), the rotation is written for this tick and the camera brings it back on the next one; stats().apply_failures counts it.

In authinput only the packet changes. The movement of the packet still points where the player looks: use rotate_movement or lock_angles so that the server moves you along the same path (see movement).

Returns: nothing.

Errors:

Message Cause
bad argument #1 to 'set_view_angles' (finite number expected) a pitch, yaw or roll that is NaN or infinite (#2 for the yaw, #3 for the roll)
bad argument #3 to 'set_view_angles' (roll is not supported (Bedrock has no view roll)) a roll other than 0
bad argument #1 to 'set_view_angles' (pitch must be within [-90, 90]) a pitch out of range
cmd:set_view_angles(0, 90)

cmd:get_head_yaw(): number

cmd:get_head_yaw(): number

Returns: the yaw of the head, in degrees.

print(cmd:get_head_yaw())

cmd:set_head_yaw(yaw)

cmd:set_head_yaw(yaw: number)

Turns the head only; the body and the view keep their yaw.

Name Type Description
yaw number degrees, any finite value

Returns: nothing.

Errors: bad argument #1 to 'set_head_yaw' (finite number expected).

local _, yaw = cmd:get_view_angles()
cmd:set_head_yaw(yaw + 45)

cmd:lock_angles()

cmd:lock_angles()

Makes the angle changes of this cmd silent: the server receives them, the view of the player does not move. Call it before or after set_view_angles; the flag holds for the whole cmd, so one call in any callback of the tick makes every angle change of that tick silent.

Layer Effect
createmove the camera and the rotation are not written; the new angles are carried to the PlayerAuthInputPacket of the same tick (they expire after 100 ms), and the movement in that packet, simulated with the real view, is rotated to the new yaw
authinput at the end of the callbacks, the movement is rotated from the yaw it was written for to the final yaw, as rotate_movement would

The rotation of the movement is what keeps the client and the server in agreement: the server applies the movement relative to the yaw it receives, so a silent yaw without that rotation would push you sideways and the server would correct you.

Returns: nothing.

cmd:lock_angles()
cmd:set_view_angles(0, 180)

Complete example

Look at the nearest player within 8 blocks for the server only, while the view stays where the player looks. The target is read in think because xiao.game returns nothing inside a cmd callback.

local target

xiao.signal.add("think", "look_target", function()
    target = nil
    local best = 8
    for _, p in ipairs(xiao.game.players()) do
        if p.distance ~= nil and p.distance < best then
            best, target = p.distance, {p.x, p.y, p.z}
        end
    end
end)

xiao.cmd.hook("createmove", "look", function(cmd)
    if target == nil then return end
    local x, y, z = cmd:get_position()
    local dx, dy, dz = target[1] - x, target[2] - y, target[3] - z
    local yaw = math.deg(math.atan2(-dx, dz))
    local pitch = math.deg(-math.atan2(dy, math.sqrt(dx * dx + dz * dz)))
    cmd:lock_angles()
    cmd:set_view_angles(math.max(-90, math.min(90, pitch)), yaw)
end)