diff --git a/project.godot b/project.godot index 2e3eafe..9f35f31 100644 --- a/project.godot +++ b/project.godot @@ -9,6 +9,11 @@ config_version=4 _global_script_classes=[ { +"base": "Camera", +"class": "CameraController", +"language": "GDScript", +"path": "res://utils/camera/CameraController.gd" +}, { "base": "Reference", "class": "Delaunator", "language": "GDScript", @@ -25,6 +30,7 @@ _global_script_classes=[ { "path": "res://utils/terrain/Terrain.gd" } ] _global_script_class_icons={ +"CameraController": "", "Delaunator": "", "PoissonDiscSampling": "", "Terrain": "" @@ -40,6 +46,19 @@ config/icon="res://icon.png" common/drop_mouse_on_gui_input_disabled=true +[input] + +main_command={ +"deadzone": 0.5, +"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":2,"pressed":false,"doubleclick":false,"script":null) + ] +} +alt_command={ +"deadzone": 0.5, +"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null) + ] +} + [physics] common/enable_pause_aware_picking=true diff --git a/utils/camera/CamBase.gd b/utils/camera/CamBase.gd new file mode 100644 index 0000000..111b695 --- /dev/null +++ b/utils/camera/CamBase.gd @@ -0,0 +1,98 @@ +extends Spatial + +const MOVE_MARGIN = 20 +const MOVE_SPEED = 30 + +const ray_length = 1000 +onready var cam = $Camera + +var team = 0 +var selected_units = [] +onready var selection_box = $SelectionBox +var start_sel_pos = Vector2() + +func _process(delta): + var m_pos = get_viewport().get_mouse_position() +# calc_move(m_pos, delta) + if Input.is_action_just_pressed("main_command"): + move_selected_units(m_pos) + if Input.is_action_just_pressed("alt_command"): + selection_box.start_sel_pos = m_pos + start_sel_pos = m_pos + if Input.is_action_pressed("alt_command"): + selection_box.m_pos = m_pos + selection_box.is_visible = true + else: + selection_box.is_visible = false + if Input.is_action_just_released("alt_command"): + select_units(m_pos) + +func calc_move(m_pos, delta): + var v_size = get_viewport().size + var move_vec = Vector3() + if m_pos.x < MOVE_MARGIN: + move_vec.x -= 1 + if m_pos.y < MOVE_MARGIN: + move_vec.z -= 1 + if m_pos.x > v_size.x - MOVE_MARGIN: + move_vec.x += 1 + if m_pos.y > v_size.y - MOVE_MARGIN: + move_vec.z += 1 + move_vec = move_vec.rotated(Vector3(0, 1, 0), rotation_degrees.y) + global_translate(move_vec * delta * MOVE_SPEED) + +func move_selected_units(m_pos): + var result = raycast_from_mouse(m_pos, 1) + if result: + var unit_index = 0 + for unit in selected_units: + var new_position = result.position + new_position.x = new_position.x + unit_index + unit.move_to(new_position) + unit_index += 1 + +func select_units(m_pos): + var new_selected_units = [] + if m_pos.distance_squared_to(start_sel_pos) < 16: + var u = get_unit_under_mouse(m_pos) + if u != null: + new_selected_units.append(u) + else: + new_selected_units = get_units_in_box(start_sel_pos, m_pos) + + for unit in selected_units: + unit.deselect() + for unit in new_selected_units: + unit.select() + selected_units = new_selected_units + +func get_unit_under_mouse(m_pos): + var result = raycast_from_mouse(m_pos, 3) + if result and result.collider.is_in_group("units"): + return result.collider + +func get_units_in_box(top_left, bot_right): + if top_left.x > bot_right.x: + var tmp = top_left.x + top_left.x = bot_right.x + bot_right.x = tmp + if top_left.y > bot_right.y: + var tmp = top_left.y + top_left.y = bot_right.y + bot_right.y = tmp + var box = Rect2(top_left, bot_right - top_left) + var box_selected_units = [] + for unit in get_tree().get_nodes_in_group("units"): + if box.has_point(cam.unproject_position(unit.global_transform.origin)): + box_selected_units.append(unit) + return box_selected_units + +func raycast_from_mouse(m_pos, collision_mask): + var ray_start = cam.project_ray_origin(m_pos) + var ray_end = ray_start + cam.project_ray_normal(m_pos) * ray_length + var space_state = get_world().direct_space_state + return space_state.intersect_ray(ray_start, ray_end, [], collision_mask) + + +func _on_Camera_camera_moved(new_location): + pass # Replace with function body. diff --git a/utils/camera/CamBase.tscn b/utils/camera/CamBase.tscn new file mode 100644 index 0000000..78190c1 --- /dev/null +++ b/utils/camera/CamBase.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://utils/camera/SelectionBox.gd" type="Script" id=1] +[ext_resource path="res://utils/camera/CamBase.gd" type="Script" id=2] +[ext_resource path="res://utils/camera/CameraController.gd" type="Script" id=3] +[ext_resource path="res://utils/camera/CameraInput.gd" type="Script" id=4] + +[node name="CamBase" type="Spatial"] +transform = Transform( 1, 0, 0, 0, 0.34202, 0.939693, 0, -0.939693, 0.34202, 0, 0, 0 ) +script = ExtResource( 2 ) + +[node name="Camera" type="Camera" parent="."] +size = 20.0 +near = 0.01 +far = 8192.0 +script = ExtResource( 3 ) +movement_speed = 70.017 +min_zoom = 51.0 +zoom_sensibility = 2.818 +rotation_sensibility = 1.0 + +[node name="Node" type="Node" parent="Camera"] +script = ExtResource( 4 ) + +[node name="SelectionBox" type="Control" parent="."] +margin_right = 40.0 +margin_bottom = 40.0 +script = ExtResource( 1 ) + +[connection signal="on_change_action" from="Camera/Node" to="Camera" method="change_action"] +[connection signal="on_change_velocity" from="Camera/Node" to="Camera" method="change_velocity"] +[connection signal="on_rotate_view" from="Camera/Node" to="Camera" method="rotate_view"] +[connection signal="on_zoom" from="Camera/Node" to="Camera" method="zoom"] diff --git a/utils/camera/CameraController.gd b/utils/camera/CameraController.gd new file mode 100644 index 0000000..f5c571c --- /dev/null +++ b/utils/camera/CameraController.gd @@ -0,0 +1,140 @@ +extends Camera +class_name CameraController + +signal camera_moved(new_location) + +enum CAMERA_ACTIONS{ + MOVING, + ROTATING_VIEW, +} + +export(float,1,100) var movement_speed = 30 +export(float,0.01,0.99) var movement_damping = 0.74 +export(float,0.01, 3.1415) var max_rotation = 1.2 +export(float,0.01, 3.1415) var min_rotation = 0.5 + +#Value in percentage of screen portion +#A value of 0.3 means that when you place the cursor 30% or less away from an edge it will start pushing the camera +export(float, 0.0,1.0) var edge_size = 0.0 + +#EDIT HERE--->**,***<--- ZOOM MIN AND MAX LIMITS +export(float, 10,100) var min_zoom = 25 +export(float, 10,100) var max_zoom = 100 + +export(float, 1,3) var zoom_sensibility = 2.5 + +export(float, 1,3) var rotation_sensibility = 2.3 +export(float, 1.0, 10.0) var height = 5.0 +var pitch : float +var yaw : float +var current_action = CAMERA_ACTIONS.MOVING +var velocity : Vector2 + +func _ready(): +# Input.set_mouse_mode(Input.MOUSE_MODE_CONFINED) + + pitch = rotation.x + yaw = rotation.y + +# var new_rotation = (max_zoom - fov) * (max_rotation - min_rotation) / max_zoom + min_rotation + transform.basis = Basis(Vector3(1, 0, 0), (min_rotation + max_rotation) / 2.0) + fov = (min_zoom + max_zoom) / 2.0 + +func change_action(action): + current_action = action + match(current_action): +# CAMERA_ACTIONS.MOVING: +# Input.set_mouse_mode(Input.MOUSE_MODE_CONFINED) + CAMERA_ACTIONS.ROTATING_VIEW: + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + +func _process(delta): + + match(current_action): + CAMERA_ACTIONS.MOVING: + #CAMERA MOVEMENT + velocity.x = clamp(velocity.x * movement_damping,-1.0,1.0) + velocity.y = clamp(velocity.y * movement_damping,-1.0,1.0) + + if velocity != Vector2.ZERO: + move(velocity) + + +func change_velocity(_velocity : Vector2): + velocity = _velocity + +func move(_velocity : Vector2): + #Move along cameras X axis + global_transform.origin += global_transform.basis.x * velocity.x * movement_speed * get_process_delta_time() + #Calculate a forward camera direction that is perpendicular to the XZ plane + var forward = global_transform.basis.x.cross(Vector3.UP) + #Move the camera along that forward direction + global_transform.origin += forward * velocity.y * movement_speed * get_process_delta_time() + + var y_offset = 0 + var space_state = get_world().direct_space_state + var result = space_state.intersect_ray(Vector3(global_transform.origin.x, 100, global_transform.origin.z), Vector3(global_transform.origin.x, 0, global_transform.origin.z)) + if result: + y_offset = result.position.y + else: + y_offset = 0 + global_transform.origin.y = max(10 + y_offset * 1.3, 30) + + emit_signal("camera_moved", global_transform.origin) + + +func zoom(direction : float): + #Zooming using fov + var new_fov = fov + (sign(direction) * pow(abs(direction),zoom_sensibility)/100 * get_process_delta_time()) + fov = clamp(new_fov,min_zoom,max_zoom) + + # Linear equation + var slope = (min_rotation - max_rotation) / (max_zoom - min_zoom) + var b = max_rotation - slope * min_zoom + var new_rotation = slope * fov + b + transform.basis = Basis(Vector3(1, 0, 0), new_rotation) + + +func rotate_view(axis : Vector2): + + var pitch_rotation_amount = -axis.y/100 * get_process_delta_time() * rotation_sensibility + var yaw_rotation_amount = -axis.x/100 * get_process_delta_time() * rotation_sensibility + + pitch += pitch_rotation_amount + pitch = clamp(pitch,-PI/2,0) + + yaw += yaw_rotation_amount + + rotation.x = pitch + rotation.y = yaw + +func _on_Map_map_clicked(position): + global_transform.origin.x = position.x + global_transform.origin.z = position.y + + + var y_offset = 0 + var space_state = get_world().direct_space_state + var result = space_state.intersect_ray(Vector3(global_transform.origin.x, 100, global_transform.origin.z), Vector3(global_transform.origin.x, 0, global_transform.origin.z)) + if result: + y_offset = result.position.y + else: + y_offset = 0 + global_transform.origin.y = max(height + y_offset * 1.3, 30) + pass # Replace with function body. + + +func _on_World_character_created(position): + global_transform.origin.x = position.x + global_transform.origin.z = position.y + + + var y_offset = 0 + var space_state = get_world().direct_space_state + var result = space_state.intersect_ray(Vector3(global_transform.origin.x, 100, global_transform.origin.z), Vector3(global_transform.origin.x, 0, global_transform.origin.z)) + if result: + y_offset = result.position.y + else: + y_offset = 0 + global_transform.origin.y = max(height + y_offset * 1.3, 30) + pass # Replace with function body. diff --git a/utils/camera/CameraInput.gd b/utils/camera/CameraInput.gd new file mode 100644 index 0000000..4c0e185 --- /dev/null +++ b/utils/camera/CameraInput.gd @@ -0,0 +1,158 @@ +extends Node + +export(float,0.001,1.0) var screen_edge_size : float = 0.3 +export(float) var mouse_wheel_damping = 0.9 + +#USED TO CALCULATE RAW MOVEMENT WHILE PUSHING AN EDGE +var horizontal : float = 0.0 +var vertical : float = 0.0 + +#USED TO STORE MOUSE WHEEL INERTIA TO ENABLE SMOOTH STOPPING +var mouse_wheel : float = 0.0 + +signal on_change_velocity(velocity) +signal on_rotate_view(relative) +signal on_change_action(new_state) +signal on_zoom(value) + +var current_action + +#TOUCH VARIABLE FOR MOBILE +var touch_count : int = 0 +var swipe_start : Vector2 + +func _ready(): + connect("on_change_action",self,"change_action") + emit_signal("on_change_action",CameraController.CAMERA_ACTIONS.MOVING) + +func change_action(action): + current_action = action + +func toggle_action(): + current_action = 1 - current_action + +func start_swipe(position : Vector2): + swipe_start = position + +func move_swipe(position : Vector2): + if blocked_movement: + return + + var delta = (position - swipe_start)*-1 + var direction_x = sign(delta.x) + var direction_y = sign(delta.y) + + var view_size = get_viewport().get_visible_rect().size - Vector2.ONE + + horizontal = range_lerp(abs(delta.x),0,view_size.x,0.0,1.0) + vertical = range_lerp(abs(delta.y),0,view_size.y,0.0,1.0) + + #Applies direction + horizontal *= direction_x + vertical *= direction_y + +var touches = [ Vector2.ZERO, Vector2.ZERO ] +var start_pinch_distance : float +var last_pinch_distance : float +var pinching : float + +var blocked_movement : bool = false + +func _input(event): + + if OS.get_name() == "Android" or OS.get_name() == "iOS": + #MOBILE############## + if event is InputEventScreenTouch: + #SET TOUCH STARTING POSTIION + touches[event.index] = event.position + if event.pressed: + touch_count += 1 + start_swipe(event.position) + else: + touch_count -= 1 + #RENABLE SWIPE MOVEMENT BECAUSE EVERY TOUCH WAS LIFTED + if blocked_movement and touch_count <= 0: + blocked_movement = false + + #RESET PINCHING VALUE WHEN A NEW TOUCH IS DETECTED OR HAS BEEN LIFTED + pinching = 0.0 + if touch_count == 2: + #STARTED ZOOMING, BLOCK MOVEMENT UNTIL EVERY TOUCH IS LIFTED + blocked_movement = true + start_pinch_distance = (touches[1] - touches[0]).length() + + if event is InputEventScreenDrag: + if touch_count == 2: + #UPDATE TOUCHES POSITIONS + touches[event.index] = event.position + #CALCULATE DISTANCE BETWEEN TOUCHES + var pinch_distance = (touches[1] - touches[0]).length() + var pinch_direction = 1 if pinch_distance > last_pinch_distance else -1 + #CALCULATE PINCH DELTA + pinching = abs(start_pinch_distance - pinch_distance) * pinch_direction + #USE MOUSE WHEEL BUFFER TO ENABLE SMOOTHING + mouse_wheel += pinching * get_process_delta_time() + last_pinch_distance = pinch_distance + else: + if current_action == CameraController.CAMERA_ACTIONS.MOVING: + move_swipe(event.position) + elif current_action == CameraController.CAMERA_ACTIONS.ROTATING_VIEW: + emit_signal("on_rotate_view",event.relative) + + ###############MOBILE + else: + #PC################## + #Camera edge pushing + if event is InputEventMouseMotion: + #ROTATE VIEW + if current_action == CameraController.CAMERA_ACTIONS.ROTATING_VIEW: + emit_signal("on_rotate_view",event.relative) + #Gets screen size + var view_size = get_viewport().get_visible_rect().size - Vector2.ONE + #Get mouse position in percentage values relative to the screen + var delta = (event.position) / view_size + #Convert it to a range between [-1,1] + delta = (delta * 2) - Vector2.ONE + + if current_action == CameraController.CAMERA_ACTIONS.MOVING: + #Store it an buffer to use it on _process + #Calculates delta based on percentage between the edge size and the actual edge + horizontal = max(abs(delta.x) - (1.0 - screen_edge_size),0) + vertical = max(abs(delta.y) - (1.0 - screen_edge_size),0) + #Converts it to an [0.0,1.0] range + horizontal = range_lerp(horizontal,0.0,screen_edge_size,0.0,1.0) + vertical = range_lerp(vertical,0.0,screen_edge_size,0.0,1.0) + #Applies direction + horizontal *= sign(delta.x) + vertical *= sign(delta.y) + elif current_action == CameraController.CAMERA_ACTIONS.ROTATING_VIEW: + horizontal = delta.x + vertical = delta.y + pass + + + if event is InputEventMouseButton: + #WHEEL SCROLL + if event.button_index == BUTTON_WHEEL_UP or event.button_index == BUTTON_WHEEL_DOWN: + if event.pressed and not event.is_echo(): + var direction = (-1 if event.button_index == BUTTON_WHEEL_UP else 0) + (1 if event.button_index == BUTTON_WHEEL_DOWN else 0) + mouse_wheel += direction * get_process_delta_time() * 1000 + ###################PC + +func _process(delta): + + #PC###### + match(current_action): + CameraController.CAMERA_ACTIONS.MOVING: + #RESIDUAL MOVEMENT + if horizontal != 0 or vertical != 0: + emit_signal("on_change_velocity",Vector2(horizontal, vertical)) + + #MOUSE WHEEL + if mouse_wheel != 0: + mouse_wheel = mouse_wheel * mouse_wheel_damping + emit_signal("on_zoom",mouse_wheel) + #######PC + + + diff --git a/utils/camera/SelectionBox.gd b/utils/camera/SelectionBox.gd new file mode 100644 index 0000000..7efffc1 --- /dev/null +++ b/utils/camera/SelectionBox.gd @@ -0,0 +1,17 @@ +extends Control + +var is_visible = false +var m_pos = Vector2() +var start_sel_pos = Vector2() +const sel_box_col = Color(0, 1, 0) +const sel_box_line_width = 3 + +func _draw(): + if is_visible and start_sel_pos != m_pos: + draw_line(start_sel_pos, Vector2(m_pos.x, start_sel_pos.y), sel_box_col, sel_box_line_width) + draw_line(start_sel_pos, Vector2(start_sel_pos.x, m_pos.y), sel_box_col, sel_box_line_width) + draw_line(m_pos, Vector2(m_pos.x, start_sel_pos.y), sel_box_col, sel_box_line_width) + draw_line(m_pos, Vector2(start_sel_pos.x, m_pos.y), sel_box_col, sel_box_line_width) + +func _process(delta): + update() diff --git a/world/game.tscn b/world/game.tscn index 2f32e71..e8339b9 100644 --- a/world/game.tscn +++ b/world/game.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=6 format=2] +[gd_scene load_steps=7 format=2] [ext_resource path="res://ui/ui.tscn" type="PackedScene" id=1] [ext_resource path="res://world/game.gd" type="Script" id=2] [ext_resource path="res://world/default_env.tres" type="Environment" id=3] [ext_resource path="res://world/World.gd" type="Script" id=4] +[ext_resource path="res://utils/camera/CamBase.tscn" type="PackedScene" id=5] [sub_resource type="PlaneMesh" id=1] size = Vector2( 2000, 2000 ) @@ -13,6 +14,9 @@ script = ExtResource( 2 ) [node name="UI" parent="." instance=ExtResource( 1 )] +[node name="Map" parent="UI" index="0"] +scale = Vector2( 0.25, 0.25 ) + [node name="World" type="Spatial" parent="."] script = ExtResource( 4 ) @@ -23,12 +27,10 @@ mesh = SubResource( 1 ) [node name="WorldEnvironment" type="WorldEnvironment" parent="World"] environment = ExtResource( 3 ) -[node name="Camera" type="Camera" parent="World"] -transform = Transform( 1, 0, 0, 0, 0.509837, 0.860271, 0, -0.860271, 0.509837, 0, 5.05008, 66.6125 ) -near = 0.01 -far = 8192.0 +[node name="CamBase" parent="World" instance=ExtResource( 5 )] [connection signal="world_loaded" from="." to="UI/Map" method="_on_Game_world_loaded"] [connection signal="world_loaded" from="." to="World" method="_on_Game_world_loaded"] [editable path="UI"] +[editable path="World/CamBase"]