#view.origin() → vec3
Where the camera is.
Read and move the camera, change the field of view, and turn a point in the world into a point on the screen.
Two things belong here: the camera itself - where it is, which way it looks, how wide - and projection: finding where a point in the world lands on your screen, which is how a script draws a label above a player's head.
-- a dot above every enemy's head
on( "frame", function( canvas )
for _, player in ipairs( ents.players( { enemies = true, alive = true } ) ) do
local head = player:bone( ents.bone.head )
if head then
local x, y, on_screen = view.project( head + vec3( 0, 0, 12 ) )
if on_screen then
canvas:disc( x, y, 3, color( 244, 63, 94 ) )
end
end
end
end )Angles are vec3s of degrees: x is the pitch (positive looks down, negative up), y the yaw (0 looks along +X, 90 along +Y, counter-clockwise seen from above), z the roll, which is 0 in practice. Positions are world units. vec3:forward(), right() and up() turn angles into direction vectors.
view.origin() → vec3Where the camera is.
view.angles() → vec3Which way it looks.
view.fov() → numberThe field of view in degrees.
view.ready() → booleanWhether the game has produced a camera yet. Before it has - on the main menu, while a map loads - the other calls have nothing meaningful to answer, and project answers nil.
view.project( position ) → x, y, on_screen, depth | nilWhere a world point lands on the screen. position is a vec3. Returns four values:
x, y - the pixel the point projects to, from the top left corner of the screen.on_screen - true when the point is in front of the camera and inside the picture. When it is false the point is behind the camera or outside the view, and x and y are not meaningful - do not draw there.depth - how far in front of the camera the point is, roughly in world units. Use it to scale things with distance.Returns nil instead when there is no camera yet.
local x, y, on_screen, depth = view.project( point )
if on_screen then
local size = math.clamp( 600 / depth, 4, 24 ) -- smaller when farther away
canvas:disc( x, y, size, color( 255, 255, 255 ) )
endThe view event hands a script the camera as it is about to be rendered, after the cheat's own camera work. The object it receives reads and writes the camera directly, so a change made there is what the game draws. It is only valid inside the handler - using one you kept raises this view is only valid inside the view event that received it.
on( "view", function( view )
view:set_fov( 105 )
end )v:origin() → vec3The camera's position.
v:set_origin( position )Moves the camera. The player does not move - only what you see does.
v:angles() → vec3The camera's angles.
v:set_angles( angles )Turns the camera without turning the player: the game's picture changes, the command and the server's view of where you look do not.
v:fov() → numberv:set_fov( degrees )Sets the field of view, clamped to 1 to 170 degrees.
-- a third-person camera: 80 units behind the head
on( "view", function( view )
local forward = view:angles( ):forward( )
view:set_origin( view:origin( ) - forward * 80 )
end )A name tag that scales with distance and fades out at range:
local font = render.font( "Segoe UI", 14 )
on( "frame", function( canvas )
local me = ents.local_pawn( )
if not me or not font then return end
for _, player in ipairs( ents.players( { enemies = true, alive = true } ) ) do
local head = player:bone( ents.bone.head )
if head then
local x, y, on_screen, depth = view.project( head + vec3( 0, 0, 14 ) )
if on_screen then
local fade = math.clamp( 1 - depth / 2500, 0.15, 1 )
local label = string.format( "%s %d", player:name( ), player:health( ) )
canvas:text( x, y, label, color( 255, 255, 255, 255 * fade ),
{ font = font, align = "center", valign = "bottom", shadow = true } )
end
end
end
end )