Added Dart Physics

This commit is contained in:
ced
2026-06-20 16:19:33 -05:00
parent feab4d7e4f
commit a77cea1b3d
15 changed files with 296 additions and 1 deletions
+21 -1
View File
@@ -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("")
+93
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
uid://8kk531f88oqw
+27
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
uid://ds3uiri3oc05v
+5
View File
@@ -0,0 +1,5 @@
extends Node3D
func _unhandled_input(event):
if event.is_action_pressed("restart"):
get_tree().reload_current_scene()
+1
View File
@@ -0,0 +1 @@
uid://geqmah8kbko0
+39
View File
@@ -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("")
+15
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
uid://ckbdoamrm313u
+50
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
uid://sueo33rxr07e
+16
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
uid://cc5yy1yc3j2uu
+24
View File
@@ -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"