From 3a4326ca505b8b5a28b46bc27d882733ea0ad55a Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Wed, 20 Nov 2024 12:27:22 -0700 Subject: [PATCH] Balls are spawned dynamically at ball point --- src/equipment/balls/physics_ball/game_ball.gd | 2 + .../balls/plasma_ball/plasma_ball.gd | 2 +- src/player/shot_setup/ball_point.gd | 49 +++++++++++++++ src/player/shot_setup/hitbox.gd | 5 +- src/player/shot_setup/shot_setup.gd | 62 ++++++++++++++----- src/player/shot_setup/shot_setup.tscn | 34 ++-------- 6 files changed, 106 insertions(+), 48 deletions(-) create mode 100644 src/player/shot_setup/ball_point.gd diff --git a/src/equipment/balls/physics_ball/game_ball.gd b/src/equipment/balls/physics_ball/game_ball.gd index 9f8f685..86a2b65 100644 --- a/src/equipment/balls/physics_ball/game_ball.gd +++ b/src/equipment/balls/physics_ball/game_ball.gd @@ -1,6 +1,7 @@ class_name GameBall extends RigidBody3D ## Base class for all gfolf balls +## Fired as soon as this ball enters a water hazard signal entered_water const TERRAIN_DAMPING_EPSILON := 1e-6 @@ -89,6 +90,7 @@ func get_reoriented_basis() -> Basis: func _on_sleeping_state_changed() -> void: + print("SLEEPING STATE: ", sleeping) if sleeping: # Trigger to reassign on wake _awake = false diff --git a/src/equipment/balls/plasma_ball/plasma_ball.gd b/src/equipment/balls/plasma_ball/plasma_ball.gd index 50316e0..ac86964 100644 --- a/src/equipment/balls/plasma_ball/plasma_ball.gd +++ b/src/equipment/balls/plasma_ball/plasma_ball.gd @@ -8,9 +8,9 @@ func _on_body_entered(_body: Node) -> void: print_debug("Plasma ball stuck to ", _body) # Freeze physics as soon as we hit something freeze = true - sleeping = true manual_sleep_timer.start() func _fire_sleep_signal() -> void: + sleeping = true sleeping_state_changed.emit() diff --git a/src/player/shot_setup/ball_point.gd b/src/player/shot_setup/ball_point.gd new file mode 100644 index 0000000..a82e423 --- /dev/null +++ b/src/player/shot_setup/ball_point.gd @@ -0,0 +1,49 @@ +class_name BallPoint extends Node3D +## Ball spawn point & origin. I.E. the "tee". + +## Emitted when a new ball is placed. +signal ball_changed(ball: GameBall) + +## Types of game balls +enum Type { + NONE, + BASIC, + PLASMA, +} + +## Scenes for each type of ball. +const SCENE_MAP: Dictionary = { + Type.BASIC: preload("res://src/equipment/balls/physics_ball/physics_ball.tscn"), + Type.PLASMA: preload("res://src/equipment/balls/plasma_ball/plasma_ball.tscn"), +} + +@export var ball: GameBall: + set(value): + ball = value + ball_changed.emit(ball) + + +## Get a new instance of a ball of the given type. +## Returns null if the type can't be instantiated (e.g. NONE type) +func get_instance(type: Type) -> GameBall: + if type in SCENE_MAP: + var scene: PackedScene = SCENE_MAP.get(type) + return scene.instantiate() as GameBall + return null + + +## Clear any existing ball, instantiate a new one of the given type, and place it at the ball point. +func spawn_ball(type: Type) -> void: + # Clear existing ball + if is_instance_valid(ball): + ball.queue_free() + + ball = get_instance(type) + if is_instance_valid(ball): + add_child(ball) + snap() + + +## Snap the ball back to the ball point. +func snap() -> void: + ball.global_transform = global_transform diff --git a/src/player/shot_setup/hitbox.gd b/src/player/shot_setup/hitbox.gd index 40fa049..63cb687 100644 --- a/src/player/shot_setup/hitbox.gd +++ b/src/player/shot_setup/hitbox.gd @@ -3,13 +3,12 @@ extends Area3D signal ball_collision(ball: GameBall) -@export var ignored_balls: Array[GameBall] = [] - @onready var shot_setup: ShotSetup = $".." func _on_ball_entered(ball: GameBall) -> void: - if not ball in ignored_balls: + # TODO generalize this in the future if we have multiple players on teams + if ball != shot_setup.game_ball: ball_collision.emit(ball) diff --git a/src/player/shot_setup/shot_setup.gd b/src/player/shot_setup/shot_setup.gd index 3e9cea3..3c19d36 100644 --- a/src/player/shot_setup/shot_setup.gd +++ b/src/player/shot_setup/shot_setup.gd @@ -41,6 +41,9 @@ const WATER_DAMAGE := 10.0 ## Initially-selected club @export var initial_club: Club.Type = Club.Type.DRIVER +## Initially-selected ball type +@export var initial_ball: BallPoint.Type = BallPoint.Type.BASIC + @export_category("Shot Parameters") @export var base_power := 2.5 @export var base_curve := 0.0 @@ -86,6 +89,12 @@ var club: Club.Type: _on_club_change(value) club = value +var ball_type: BallPoint.Type: + set(value): + if value != ball_type: + ball_point.spawn_ball(value) + ball_type = value + var shot_ref: Node3D var shot_power: float: @@ -100,6 +109,10 @@ var shot_curve: float: get: return hud.power_bar.value +var game_ball: GameBall: + get: + return ball_point.ball + var _free_camera: FreeCamera var _returning_free_camera := false var _restart_queued := false @@ -123,9 +136,7 @@ var _tracking_camera: OrbitalCamera @onready var arrow_animation: AnimationPlayer = %ArrowAnimation @onready var shot_projection: ProjectileArc = %ShotProjection -@onready var ball_point: Node3D = %BallPoint -# @onready var game_ball: GameBall = %PhysicsBall -@onready var game_ball: GameBall = %PlasmaBall +@onready var ball_point: BallPoint = %BallPoint @onready var drive_ref: RayCast3D = %DriveRef @onready var drive_arrow: Node3D = %DriveArrow @@ -138,8 +149,6 @@ var _tracking_camera: OrbitalCamera @onready var ball_return_timer: Timer = %BallReturnTimer -@onready var ball_impulse_debug: Node3D = %BallImpulseDebug - @onready var camera_distance := zoom.position.z: set = _set_camera_distance @@ -155,6 +164,7 @@ func _ready() -> void: # Create & set up HUD hud = ShotHUD.create(player) world.ui.add_player_hud(hud) + ball_type = initial_ball club = initial_club @@ -223,16 +233,16 @@ func take_shot() -> void: var impulse := get_shot_impulse(shot_power) print_debug("Shot impulse: ", impulse, "; ", impulse.length(), " N*s") - ball_impulse_debug.transform = ( - Transform3D.IDENTITY.scaled(Vector3.ONE * impulse.length()).looking_at(impulse) - ) - - game_ball.freeze = false - game_ball.apply_central_impulse(impulse) + if game_ball: + game_ball.freeze = false + game_ball.apply_central_impulse(impulse) ## Make the shot projection widget visible, with animated transition func _show_shot_projection() -> void: + if not game_ball: + return + shot_projection.initial_speed = 1 shot_projection.basis = shot_ref.basis.orthonormalized() var shot_speed := get_shot_impulse(1.0).length() / game_ball.mass @@ -272,6 +282,9 @@ func return_free_cam() -> void: func return_ball() -> void: + if not game_ball: + return + game_ball.freeze = true var tween := get_tree().create_tween() ( @@ -288,6 +301,9 @@ func return_ball() -> void: func travel_to_ball() -> void: + if not game_ball: + return + game_ball.freeze = true global_position = game_ball.global_position @@ -296,12 +312,14 @@ func travel_to_ball() -> void: direction.rotation.y = 0 _target_rotation.y = 0 global_basis = game_ball.get_reoriented_basis() - print_debug("REORIENTED BASIS: ", global_basis) - game_ball.global_transform = ball_point.global_transform + ball_point.snap() func start_shot_track() -> void: + if not game_ball: + return + if phase == Phase.SHOT: _tracking_camera = OrbitalCamera.create(game_ball) _tracking_camera.rotation.y = randf_range(0.0, TAU) @@ -331,7 +349,8 @@ func _on_club_change(new_club_type: Club.Type) -> void: wedge_arrow.hide() iron_arrow.hide() putt_arrow.hide() - game_ball.iron_ball = false + if game_ball: + game_ball.iron_ball = false hud.club_selector.value = new_club_type # TODO club change animation character.hold_right(new_club.get_model()) @@ -348,7 +367,8 @@ func _on_club_change(new_club_type: Club.Type) -> void: Club.Type.IRON: shot_ref = iron_ref iron_arrow.show() - game_ball.iron_ball = true + if game_ball: + game_ball.iron_ball = true Club.Type.SPECIAL: # TODO figure this out shot_ref = drive_ref @@ -358,7 +378,6 @@ func _on_club_change(new_club_type: Club.Type) -> void: ## Called immediately before `phase` is mutated. func _on_phase_change(new_phase: Phase) -> void: - print_debug("Player ", player.name, ": change to ", Phase.keys()[new_phase]) match new_phase: Phase.AIM: hud.show_hud() @@ -400,7 +419,18 @@ func _on_phase_change(new_phase: Phase) -> void: finished.emit(self) +func _on_game_ball_changed(ball: GameBall) -> void: + if ball: + ball.iron_ball = (club == Club.Type.IRON) + ball.entered_water.connect(_on_ball_entered_water) + ball.sleeping_state_changed.connect(_on_ball_sleeping_state_changed) + + func _process(delta: float) -> void: + # REMOVEME + if Input.is_action_just_pressed("ui_menu"): + print("Debugging...") + ## Visual updates # Rotation direction.rotation.y = lerp_angle( diff --git a/src/player/shot_setup/shot_setup.tscn b/src/player/shot_setup/shot_setup.tscn index 7c76b69..632bea7 100644 --- a/src/player/shot_setup/shot_setup.tscn +++ b/src/player/shot_setup/shot_setup.tscn @@ -1,22 +1,13 @@ -[gd_scene load_steps=19 format=3 uid="uid://cy7t2tc4y3b4"] +[gd_scene load_steps=17 format=3 uid="uid://cy7t2tc4y3b4"] [ext_resource type="Script" path="res://src/player/shot_setup/shot_setup.gd" id="1_r6ei4"] +[ext_resource type="Script" path="res://src/player/shot_setup/ball_point.gd" id="2_e6i3g"] [ext_resource type="PackedScene" uid="uid://c2k88ns0h5ie1" path="res://src/ui/3d/arrow/arrow.tscn" id="2_s70wl"] -[ext_resource type="PackedScene" uid="uid://dcqxlbsrubapk" path="res://src/equipment/balls/plasma_ball/plasma_ball.tscn" id="3_8dte7"] [ext_resource type="PackedScene" uid="uid://1s3gywmoi20e" path="res://src/characters/player_characters/gfolf_girl/gfolf_girl.tscn" id="3_e4aur"] [ext_resource type="PackedScene" uid="uid://fht6j87o8ecr" path="res://src/ui/3d/projectile_arc/projectile_arc.tscn" id="4_ry2ho"] [ext_resource type="PackedScene" uid="uid://dbdul15c4oblg" path="res://src/ui/3d/projected_target.tscn" id="6_mynqj"] [ext_resource type="Script" path="res://src/player/shot_setup/hitbox.gd" id="7_uh8kn"] -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lnol1"] -albedo_color = Color(0, 0.537255, 1, 1) - -[sub_resource type="CylinderMesh" id="CylinderMesh_ql2ui"] -material = SubResource("StandardMaterial3D_lnol1") -top_radius = 0.02 -bottom_radius = 0.02 -height = 1.0 - [sub_resource type="Animation" id="Animation_ug2a7"] length = 0.001 tracks/0/type = "value" @@ -213,18 +204,7 @@ script = ExtResource("1_r6ei4") [node name="BallPoint" type="Node3D" parent="."] unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.08, 0) - -[node name="PlasmaBall" parent="BallPoint" instance=ExtResource("3_8dte7")] -unique_name_in_owner = true - -[node name="BallImpulseDebug" type="Node3D" parent="BallPoint"] -unique_name_in_owner = true -visible = false - -[node name="MeshInstance3D" type="MeshInstance3D" parent="BallPoint/BallImpulseDebug"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, -0.5) -mesh = SubResource("CylinderMesh_ql2ui") -skeleton = NodePath("../..") +script = ExtResource("2_e6i3g") [node name="PlayerPivot" type="Node3D" parent="."] unique_name_in_owner = true @@ -350,7 +330,7 @@ visible = false initial_speed = 50.0 time_step = 0.01 max_steps = 800 -excluded_bodies = [NodePath("../../BallPoint/PlasmaBall")] +excluded_bodies = [null] [node name="ProjectedTarget" parent="ArrowPivot/ShotProjection" instance=ExtResource("6_mynqj")] @@ -365,15 +345,13 @@ libraries = { unique_name_in_owner = true one_shot = true -[node name="Hitbox" type="Area3D" parent="." node_paths=PackedStringArray("ignored_balls")] +[node name="Hitbox" type="Area3D" parent="."] script = ExtResource("7_uh8kn") -ignored_balls = [NodePath("../BallPoint/PlasmaBall")] [node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"] shape = SubResource("SphereShape3D_xvvdi") -[connection signal="entered_water" from="BallPoint/PlasmaBall" to="." method="_on_ball_entered_water"] -[connection signal="sleeping_state_changed" from="BallPoint/PlasmaBall" to="." method="_on_ball_sleeping_state_changed"] +[connection signal="ball_changed" from="BallPoint" to="." method="_on_game_ball_changed"] [connection signal="timeout" from="BallReturnTimer" to="." method="_on_ball_return_timer_timeout"] [connection signal="ball_collision" from="Hitbox" to="." method="_on_hitbox_ball_collision"] [connection signal="body_entered" from="Hitbox" to="Hitbox" method="_on_body_entered"]