Câmera em órbita

Arraste com o botão esquerdo para girar a câmera em volta da peça central, e use a roda para dar zoom. Yaw, pitch e distância são todo o estado.

O editor acima está rodando este projeto. Mude uma linha e ele recarrega.

Classes do Godot usadas

Camera3DInputEventMouseMotionInputEventMouseButton

O código

scripts/main.gd
extends Node

const TARGET: Vector3 = Vector3(0, 1.4, 0)
const SENSITIVITY: float = 0.007
const PITCH_LIMIT: float = 1.35
const ZOOM_STEP: float = 0.7

var camera: Camera3D
var yaw: float = 0.7
var pitch: float = 0.4
var distance: float = 9.8


func _ready() -> void:
	_add_environment()
	_add_light()
	_add_ground()
	_add_centerpiece()
	_add_pillars()

	camera = Camera3D.new()
	add_child(camera)
	camera.current = true
	_place_camera()

	print('drag with the left button to orbit the torus, and the wheel zooms in and out')


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		var motion: InputEventMouseMotion = event

		if motion.button_mask & MOUSE_BUTTON_MASK_LEFT:
			yaw -= motion.relative.x * SENSITIVITY
			# beyond 90 degrees the orbit direction flips and look_at goes singular against Vector3.UP
			pitch = clamp(pitch - motion.relative.y * SENSITIVITY, -PITCH_LIMIT, PITCH_LIMIT)
			_place_camera()

	if event is InputEventMouseButton:
		var click: InputEventMouseButton = event

		if not click.pressed:
			return

		if click.button_index == MOUSE_BUTTON_WHEEL_UP:
			distance = clamp(distance - ZOOM_STEP, 4.0, 24.0)
			_place_camera()
		elif click.button_index == MOUSE_BUTTON_WHEEL_DOWN:
			distance = clamp(distance + ZOOM_STEP, 4.0, 24.0)
			_place_camera()


func _place_camera() -> void:
	var offset: Vector3 = Vector3(cos(pitch) * sin(yaw), sin(pitch), cos(pitch) * cos(yaw))

	camera.position = TARGET + offset * distance
	camera.look_at(TARGET, Vector3.UP)


func _add_centerpiece() -> void:
	var base: CylinderMesh = CylinderMesh.new()
	base.top_radius = 1.1
	base.bottom_radius = 1.5
	base.height = 1.0

	var pedestal: MeshInstance3D = MeshInstance3D.new()
	pedestal.mesh = base
	pedestal.material_override = _make_material('#3a4152', 0.6)
	pedestal.position = Vector3(0, 0.5, 0)
	add_child(pedestal)

	var ring: TorusMesh = TorusMesh.new()
	ring.inner_radius = 0.7
	ring.outer_radius = 1.25

	var material: StandardMaterial3D = _make_material('#f3d48d', 0.25)
	material.metallic = 0.7
	material.emission_enabled = true
	material.emission = Color('#f3d48d')
	material.emission_energy_multiplier = 0.5

	var torus: MeshInstance3D = MeshInstance3D.new()
	torus.mesh = ring
	torus.material_override = material
	torus.position = Vector3(0, 2.0, 0)
	torus.rotation_degrees = Vector3(-25, 0, 0)
	add_child(torus)


func _add_pillars() -> void:
	var colors: Array[String] = ['#8da5f3', '#8eef97', '#fc7f7f', '#8da5f3', '#8eef97', '#fc7f7f']

	for i in colors.size():
		var turn: float = TAU * i / colors.size()
		var height: float = 1.4 + float(i % 3) * 0.8

		var mesh: BoxMesh = BoxMesh.new()
		mesh.size = Vector3(0.9, height, 0.9)

		var pillar: MeshInstance3D = MeshInstance3D.new()
		pillar.mesh = mesh
		pillar.material_override = _make_material(colors[i], 0.5)
		pillar.position = Vector3(cos(turn), 0, sin(turn)) * 5.2 + Vector3(0, height * 0.5, 0)
		add_child(pillar)


func _add_ground() -> void:
	var mesh: PlaneMesh = PlaneMesh.new()
	mesh.size = Vector2(120, 120)

	var ground: MeshInstance3D = MeshInstance3D.new()
	ground.mesh = mesh
	ground.material_override = _make_material('#242938', 0.95)
	add_child(ground)


func _make_material(hex: String, roughness: float) -> StandardMaterial3D:
	var material: StandardMaterial3D = StandardMaterial3D.new()
	material.albedo_color = Color(hex)
	material.roughness = roughness

	return material


func _add_environment() -> void:
	var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
	sky_material.sky_top_color = Color(0.07, 0.09, 0.18)
	sky_material.sky_horizon_color = Color(0.26, 0.29, 0.4)
	sky_material.ground_bottom_color = Color(0.15, 0.17, 0.23)
	sky_material.ground_horizon_color = Color(0.18, 0.2, 0.28)

	var sky: Sky = Sky.new()
	sky.sky_material = sky_material

	var env: Environment = Environment.new()
	env.background_mode = Environment.BG_SKY
	env.sky = sky
	env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
	env.ambient_light_energy = 0.8

	var world: WorldEnvironment = WorldEnvironment.new()
	world.environment = env
	add_child(world)


func _add_light() -> void:
	var light: DirectionalLight3D = DirectionalLight3D.new()
	light.rotation_degrees = Vector3(-55, -40, 0)
	light.light_energy = 1.3
	light.shadow_enabled = true
	add_child(light)

Mais exemplos de Cena 3D