Écran partagé

Deux SubViewports côte à côte avec une caméra chacun, tous deux tournés vers le même World3D. Chaque volet suit son propre cube.

L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.

Classes Godot utilisées

SubViewportSubViewportContainerCamera3D

Le code

scripts/main.gd
extends Node

const SPEED: float = 6.0

var blue: Node3D
var green: Node3D
var blue_camera: Camera3D
var green_camera: Camera3D
var elapsed: float = 0.0
var manual: bool = false


func _ready() -> void:
	_add_environment()
	_add_light()
	_add_arena()

	blue = _add_runner(Color('#8da5f3'))
	green = _add_runner(Color('#8eef97'))

	var row: HBoxContainer = HBoxContainer.new()
	row.add_theme_constant_override('separation', 6)
	add_child(row)
	row.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

	blue_camera = _add_pane(row)
	green_camera = _add_pane(row)

	_add_hud()
	_place_cameras()

	print('two SubViewports share one World3D, wasd drives the blue cube and the arrows the green one')


func _process(delta: float) -> void:
	elapsed += delta

	var left: Vector3 = Vector3(
		float(Input.is_key_pressed(KEY_D)) - float(Input.is_key_pressed(KEY_A)),
		0.0,
		float(Input.is_key_pressed(KEY_S)) - float(Input.is_key_pressed(KEY_W))
	)
	var right: Vector3 = Vector3(
		float(Input.is_key_pressed(KEY_RIGHT)) - float(Input.is_key_pressed(KEY_LEFT)),
		0.0,
		float(Input.is_key_pressed(KEY_DOWN)) - float(Input.is_key_pressed(KEY_UP))
	)

	if left != Vector3.ZERO or right != Vector3.ZERO:
		manual = true

	if manual:
		blue.position += left.normalized() * SPEED * delta
		green.position += right.normalized() * SPEED * delta
	else:
		blue.position = Vector3(cos(elapsed * 0.7) * 7.0, 0.7, sin(elapsed * 0.7) * 7.0)
		green.position = Vector3(cos(-elapsed * 0.5 + 2.2) * 4.5, 0.7, sin(-elapsed * 0.5 + 2.2) * 4.5)

	blue.rotation.y = elapsed * 1.4
	green.rotation.y = -elapsed * 1.4

	_place_cameras()


func _place_cameras() -> void:
	_aim(blue_camera, blue, Vector3(0, 4.5, 8))
	_aim(green_camera, green, Vector3(6, 5.5, -6))


func _aim(camera: Camera3D, target: Node3D, offset: Vector3) -> void:
	camera.position = target.position + offset
	camera.look_at(target.position, Vector3.UP)


func _add_pane(row: HBoxContainer) -> Camera3D:
	var container: SubViewportContainer = SubViewportContainer.new()
	container.stretch = true
	container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	container.size_flags_vertical = Control.SIZE_EXPAND_FILL
	row.add_child(container)

	var pane: SubViewport = SubViewport.new()
	pane.render_target_update_mode = SubViewport.UPDATE_ALWAYS
	pane.msaa_3d = Viewport.MSAA_4X
	container.add_child(pane)

	var camera: Camera3D = Camera3D.new()
	pane.add_child(camera)
	camera.current = true

	return camera


func _add_runner(color: Color) -> Node3D:
	var mesh: BoxMesh = BoxMesh.new()
	mesh.size = Vector3(1.4, 1.4, 1.4)

	var material: StandardMaterial3D = StandardMaterial3D.new()
	material.albedo_color = color
	material.roughness = 0.4

	var runner: MeshInstance3D = MeshInstance3D.new()
	runner.mesh = mesh
	runner.material_override = material
	add_child(runner)

	return runner


func _add_arena() -> void:
	var ground_mesh: PlaneMesh = PlaneMesh.new()
	ground_mesh.size = Vector2(50, 50)

	var ground_material: StandardMaterial3D = StandardMaterial3D.new()
	ground_material.albedo_color = Color('#2a2f3d')

	var ground: MeshInstance3D = MeshInstance3D.new()
	ground.mesh = ground_mesh
	ground.material_override = ground_material
	add_child(ground)

	var pillar_mesh: BoxMesh = BoxMesh.new()
	pillar_mesh.size = Vector3(1.2, 4, 1.2)

	var pillar_material: StandardMaterial3D = StandardMaterial3D.new()
	pillar_material.albedo_color = Color('#fc7f7f')
	pillar_material.roughness = 0.6

	for i in 14:
		var angle: float = TAU * i / 14.0

		var pillar: MeshInstance3D = MeshInstance3D.new()
		pillar.mesh = pillar_mesh
		pillar.material_override = pillar_material
		pillar.position = Vector3(cos(angle), 0, sin(angle)) * 17.0 + Vector3(0, 2, 0)
		add_child(pillar)


func _add_hud() -> void:
	var hud: CanvasLayer = CanvasLayer.new()
	hud.layer = 2
	add_child(hud)

	var view: Vector2 = get_viewport().get_visible_rect().size

	for item in [{ text = 'P1  wasd', color = Color('#8da5f3'), x = 40.0 }, { text = 'P2  arrows', color = Color('#8eef97'), x = view.x * 0.5 + 40.0 }]:
		var label: Label = Label.new()
		label.text = item.text
		label.add_theme_font_size_override('font_size', 24)
		label.add_theme_color_override('font_color', item.color)
		label.position = Vector2(item.x, 30)
		hud.add_child(label)


func _add_environment() -> void:
	var sky_material: ProceduralSkyMaterial = ProceduralSkyMaterial.new()
	sky_material.sky_top_color = Color(0.05, 0.06, 0.12)
	sky_material.sky_horizon_color = Color(0.14, 0.16, 0.24)
	sky_material.ground_bottom_color = Color(0.02, 0.02, 0.04)
	sky_material.ground_horizon_color = Color(0.1, 0.11, 0.16)

	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_COLOR
	env.ambient_light_color = Color(0.44, 0.48, 0.6)
	env.ambient_light_energy = 0.7

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


func _add_light() -> void:
	var light: DirectionalLight3D = DirectionalLight3D.new()
	light.rotation_degrees = Vector3(-70, -35, 0)
	light.light_energy = 1.2
	light.shadow_enabled = true
	add_child(light)

Plus d'exemples de Caméra