diff --git a/client/Global.gd b/client/Global.gd index bb6fc6e..21c6ae6 100644 --- a/client/Global.gd +++ b/client/Global.gd @@ -7,6 +7,7 @@ var account: Account var camera: Camera var player: Player var players: PlayerManager +var terrain: Terrain3D var instance_id: int var interacting_with_ui: bool diff --git a/client/Main.tscn b/client/Main.tscn index 2f29395..9f3999f 100644 --- a/client/Main.tscn +++ b/client/Main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=34 format=3 uid="uid://b40y7iuskv1ar"] +[gd_scene load_steps=35 format=3 uid="uid://b40y7iuskv1ar"] [ext_resource type="Script" path="res://Main.gd" id="1_cw3ws"] [ext_resource type="Script" path="res://network/Client.gd" id="2_8hxcx"] @@ -18,6 +18,7 @@ [ext_resource type="Script" path="res://world/generator/Generator.gd" id="15_25nmg"] [ext_resource type="PackedScene" uid="uid://cb2t7bvvf3gwh" path="res://enemy/slime/Slime.tscn" id="15_hgl78"] [ext_resource type="Script" path="res://world/PlayerManager.gd" id="16_dp6bj"] +[ext_resource type="Script" path="res://world/Terrain.gd" id="16_icmfi"] [ext_resource type="PackedScene" uid="uid://dagn5bf7ou3sd" path="res://ui/UI.tscn" id="17_43qhq"] [ext_resource type="FastNoiseLite" uid="uid://d3f4lk8q04haa" path="res://world/trees/TreeNoise.tres" id="19_ctwmw"] [ext_resource type="Script" path="res://world/generator/TreeGenerator.gd" id="19_kcwnm"] @@ -32,13 +33,13 @@ [ext_resource type="Terrain3DMaterial" uid="uid://nhcs8ekjedbb" path="res://world/terrain/Material.tres" id="31_of12o"] [ext_resource type="Terrain3DTextureList" uid="uid://cpq0eq7wr08kf" path="res://world/terrain/TextureList.tres" id="32_d5ywf"] -[sub_resource type="FastNoiseLite" id="FastNoiseLite_xy4ll"] -seed = 300 - [sub_resource type="FastNoiseLite" id="FastNoiseLite_yp2fx"] seed = 100 frequency = 0.1 +[sub_resource type="FastNoiseLite" id="FastNoiseLite_xy4ll"] +seed = 300 + [node name="Main" type="Node" node_paths=PackedStringArray("ui")] script = ExtResource("1_cw3ws") ui = NodePath("UI") @@ -108,10 +109,22 @@ autostart = true [node name="World" type="Node3D" parent="."] -[node name="Terrain3D" type="Terrain3D" parent="World"] +[node name="Terrain" type="Terrain3D" parent="World"] storage = ExtResource("30_bvq0o") material = ExtResource("31_of12o") texture_list = ExtResource("32_d5ywf") +script = ExtResource("16_icmfi") + +[node name="Enemies" type="Node3D" parent="World"] +visible = false +script = ExtResource("15_25nmg") +scene = ExtResource("15_hgl78") +noise = SubResource("FastNoiseLite_yp2fx") +size_x = 100.0 +size_z = 100.0 +step = 2.0 +density = 0.3 +tilt = 0.15 [node name="Houses" type="Node3D" parent="World"] visible = false @@ -139,18 +152,7 @@ scale_min = 0.6 scale_max = 1.1 tilt = 0.15 -[node name="Enemies" type="Node3D" parent="World"] -visible = false -script = ExtResource("15_25nmg") -scene = ExtResource("15_hgl78") -noise = SubResource("FastNoiseLite_yp2fx") -size_x = 100.0 -size_z = 100.0 -step = 2.0 -density = 0.3 -tilt = 0.15 - -[node name="Follow" parent="World" instance=ExtResource("12_aljdh")] +[node name="Camera" parent="World" instance=ExtResource("12_aljdh")] [node name="Environment" type="WorldEnvironment" parent="World"] environment = ExtResource("14_kuej8") diff --git a/client/camera/Camera.gd b/client/camera/Camera.gd index 950f629..faf2daf 100644 --- a/client/camera/Camera.gd +++ b/client/camera/Camera.gd @@ -1,47 +1,141 @@ class_name Camera extends Camera3D +@export var center_offset := Vector3(0, 1.4, 0) +var center: Vector3 +var look_offset: Vector3 +var target_position: Vector3 + +@export_group("Collision") +@export var min_height_above_ground: float = 0 +@export_flags_3d_physics var collision_mask := 1 +var occlusion: Dictionary +var occlusion_distance := 0.0 + +@export_group("Rotation") +@export var sensitivity: float = 0.2 +@export var min_angle_x: float = -80.0 +@export var max_angle_x: float = 45.0 +var angle_x: float +var angle_y: float +var look_enabled: bool + @export_group("Zoom") @export var zoom_speed := 0.5 @export var zoom_interpolation := 10.0 -@export var zoom_min := 2.0 +@export var zoom_min := 2.5 @export var zoom_max := 8.0 - +var distance: float var target_distance: float func _ready(): - target_distance = position.z + distance = 7.5 + target_distance = distance + sensitivity = deg_to_rad(sensitivity) + min_angle_x = deg_to_rad(min_angle_x) + max_angle_x = deg_to_rad(max_angle_x) Global.camera = self + update_look_offset() + +func _unhandled_input(event): + if !event.is_action("look"): + return + + match event.is_pressed(): + true: + DisplayServer.mouse_set_mode(DisplayServer.MOUSE_MODE_CAPTURED) + look_enabled = true + false: + DisplayServer.mouse_set_mode(DisplayServer.MOUSE_MODE_VISIBLE) + look_enabled = false func _input(event): if event.is_action_pressed("zoom_in", true): target_distance -= zoom_speed on_distance_changed() get_viewport().set_input_as_handled() + return if event.is_action_pressed("zoom_out", true): target_distance += zoom_speed on_distance_changed() get_viewport().set_input_as_handled() + return + + if !look_enabled: + return + + if not event is InputEventMouseMotion: + return + + angle_x -= event.relative.y * sensitivity + angle_y -= event.relative.x * sensitivity + angle_x = clampf(angle_x, min_angle_x, max_angle_x) + + update_look_offset() + +func _process(delta): + if !Global.player: + return + + center = Global.player.position + center_offset + distance = Math.dampf(distance, target_distance, zoom_interpolation * delta) + target_position = center + look_offset * distance + + if min_height_above_ground > 0: + var result := check_terrain() + + if result && target_position.y - result.position.y < min_height_above_ground: + target_position.y = result.position.y + min_height_above_ground + + global_position = target_position + check_occlusion() + + if occlusion_distance < distance: + global_position = center + look_offset * occlusion_distance + + look_at(center) func on_distance_changed(): target_distance = clampf(target_distance, zoom_min, zoom_max) Global.camera_attributes.dof_blur_far_distance = target_distance + 1.0 -func is_below_terrain(): - return %CameraRays.occlusion_point.y > global_position.y +func update_look_offset(): + look_offset = get_offset(Vector3.BACK) -func _process(delta): - var distance := target_distance +func get_offset(vec: Vector3) -> Vector3: + vec = vec.rotated(Vector3.RIGHT, angle_x) + vec = vec.rotated(Vector3.UP, angle_y) + return vec - if %CameraRays.occlusion_distance < target_distance: - distance = %CameraRays.occlusion_distance - 0.5 +func check_terrain() -> Dictionary: + var space := get_world_3d().direct_space_state + var from := Vector3(target_position.x, 100, target_position.z) + var to := Vector3(target_position.x, -100, target_position.z) + var query := PhysicsRayQueryParameters3D.create(from, to, collision_mask) + return space.intersect_ray(query) - if is_below_terrain(): - position.z = distance - return +func check_occlusion(): + occlusion_distance = INF + var rect := get_viewport().get_visible_rect() + var space := get_world_3d().direct_space_state + send_ray_to_screen(rect.position, space) + send_ray_to_screen(Vector2(rect.end.x, rect.position.y), space) + send_ray_to_screen(rect.get_center(), space) + send_ray_to_screen(Vector2(rect.position.x, rect.end.y), space) + send_ray_to_screen(rect.end, space) - if abs(distance - position.z) < 0.01: +func send_ray_to_screen(point: Vector2, space: PhysicsDirectSpaceState3D): + var screen_position := project_position(point, near) + var query := PhysicsRayQueryParameters3D.create(center, screen_position, collision_mask) + var hit := space.intersect_ray(query) + + if !hit: return - position.z = Math.dampf(position.z, distance, zoom_interpolation * delta) + var hit_point := hit.position as Vector3 + var hit_distance := (hit_point - center).length() + + if hit_distance < occlusion_distance: + occlusion = hit + occlusion_distance = hit_distance diff --git a/client/camera/Camera.tscn b/client/camera/Camera.tscn index 24fc3cc..8bfc74f 100644 --- a/client/camera/Camera.tscn +++ b/client/camera/Camera.tscn @@ -1,28 +1,12 @@ -[gd_scene load_steps=6 format=3 uid="uid://cpdoq0oh84mfw"] +[gd_scene load_steps=3 format=3 uid="uid://cpdoq0oh84mfw"] -[ext_resource type="Script" path="res://camera/FollowPlayer.gd" id="1_48rtd"] -[ext_resource type="Script" path="res://camera/CameraPivot.gd" id="2_dylfm"] [ext_resource type="Script" path="res://camera/Camera.gd" id="2_pwdc2"] [ext_resource type="CameraAttributesPractical" uid="uid://b835orxyqq6w5" path="res://camera/CameraPractical.tres" id="3_olar0"] -[ext_resource type="Script" path="res://camera/CameraRays.gd" id="5_5b07p"] -[node name="Follow" type="Marker3D"] -script = ExtResource("1_48rtd") - -[node name="Pivot" type="Marker3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.4, 0) -script = ExtResource("2_dylfm") -sensitivity = 0.2 - -[node name="Camera" type="Camera3D" parent="Pivot"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 7.5) +[node name="Camera" type="Camera3D"] attributes = ExtResource("3_olar0") current = true fov = 45.0 size = 10.0 far = 120.0 script = ExtResource("2_pwdc2") - -[node name="CameraRays" type="Node3D" parent="Pivot"] -unique_name_in_owner = true -script = ExtResource("5_5b07p") diff --git a/client/camera/CameraPivot.gd b/client/camera/CameraPivot.gd deleted file mode 100644 index cee9689..0000000 --- a/client/camera/CameraPivot.gd +++ /dev/null @@ -1,32 +0,0 @@ -extends Node3D - -@export var sensitivity: float -@export var min_rotation_x: float = -80.0 -@export var max_rotation_x: float = 25.0 - -var enabled: bool - -func _ready(): - assert(rotation_order == EULER_ORDER_YXZ) - min_rotation_x = deg_to_rad(min_rotation_x) - max_rotation_x = deg_to_rad(max_rotation_x) - -func _unhandled_input(event): - if event.is_action_pressed("look"): - DisplayServer.mouse_set_mode(DisplayServer.MOUSE_MODE_CAPTURED) - enabled = true - elif event.is_action_released("look"): - DisplayServer.mouse_set_mode(DisplayServer.MOUSE_MODE_VISIBLE) - enabled = false - -func _input(event): - if !enabled: - return - - if not event is InputEventMouseMotion: - return - - rotation.x += deg_to_rad(-event.relative.y * sensitivity) - rotation.y += deg_to_rad(-event.relative.x * sensitivity) - - rotation.x = clampf(rotation.x, min_rotation_x, max_rotation_x) \ No newline at end of file diff --git a/client/camera/CameraRays.gd b/client/camera/CameraRays.gd deleted file mode 100644 index 6452f09..0000000 --- a/client/camera/CameraRays.gd +++ /dev/null @@ -1,38 +0,0 @@ -class_name CameraRays -extends Node3D - -@export_flags_3d_physics var collision_mask := 1 - -var occlusion_distance: float = 1000 -var occlusion_point: Vector3 -var pivot: Node3D - -func _ready(): - pivot = get_parent() - -func _physics_process(_delta): - occlusion_distance = Global.camera.far - - var corner: Vector2 = get_viewport().get_visible_rect().size - var space_state = get_world_3d().direct_space_state - - send_ray(space_state, Vector2(0, 0)) - send_ray(space_state, Vector2(corner.x, 0)) - send_ray(space_state, Vector2(0, corner.y)) - send_ray(space_state, Vector2(corner.x, corner.y)) - send_ray(space_state, Vector2(corner.x / 2, corner.y / 2)) - -func send_ray(space_state: PhysicsDirectSpaceState3D, pos: Vector2): - var from := pivot.global_position - var to := Global.camera.project_position(pos, -1) - var query = PhysicsRayQueryParameters3D.create(from, to, collision_mask) - var result = space_state.intersect_ray(query) - - if result: - var hit_point: Vector3 = result.position - var diff := hit_point - pivot.global_position - var distance := diff.length() - - if distance < occlusion_distance: - occlusion_distance = distance - occlusion_point = hit_point \ No newline at end of file diff --git a/client/camera/FollowPlayer.gd b/client/camera/FollowPlayer.gd deleted file mode 100644 index bcba24d..0000000 --- a/client/camera/FollowPlayer.gd +++ /dev/null @@ -1,14 +0,0 @@ -extends Node3D - -@export var interpolate: bool -@export var speed: float - -func _process(delta): - if Global.player == null: - return - - if interpolate: - position = Math.damp(position, Global.player.position, speed * delta) - else: - position = Global.player.position - diff --git a/client/math/Math.gd b/client/math/Math.gd index 0fabb6e..63a9580 100644 --- a/client/math/Math.gd +++ b/client/math/Math.gd @@ -9,6 +9,9 @@ static func dampf(from: float, to: float, weight: float, smoothing: float = 0.75 static func damp_angle(from: float, to: float, weight: float, smoothing: float = 0.75): return lerp_angle(from, to, 1 - exp(-smoothing * weight)) +static func damp_spherical(from: Vector3, to: Vector3, weight: float, smoothing: float = 0.75): + return from.slerp(to, 1 - exp(-smoothing * weight)) + static func from_to_rotation(from: Vector3, to: Vector3) -> Quaternion: var axis := from.cross(to).normalized() var angle := from.angle_to(to) diff --git a/client/project.godot b/client/project.godot index 410ff6f..bc1ddcb 100644 --- a/client/project.godot +++ b/client/project.godot @@ -31,7 +31,6 @@ gdscript/warnings/integer_division=0 window/size/viewport_width=1920 window/size/viewport_height=1080 -window/size/mode=3 window/size/window_width_override=960 window/size/window_height_override=540 window/stretch/mode="canvas_items" diff --git a/client/world/Terrain.gd b/client/world/Terrain.gd new file mode 100644 index 0000000..0648710 --- /dev/null +++ b/client/world/Terrain.gd @@ -0,0 +1,4 @@ +extends Terrain3D + +func _ready(): + Global.terrain = self \ No newline at end of file