diff --git a/Scenes/Dart.tscn b/Scenes/Dart.tscn index 62e1a53..6732a01 100644 --- a/Scenes/Dart.tscn +++ b/Scenes/Dart.tscn @@ -1,3 +1,23 @@ [gd_scene format=3 uid="uid://bswbcehkkqbk0"] -[node name="Dart" type="Node3D"] +[ext_resource type="Script" uid="uid://8kk531f88oqw" path="res://Scenes/DartPhysics.gd" id="1_s1kro"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_xxti7"] + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_xxti7"] + +[node name="Dart" type="Node3D" unique_id=367982453] + +[node name="RigidBody3D" type="RigidBody3D" parent="." unique_id=1993396351] +mass = 100.0 +center_of_mass_mode = 1 +center_of_mass = Vector3(0, -0.1, 0) +script = ExtResource("1_s1kro") +angle_of_plane = -25.0 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D" unique_id=1030334431] +shape = SubResource("CapsuleShape3D_xxti7") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="RigidBody3D/CollisionShape3D" unique_id=136997515] +mesh = SubResource("CapsuleMesh_xxti7") +skeleton = NodePath("") diff --git a/Scenes/DartPhysics.gd b/Scenes/DartPhysics.gd new file mode 100644 index 0000000..d8e9105 --- /dev/null +++ b/Scenes/DartPhysics.gd @@ -0,0 +1,93 @@ +extends RigidBody3D + +class_name Dart + + + +var dragging: bool = false +var drag_plane: Plane +var drag_offset: Vector3 = Vector3.ZERO +var target_pos: Vector3 = Vector3.ZERO + +# velocity tracking for release throw +var position_history: Array[Vector3] = [] +var time_history: Array[float] = [] +const HISTORY_LENGTH = 5 + +# drag "stiffness" tuning +@export var drag_follow_speed: float = 20.0 # how fast it chases the mouse +@export var max_drag_speed: float = 15.0 # clamp so it doesn't go insane +@export_range(-180,180) var angle_of_plane: float = 15.0 + +func start_drag(camera: Camera3D, mouse_pos: Vector2, hit_point: Vector3): + dragging = true + + # keep it physics-active — don't freeze + gravity_scale = 0.0 # optional: ignore gravity while held so it doesn't sag + linear_damp = 4.0 # optional: a bit of damping for stability while dragged + + #drag_plane = Plane(camera.global_transform.basis.z, hit_point) + var base_normal = camera.global_transform.basis.z + var tilt = base_normal.rotated(camera.global_transform.basis.x, deg_to_rad(angle_of_plane)) # tilt adjustable around camera's right axis + drag_plane = Plane(tilt.normalized(), hit_point) + drag_offset = global_position - hit_point + + position_history.clear() + time_history.clear() + +func update_drag(camera: Camera3D, mouse_pos: Vector2): + if not dragging: + return + + var ray_origin = camera.project_ray_origin(mouse_pos) + var ray_dir = camera.project_ray_normal(mouse_pos) + + var intersection = drag_plane.intersects_ray(ray_origin, ray_dir) + if intersection: + target_pos = intersection + drag_offset + +func _physics_process(delta): + if dragging: + # chase the target position with velocity instead of teleporting + var to_target = target_pos - global_position + var desired_velocity = to_target * drag_follow_speed + + if desired_velocity.length() > max_drag_speed: + desired_velocity = desired_velocity.normalized() * max_drag_speed + + linear_velocity = desired_velocity + angular_velocity = angular_velocity.lerp(Vector3.ZERO, 0.2) # optional: damp spin while dragging + + # track actual position for momentum calc on release + position_history.append(global_position) + time_history.append(Time.get_ticks_msec() / 1000.0) + if position_history.size() > HISTORY_LENGTH: + position_history.pop_front() + time_history.pop_front() + +func release_drag(): + if not dragging: + return + dragging = false + + gravity_scale = 1.0 + linear_damp = 0.0 # reset to whatever your normal dart damping is + + var velocity = calculate_throw_velocity() + linear_velocity = velocity + # angular_velocity left as-is from physics, or compute your own spin here + +func calculate_throw_velocity() -> Vector3: + if position_history.size() < 2: + return linear_velocity # fallback to whatever physics already had + + var start_pos = position_history[0] + var end_pos = position_history[position_history.size() - 1] + var start_time = time_history[0] + var end_time = time_history[time_history.size() - 1] + + var dt = end_time - start_time + if dt <= 0.0: + return linear_velocity + + return (end_pos - start_pos) / dt diff --git a/Scenes/DartPhysics.gd.uid b/Scenes/DartPhysics.gd.uid new file mode 100644 index 0000000..c6d8fdc --- /dev/null +++ b/Scenes/DartPhysics.gd.uid @@ -0,0 +1 @@ +uid://8kk531f88oqw diff --git a/Scenes/camera_3d.gd b/Scenes/camera_3d.gd new file mode 100644 index 0000000..c9c8ddf --- /dev/null +++ b/Scenes/camera_3d.gd @@ -0,0 +1,27 @@ +extends Camera3D + +var held_dart: Dart = null + +func _unhandled_input(event): + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: + if event.pressed: + try_grab_dart(event.position) + else: + if held_dart: + held_dart.release_drag() + held_dart = null + elif event is InputEventMouseMotion and held_dart: + held_dart.update_drag(self, event.position) + +func try_grab_dart(mouse_pos: Vector2): + var ray_origin = project_ray_origin(mouse_pos) + var ray_dir = project_ray_normal(mouse_pos) + var ray_end = ray_origin + ray_dir * 1000.0 + + var space_state = get_world_3d().direct_space_state + var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_end) + var result = space_state.intersect_ray(query) + + if result and result.collider is Dart: + held_dart = result.collider + held_dart.start_drag(self, mouse_pos, result.position) diff --git a/Scenes/camera_3d.gd.uid b/Scenes/camera_3d.gd.uid new file mode 100644 index 0000000..7ad7d4b --- /dev/null +++ b/Scenes/camera_3d.gd.uid @@ -0,0 +1 @@ +uid://ds3uiri3oc05v diff --git a/Scenes/main.gd b/Scenes/main.gd new file mode 100644 index 0000000..7b1663a --- /dev/null +++ b/Scenes/main.gd @@ -0,0 +1,5 @@ +extends Node3D + +func _unhandled_input(event): + if event.is_action_pressed("restart"): + get_tree().reload_current_scene() diff --git a/Scenes/main.gd.uid b/Scenes/main.gd.uid new file mode 100644 index 0000000..cb2871d --- /dev/null +++ b/Scenes/main.gd.uid @@ -0,0 +1 @@ +uid://geqmah8kbko0 diff --git a/Scenes/main.tscn b/Scenes/main.tscn new file mode 100644 index 0000000..08a345e --- /dev/null +++ b/Scenes/main.tscn @@ -0,0 +1,39 @@ +[gd_scene format=3 uid="uid://doh7p84eej78c"] + +[ext_resource type="Script" uid="uid://geqmah8kbko0" path="res://Scenes/main.gd" id="1_8gbba"] +[ext_resource type="Script" uid="uid://ds3uiri3oc05v" path="res://Scenes/camera_3d.gd" id="1_bo1nx"] +[ext_resource type="PackedScene" uid="uid://bswbcehkkqbk0" path="res://Scenes/Dart.tscn" id="1_jjgbg"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_qu08v"] +size = Vector3(1, 0.01, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_6q475"] +albedo_color = Color(0, 0.42745098, 0.05490196, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_xxti7"] +material = SubResource("StandardMaterial3D_6q475") +size = Vector3(1, 0.01, 1) + +[node name="Main" type="Node3D" unique_id=819165214] +script = ExtResource("1_8gbba") + +[node name="Camera3D" type="Camera3D" parent="." unique_id=1570426839] +transform = Transform3D(-1, 2.4096988e-08, -8.4036174e-08, 0, 0.9612618, 0.2756374, 8.742278e-08, 0.2756374, -0.9612618, 0, 5.0674033, -6.36445) +fov = 90.0 +script = ExtResource("1_bo1nx") + +[node name="Dart" parent="." unique_id=367982453 instance=ExtResource("1_jjgbg")] +transform = Transform3D(1, 0, 0, 0, 0.0067959065, 0.99997693, 0, -0.99997693, 0.0067959065, 0, 2.692557, 0) + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1891469178] +transform = Transform3D(-0.789482, 0.24223976, -0.56394875, -0.56595325, 0.06825608, 0.821607, 0.23751883, 0.9678125, 0.083209395, -46.816067, 15.942318, -50.370865) + +[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=889230389] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=229129629] +transform = Transform3D(100, 0, 0, 0, 100, 0, 0, 0, 100, 0, 0, 0) +shape = SubResource("BoxShape3D_qu08v") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="StaticBody3D/CollisionShape3D" unique_id=849495197] +mesh = SubResource("BoxMesh_xxti7") +skeleton = NodePath("") diff --git a/Scripts/camera_3d.gd b/Scripts/camera_3d.gd new file mode 100644 index 0000000..6052b06 --- /dev/null +++ b/Scripts/camera_3d.gd @@ -0,0 +1,15 @@ +extends Camera3D + +const RAY_LENGTH = 1000 + +func ray_cast(): + var space_state = get_world_3d().direct_space_state + var cam = $"." + var mousepos = get_viewport().get_mouse_position() + var origin = cam.project_ray_origin(mousepos) + var end = origin + cam.project_ray_normal(mousepos) * RAY_LENGTH + var query = PhysicsRayQueryParameters3D.create(origin,end) + query.collide_with_areas = true + var result = space_state.intersect_ray(query) + result = result.get("collider") + return result diff --git a/Scripts/camera_3d.gd.uid b/Scripts/camera_3d.gd.uid new file mode 100644 index 0000000..27e151f --- /dev/null +++ b/Scripts/camera_3d.gd.uid @@ -0,0 +1 @@ +uid://ckbdoamrm313u diff --git a/Scripts/dart.gd b/Scripts/dart.gd new file mode 100644 index 0000000..121daee --- /dev/null +++ b/Scripts/dart.gd @@ -0,0 +1,50 @@ +extends Node3D + +var draggingCollider +var mousePosition +var doDrag = false + +func _input(event): + var intersect + + if event is InputEventMouse: + intersect = get_mouse_intersect(event.position) + if intersect: mousePosition = intersect.position + #snap on collider + #if intersect: mousePosition = intersect.collider.global_position + + if event is InputEventMouseButton: + var leftButtonPressed = event.button_index == MOUSE_BUTTON_LEFT && event.pressed + var leftButtonReleased = event.button_index == MOUSE_BUTTON_LEFT && !event.pressed + + if leftButtonReleased: + doDrag = false + drag_and_drop(intersect) + elif leftButtonPressed: + doDrag = true + drag_and_drop(intersect) + + +func _process(delta): + if draggingCollider: + draggingCollider.global_position = mousePosition + +func drag_and_drop(intersect): + var canMove = intersect.collider in get_tree().get_nodes_in_group("Dart") + if !draggingCollider && doDrag && canMove: + draggingCollider = intersect.collider + elif draggingCollider: + draggingCollider = null + +func get_mouse_intersect(mousePosition): + var currentCamera = get_viewport().get_camera_3d() + var params = PhysicsRayQueryParameters3D.new() + + params.from = currentCamera.project_ray_origin(mousePosition) + params.to = currentCamera.project_position(mousePosition, 1000) + if draggingCollider: params.exclude = [draggingCollider] + + var worldspace = get_world_3d().direct_space_state + var result = worldspace.intersect_ray(params) + + return result diff --git a/Scripts/dart.gd.uid b/Scripts/dart.gd.uid new file mode 100644 index 0000000..3b13ace --- /dev/null +++ b/Scripts/dart.gd.uid @@ -0,0 +1 @@ +uid://sueo33rxr07e diff --git a/Scripts/mesh_instance_3d.gd b/Scripts/mesh_instance_3d.gd new file mode 100644 index 0000000..f4ebe81 --- /dev/null +++ b/Scripts/mesh_instance_3d.gd @@ -0,0 +1,16 @@ +extends MeshInstance3D + +@onready var camera = get_node("Camera3D") +@onready var area = get_node("../../../Dart") + +func _input(event): + if Input.is_action_just_pressed("leftClick"): + print(camera.ray_cast().position) + + if camera.ray_cast() != null and Input.is_action_pressed("midleclic"): + var mousepos = get_viewport().get_mouse_position() + var origin = camera.projectray_origin(mousepos) + var end = camera.project_ray_normal(mousepos) + var depth = origin.distence_to(area.global_position) + var final_position = origin + end * depth + area.global_position=final_position diff --git a/Scripts/mesh_instance_3d.gd.uid b/Scripts/mesh_instance_3d.gd.uid new file mode 100644 index 0000000..5bd89f0 --- /dev/null +++ b/Scripts/mesh_instance_3d.gd.uid @@ -0,0 +1 @@ +uid://cc5yy1yc3j2uu diff --git a/project.godot b/project.godot index 034d35b..444107f 100644 --- a/project.godot +++ b/project.godot @@ -15,9 +15,33 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] config/name="Dart Game" +run/main_scene="uid://doh7p84eej78c" config/features=PackedStringArray("4.7", "Mobile") config/icon="res://icon.svg" +[display] + +window/stretch/mode="viewport" +window/stretch/aspect="expand" + +[global_group] + +Dart="All throwable darts" + +[input] + +leftClick={ +"deadzone": 0.2, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} +restart={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":16,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + [rendering] renderer/rendering_method="mobile"