18 Commits 5f11856b1f ... fd3494e2e1

Author SHA1 Message Date
  DricomDragon fd3494e2e1 Merge branch 'new/biplan' 4 days ago
  DricomDragon 1b15a1ae7e :wrench: Prevent discrete errors to propagate 4 days ago
  DricomDragon 8f2c5e90a8 :beetle: Enable collision effects 4 days ago
  DricomDragon 6ade09c74f :wrench: Let the biplan on its whells 4 days ago
  DricomDragon 7a89b6664a :beetle: Fix far foil craziness 4 days ago
  DricomDragon 4a04ced029 :wrench: Create foil resistance 4 days ago
  DricomDragon 2f424e0c80 :wrench: Fill missing hitboxes 4 days ago
  DricomDragon 9547d3c836 :beetle: Fix seat position 4 days ago
  DricomDragon d75cf58c50 :beetle: Fix pitch and turn torque 4 days ago
  DricomDragon 836533edea :beetle: Fix the thrust direction 4 days ago
  DricomDragon 374084e844 :fire: Remove old plane debugs 4 days ago
  DricomDragon 1992affabf Copy plane script 4 days ago
  DricomDragon be73fbcdbe Add links for wheels to prevent them to go through ground 4 days ago
  DricomDragon 13f8091d98 Set up wheels 4 days ago
  DricomDragon fd46ef925c Make a first groundable biplan 4 days ago
  DricomDragon 3d25e72d16 :beetle: Fix return to title crash 1 week ago
  DricomDragon 82868253a6 :beetle: Fix unhappy Godot collecting chocolate 1 week ago
  DricomDragon 67568df793 Add chocolate pickup sound 1 week ago

+ 3 - 1
godot/component/entity/collectibles/chocolate/chocolate.gd

@@ -5,6 +5,7 @@ extends Node3D
 const COLLECT_ANIM := "collection"
 
 @onready var chocolateAnimation: AnimationPlayer = $ChocolateAnimation
+@onready var pickupSound: AudioStreamPlayer3D = $ChocolatePickupSound
 
 var _collected := false
 
@@ -20,9 +21,10 @@ func _on_chocolate_tablet_body_entered(body: Node3D) -> void:
 
 func _give_chocolate_to(walker: Walker) -> void:
 	walker.give_chocolate()
-	reparent(walker)
+	reparent.call_deferred(walker)
 	chocolateAnimation.play(COLLECT_ANIM)
 	chocolateAnimation.animation_finished.connect(_finish)
+	pickupSound.play()
 
 
 func _finish(animation_name: String) -> void:

+ 26 - 22
godot/component/entity/collectibles/chocolate/chocolate.tscn

@@ -1,26 +1,7 @@
-[gd_scene load_steps=11 format=3 uid="uid://ba3gf7iy3mxbq"]
+[gd_scene load_steps=12 format=3 uid="uid://ba3gf7iy3mxbq"]
 
 [ext_resource type="Script" path="res://component/entity/collectibles/chocolate/chocolate.gd" id="1_wk24q"]
-
-[sub_resource type="Animation" id="Animation_0f754"]
-resource_name = "idle"
-length = 4.0
-loop_mode = 1
-step = 1.0
-tracks/0/type = "rotation_3d"
-tracks/0/imported = false
-tracks/0/enabled = true
-tracks/0/path = NodePath(".")
-tracks/0/interp = 1
-tracks/0/loop_wrap = true
-tracks/0/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1, 2, 1, 0, 1, 0, 0, 4, 1, 0, 0, 0, -1)
-tracks/1/type = "position_3d"
-tracks/1/imported = false
-tracks/1/enabled = true
-tracks/1/path = NodePath(".")
-tracks/1/interp = 2
-tracks/1/loop_wrap = true
-tracks/1/keys = PackedFloat32Array(0, 1, 0, 0, 0, 2, 1, 0, 0.25, 0)
+[ext_resource type="AudioStream" uid="uid://h5pmhfnb4wus" path="res://component/entity/collectibles/chocolate/pickupChocolate.wav" id="2_ecvab"]
 
 [sub_resource type="Animation" id="Animation_cfly2"]
 length = 0.001
@@ -58,6 +39,26 @@ tracks/2/interp = 2
 tracks/2/loop_wrap = true
 tracks/2/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1, 1, 0, 1.5, 0, 2, 1, 0, 0, 0)
 
+[sub_resource type="Animation" id="Animation_0f754"]
+resource_name = "idle"
+length = 4.0
+loop_mode = 1
+step = 1.0
+tracks/0/type = "rotation_3d"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath(".")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1, 2, 1, 0, 1, 0, 0, 4, 1, 0, 0, 0, -1)
+tracks/1/type = "position_3d"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath(".")
+tracks/1/interp = 2
+tracks/1/loop_wrap = true
+tracks/1/keys = PackedFloat32Array(0, 1, 0, 0, 0, 2, 1, 0, 0.25, 0)
+
 [sub_resource type="AnimationLibrary" id="AnimationLibrary_djjxd"]
 _data = {
 "RESET": SubResource("Animation_cfly2"),
@@ -95,7 +96,6 @@ libraries = {
 autoplay = "idle"
 
 [node name="ChocolateTablet" type="Area3D" parent="."]
-transform = Transform3D(0.00300002, 0, 0, 0, 0.00300002, 0, 0, 0, 0.00300002, 0, 0, 0)
 collision_layer = 32
 collision_mask = 10
 
@@ -110,4 +110,8 @@ mesh = SubResource("BoxMesh_c10ab")
 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.119209, 0)
 mesh = SubResource("BoxMesh_ialpe")
 
+[node name="ChocolatePickupSound" type="AudioStreamPlayer3D" parent="."]
+visible = false
+stream = ExtResource("2_ecvab")
+
 [connection signal="body_entered" from="ChocolateTablet" to="." method="_on_chocolate_tablet_body_entered"]

BIN
godot/component/entity/collectibles/chocolate/pickupChocolate.wav


+ 24 - 0
godot/component/entity/collectibles/chocolate/pickupChocolate.wav.import

@@ -0,0 +1,24 @@
+[remap]
+
+importer="wav"
+type="AudioStreamWAV"
+uid="uid://h5pmhfnb4wus"
+path="res://.godot/imported/pickupChocolate.wav-8e1f4ed4c4e77a50fca685d7f9084c9f.sample"
+
+[deps]
+
+source_file="res://component/entity/collectibles/chocolate/pickupChocolate.wav"
+dest_files=["res://.godot/imported/pickupChocolate.wav-8e1f4ed4c4e77a50fca685d7f9084c9f.sample"]
+
+[params]
+
+force/8_bit=false
+force/mono=false
+force/max_rate=false
+force/max_rate_hz=44100
+edit/trim=false
+edit/normalize=false
+edit/loop_mode=1
+edit/loop_begin=0
+edit/loop_end=-1
+compress/mode=0

BIN
godot/component/entity/vehicles/biplan/TheWindWaker_noWheel.glb


+ 36 - 0
godot/component/entity/vehicles/biplan/TheWindWaker_noWheel.glb.import

@@ -0,0 +1,36 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://bcysn7mbjre1r"
+path="res://.godot/imported/TheWindWaker_noWheel.glb-4660da8d08b21f8d18ffac6a1bde77d3.scn"
+
+[deps]
+
+source_file="res://component/entity/vehicles/biplan/TheWindWaker_noWheel.glb"
+dest_files=["res://.godot/imported/TheWindWaker_noWheel.glb-4660da8d08b21f8d18ffac6a1bde77d3.scn"]
+
+[params]
+
+nodes/root_type=""
+nodes/root_name=""
+nodes/apply_root_scale=true
+nodes/root_scale=1.0
+nodes/import_as_skeleton_bones=false
+meshes/ensure_tangents=true
+meshes/generate_lods=true
+meshes/create_shadow_meshes=true
+meshes/light_baking=1
+meshes/lightmap_texel_size=0.2
+meshes/force_disable_compression=false
+skins/use_named_skins=true
+animation/import=true
+animation/fps=30
+animation/trimming=false
+animation/remove_immutable_tracks=true
+animation/import_rest_as_RESET=false
+import_script/path=""
+_subresources={}
+gltf/naming_version=1
+gltf/embedded_image_handling=1

+ 90 - 0
godot/component/entity/vehicles/biplan/biplan.gd

@@ -0,0 +1,90 @@
+class_name Biplan
+extends SeatedVehicle
+
+const PLANE_FORWARD := Vector3.BACK # z increases when moving forward
+
+@export var thrust_power: float = 3500.0
+@export var turn_to_torque: float = 1000.0
+@export var move_to_pitch: float = 2000.0
+@export var wing_resistance: float = 2500.0
+@export var foil_resistance: float = 250.0
+@export var lift: float = 50.0
+
+var target_torque: float = 0.0
+var target_pitch: float = 0.0
+var target_rotation := Vector3.ZERO
+var target_thrust := Vector3.ZERO
+
+@onready var VerticalFoil : Node3D = $VerticalFoil
+
+
+func trigger_thrust(activate: bool) -> void:
+	if activate:
+		target_thrust = PLANE_FORWARD * thrust_power
+	else:
+		target_thrust = Vector3.ZERO
+
+
+func trigger_direction(dir: Vector2) -> void:
+	target_torque = dir.x * turn_to_torque
+	target_pitch = dir.y * move_to_pitch
+
+
+func get_free_seat() -> Node3D:
+	return $DrivingSeat
+
+
+func _physics_process(_delta) -> void:
+	# Command part
+	_apply_plane_rotation()
+	_apply_plane_thrust()
+
+	# Air simulation part
+	if is_sleeping():
+		return
+
+	_apply_wing_resistance()
+	_apply_foil_resistance()
+	_apply_lift()
+	transform = transform.orthonormalized()
+
+
+func _apply_plane_rotation() -> void:
+	var torque: Vector3 = transform.basis * Vector3(target_pitch, 0.0, target_torque)
+	apply_torque(torque)
+
+
+func _apply_plane_thrust() -> void:
+	var force: Vector3 = transform.basis * target_thrust
+	apply_central_force(force)
+
+
+func _apply_wing_resistance() -> void:
+	var vertical_speed = linear_velocity.dot(transform.basis * Vector3.UP)
+	var local_wing_force = Vector3.UP * -wing_resistance * vertical_speed
+	var wing_force = transform.basis * local_wing_force
+	apply_central_force(wing_force)
+
+
+func _apply_foil_resistance() -> void:
+	var foil_position : Vector3 = VerticalFoil.get_position()
+	var horizontal_speed = linear_velocity.dot(transform.basis * Vector3.RIGHT)
+	var local_foil_force = Vector3.RIGHT * -foil_resistance * horizontal_speed
+	apply_force(local_foil_force, foil_position)
+
+
+func _apply_lift() -> void:
+	var forward_speed = linear_velocity.dot(transform.basis * PLANE_FORWARD)
+	if forward_speed < 0.0 :
+		forward_speed = 0.0
+	var local_lift_force = Vector3.UP * lift * forward_speed
+	var lift_force = transform.basis * local_lift_force
+	apply_central_force(lift_force)
+
+
+func _on_dir_changed(dir: Vector2) -> void:
+	trigger_direction(dir)
+
+
+func _on_main_action(pressed: bool) -> void:
+	trigger_thrust(pressed)

+ 98 - 0
godot/component/entity/vehicles/biplan/biplan.tscn

@@ -0,0 +1,98 @@
+[gd_scene load_steps=10 format=3 uid="uid://ceqqr0vn633o4"]
+
+[ext_resource type="Script" path="res://component/entity/vehicles/biplan/biplan.gd" id="1_vi3fj"]
+[ext_resource type="PackedScene" uid="uid://bcysn7mbjre1r" path="res://component/entity/vehicles/biplan/TheWindWaker_noWheel.glb" id="2_mr41j"]
+[ext_resource type="PackedScene" uid="uid://bhutb1ntqf2vt" path="res://component/entity/vehicles/biplan/wheels/TheWindWaker_sideWheel.glb" id="3_8s6eu"]
+[ext_resource type="PackedScene" uid="uid://lcp33gkhc60y" path="res://component/entity/vehicles/biplan/wheels/TheWindWaker_rearWheel.glb" id="4_5u4vt"]
+
+[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4fsfr"]
+radius = 0.0571188
+height = 0.857268
+
+[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_d0fig"]
+radius = 0.514
+height = 4.27973
+
+[sub_resource type="BoxShape3D" id="BoxShape3D_6m175"]
+size = Vector3(7.995, 0.12, 0.5)
+
+[sub_resource type="BoxShape3D" id="BoxShape3D_ws8bj"]
+size = Vector3(0.19, 0.70166, 1)
+
+[sub_resource type="BoxShape3D" id="BoxShape3D_ipw27"]
+size = Vector3(1.6, 0.118, 0.506224)
+
+[node name="Biplan" type="VehicleBody3D" groups=["vehicle"]]
+collision_layer = 8
+collision_mask = 27
+mass = 500.0
+contact_monitor = true
+max_contacts_reported = 1
+script = ExtResource("1_vi3fj")
+
+[node name="TheWindWaker_noWheel" parent="." instance=ExtResource("2_mr41j")]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0)
+
+[node name="LeftWheelLink" type="CollisionShape3D" parent="."]
+transform = Transform3D(0.705006, -0.709202, 0, 0.709202, 0.705006, 0, 0, 0, 1, 0.639446, -0.0314799, 0.351246)
+shape = SubResource("CapsuleShape3D_4fsfr")
+
+[node name="LeftWheel" type="VehicleWheel3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.85905, -0.352406, 0.377702)
+wheel_radius = 0.245
+wheel_rest_length = 0.074
+suspension_stiffness = 75.0
+suspension_max_force = 12000.0
+damping_compression = 0.5
+damping_relaxation = 0.7
+
+[node name="TheWindWaker_sideWheel" parent="LeftWheel" instance=ExtResource("3_8s6eu")]
+
+[node name="RightWheelLink" type="CollisionShape3D" parent="."]
+transform = Transform3D(0.704634, 0.709571, 0, -0.709571, 0.704634, 0, 0, 0, 1, -0.639, -0.031, 0.351)
+shape = SubResource("CapsuleShape3D_4fsfr")
+
+[node name="RightWheel" type="VehicleWheel3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.859, -0.352, 0.378)
+wheel_radius = 0.245
+wheel_rest_length = 0.074
+suspension_stiffness = 75.0
+suspension_max_force = 12000.0
+damping_compression = 0.3
+damping_relaxation = 0.5
+
+[node name="TheWindWaker_sideWheel" parent="RightWheel" instance=ExtResource("3_8s6eu")]
+transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
+
+[node name="RearWheel" type="VehicleWheel3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.00717106, -2.87896)
+use_as_steering = true
+wheel_radius = 0.19
+wheel_rest_length = 0.057
+suspension_stiffness = 75.0
+suspension_max_force = 12000.0
+
+[node name="TheWindWaker_rearWheel" parent="RearWheel" instance=ExtResource("4_5u4vt")]
+
+[node name="Body" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0.507829, -0.522253)
+shape = SubResource("CapsuleShape3D_d0fig")
+
+[node name="TopWing" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5507, -0.84682)
+shape = SubResource("BoxShape3D_6m175")
+
+[node name="BottomWing" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 0.980604, -0.195998, 0, 0.195998, 0.980604, 0, 0.107529, -0.828761)
+shape = SubResource("BoxShape3D_6m175")
+
+[node name="DrivingSeat" type="Node3D" parent="." groups=["seat"]]
+transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 2.38419e-07, 0.462769, -0.53054)
+
+[node name="VerticalFoil" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 0.823354, -0.567528, 0, 0.567528, 0.823354, -2.38419e-07, 0.989526, -3.4046)
+shape = SubResource("BoxShape3D_ws8bj")
+
+[node name="HorizontalFoil" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.827551, -3.54696)
+shape = SubResource("BoxShape3D_ipw27")

godot/component/entity/vehicles/plane/propeller-aircraft-engine-noise.mp3 → godot/component/entity/vehicles/biplan/propeller-aircraft-engine-noise.mp3


+ 6 - 6
godot/component/entity/vehicles/plane/propeller-aircraft-engine-noise.mp3.import

@@ -3,17 +3,17 @@
 importer="mp3"
 type="AudioStreamMP3"
 uid="uid://ov6tnal7utuo"
-path="res://.godot/imported/propeller-aircraft-engine-noise.mp3-69c0070ad7ce30a0ceb30cdefa2630ec.mp3str"
+path="res://.godot/imported/propeller-aircraft-engine-noise.mp3-16a0a42895fbbf20b7ca30194da7f625.mp3str"
 
 [deps]
 
-source_file="res://component/entity/vehicles/plane/propeller-aircraft-engine-noise.mp3"
-dest_files=["res://.godot/imported/propeller-aircraft-engine-noise.mp3-69c0070ad7ce30a0ceb30cdefa2630ec.mp3str"]
+source_file="res://component/entity/vehicles/biplan/propeller-aircraft-engine-noise.mp3"
+dest_files=["res://.godot/imported/propeller-aircraft-engine-noise.mp3-16a0a42895fbbf20b7ca30194da7f625.mp3str"]
 
 [params]
 
-loop=false
-loop_offset=0
-bpm=0
+loop=true
+loop_offset=0.0
+bpm=0.0
 beat_count=0
 bar_beats=4

BIN
godot/component/entity/vehicles/biplan/wheels/TheWindWaker_rearWheel.glb


+ 36 - 0
godot/component/entity/vehicles/biplan/wheels/TheWindWaker_rearWheel.glb.import

@@ -0,0 +1,36 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://lcp33gkhc60y"
+path="res://.godot/imported/TheWindWaker_rearWheel.glb-6d04befd81df6ef745ae627cf0a63eaf.scn"
+
+[deps]
+
+source_file="res://component/entity/vehicles/biplan/wheels/TheWindWaker_rearWheel.glb"
+dest_files=["res://.godot/imported/TheWindWaker_rearWheel.glb-6d04befd81df6ef745ae627cf0a63eaf.scn"]
+
+[params]
+
+nodes/root_type=""
+nodes/root_name=""
+nodes/apply_root_scale=true
+nodes/root_scale=1.0
+nodes/import_as_skeleton_bones=false
+meshes/ensure_tangents=true
+meshes/generate_lods=true
+meshes/create_shadow_meshes=true
+meshes/light_baking=1
+meshes/lightmap_texel_size=0.2
+meshes/force_disable_compression=false
+skins/use_named_skins=true
+animation/import=true
+animation/fps=30
+animation/trimming=false
+animation/remove_immutable_tracks=true
+animation/import_rest_as_RESET=false
+import_script/path=""
+_subresources={}
+gltf/naming_version=1
+gltf/embedded_image_handling=1

BIN
godot/component/entity/vehicles/biplan/wheels/TheWindWaker_sideWheel.glb


+ 36 - 0
godot/component/entity/vehicles/biplan/wheels/TheWindWaker_sideWheel.glb.import

@@ -0,0 +1,36 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://bhutb1ntqf2vt"
+path="res://.godot/imported/TheWindWaker_sideWheel.glb-ba2edf3107a3df6d62278e0449b8ec60.scn"
+
+[deps]
+
+source_file="res://component/entity/vehicles/biplan/wheels/TheWindWaker_sideWheel.glb"
+dest_files=["res://.godot/imported/TheWindWaker_sideWheel.glb-ba2edf3107a3df6d62278e0449b8ec60.scn"]
+
+[params]
+
+nodes/root_type=""
+nodes/root_name=""
+nodes/apply_root_scale=true
+nodes/root_scale=1.0
+nodes/import_as_skeleton_bones=false
+meshes/ensure_tangents=true
+meshes/generate_lods=true
+meshes/create_shadow_meshes=true
+meshes/light_baking=1
+meshes/lightmap_texel_size=0.2
+meshes/force_disable_compression=false
+skins/use_named_skins=true
+animation/import=true
+animation/fps=30
+animation/trimming=false
+animation/remove_immutable_tracks=true
+animation/import_rest_as_RESET=false
+import_script/path=""
+_subresources={}
+gltf/naming_version=1
+gltf/embedded_image_handling=1

+ 1 - 1
godot/component/entity/vehicles/jeep/Jeep.tscn

@@ -17,8 +17,8 @@ collision_mask = 27
 mass = 400.0
 center_of_mass_mode = 1
 center_of_mass = Vector3(0, 0.06, 0)
-max_contacts_reported = 1
 contact_monitor = true
+max_contacts_reported = 1
 script = ExtResource("1_fjw46")
 max_engine_force = 1842.0
 

+ 0 - 8
godot/component/entity/vehicles/plane/tiny_plane.gd

@@ -56,10 +56,6 @@ func _apply_wing_resistance() -> void:
 	var vertical_speed = linear_velocity.dot(transform.basis * Vector3.UP)
 	var local_wing_force = Vector3.UP * -wing_resistance * vertical_speed
 	var wing_force = transform.basis * local_wing_force
-	print("= = =")
-	print("Vertical speed : ", vertical_speed)
-	print("Wing force local :", local_wing_force)
-	print("Wing force : ", wing_force)
 	apply_central_force(wing_force)
 
 
@@ -69,10 +65,6 @@ func _apply_lift() -> void:
 		forward_speed = 0.0
 	var local_lift_force = Vector3.UP * lift * forward_speed
 	var lift_force = transform.basis * local_lift_force
-	print("- - -")
-	print("Forward speed : ", forward_speed)
-	print("Lift force local :", local_lift_force)
-	print("Lift force : ", lift_force)
 	apply_central_force(lift_force)
 
 

+ 1 - 1
godot/component/ui/pause/pause_menu.gd

@@ -20,8 +20,8 @@ func _on_resume_button_pressed():
 
 
 func _on_title_button_pressed():
-	get_tree().reload_current_scene()
 	resume()
+	get_tree().reload_current_scene()
 
 
 func _on_quit_button_pressed():

+ 5 - 0
godot/project.godot

@@ -27,6 +27,11 @@ PromptManager="*res://addons/input_prompts/input_prompt_manager.gd"
 
 enabled=PackedStringArray("res://addons/input_prompts/plugin.cfg")
 
+[global_group]
+
+seat="A place on a vehicle where the player can sit on"
+vehicle="An technical object that the player can drive"
+
 [input]
 
 ui_accept={

+ 5 - 1
godot/run/levels/infinite_level.tscn

@@ -1,4 +1,4 @@
-[gd_scene load_steps=33 format=3 uid="uid://7ivipmwaw24t"]
+[gd_scene load_steps=34 format=3 uid="uid://7ivipmwaw24t"]
 
 [ext_resource type="Texture2D" uid="uid://cgwafs6ukpm7y" path="res://component/entity/ground/ground051.jpg" id="1_3ytcp"]
 [ext_resource type="Script" path="res://run/levels/camera_tracker.gd" id="1_qdm47"]
@@ -11,6 +11,7 @@
 [ext_resource type="PackedScene" uid="uid://dhmnou48k24lr" path="res://component/entity/props/triangle/flat_triangle.tscn" id="7_yyaog"]
 [ext_resource type="Script" path="res://flow/music_player/music_player.gd" id="8_iulsm"]
 [ext_resource type="AudioStream" uid="uid://c3l60bt307wjh" path="res://flow/music_player/00-Wesh-tone-realaze.mp3" id="8_ndffw"]
+[ext_resource type="PackedScene" uid="uid://ceqqr0vn633o4" path="res://component/entity/vehicles/biplan/biplan.tscn" id="10_j323j"]
 [ext_resource type="PackedScene" uid="uid://bwhwbm6jrvg42" path="res://component/entity/buildings/outer_stairs_house/outer_stairs_house.tscn" id="11_j8m6y"]
 [ext_resource type="PackedScene" uid="uid://cxdvll60r67q8" path="res://component/entity/buildings/character_sign_choco/character_sign_choco.tscn" id="13_2x2n6"]
 [ext_resource type="PackedScene" uid="uid://uy3pgfv4q3iy" path="res://component/ui/title/title_screen.tscn" id="13_2xijd"]
@@ -284,6 +285,9 @@ transform = Transform3D(0.920102, 0, 0.391679, 0, 1, 0, -0.391679, 0, 0.920102,
 [node name="Jeep9" parent="Vehicles" instance=ExtResource("6_nnvc5")]
 transform = Transform3D(0.920102, 0, 0.391679, 0, 1, 0, -0.391679, 0, 0.920102, -163.38, 0.747562, 75.9588)
 
+[node name="Biplan" parent="Vehicles" instance=ExtResource("10_j323j")]
+transform = Transform3D(1, 0, 0, 0, 0.995221, 0.0976524, 0, -0.0976524, 0.995221, 17.2242, 0.501759, 55.4111)
+
 [node name="Buildings" type="Node3D" parent="."]
 
 [node name="StairHouse1" parent="Buildings" instance=ExtResource("11_j8m6y")]

+ 55 - 0
godot/run/levels/test/biplans.tscn

@@ -0,0 +1,55 @@
+[gd_scene load_steps=7 format=3 uid="uid://c7mdo8kugd8e"]
+
+[ext_resource type="Texture2D" uid="uid://cgwafs6ukpm7y" path="res://component/entity/ground/ground051.jpg" id="1_x5qt0"]
+[ext_resource type="Script" path="res://run/levels/camera_tracker.gd" id="2_m6pqc"]
+[ext_resource type="PackedScene" uid="uid://ceqqr0vn633o4" path="res://component/entity/vehicles/biplan/biplan.tscn" id="3_wp27j"]
+
+[sub_resource type="BoxShape3D" id="BoxShape3D_ctk0t"]
+size = Vector3(500, 10, 500)
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ot7iy"]
+albedo_texture = ExtResource("1_x5qt0")
+subsurf_scatter_strength = 0.73
+grow_amount = 0.352
+point_size = 55.9
+
+[sub_resource type="QuadMesh" id="QuadMesh_dt8nl"]
+material = SubResource("StandardMaterial3D_ot7iy")
+size = Vector2(500, 500)
+orientation = 1
+
+[node name="InfiniteLevel" type="Node3D"]
+
+[node name="Ground" type="StaticBody3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
+collision_mask = 0
+
+[node name="Shape" type="CollisionShape3D" parent="Ground"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0)
+shape = SubResource("BoxShape3D_ctk0t")
+
+[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
+mesh = SubResource("QuadMesh_dt8nl")
+
+[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 0.734358, 0.678762, 0, -0.678762, 0.734358, -33.1669, 12.6773, 15.7238)
+shadow_enabled = true
+
+[node name="MainCamera" type="Camera3D" parent="."]
+transform = Transform3D(0.865641, -0.163827, 0.473103, 3.02203e-08, 0.944949, 0.327218, -0.500665, -0.283253, 0.817987, -74.1516, 5.77866, 70.029)
+current = true
+script = ExtResource("2_m6pqc")
+max_distance = 6.0
+camera_height = 2.0
+
+[node name="Biplan1" parent="." instance=ExtResource("3_wp27j")]
+transform = Transform3D(-0.0491111, 0, 0.998793, 0, 1, 0, -0.998793, 0, -0.0491111, -89.2691, 23.1855, 53.3185)
+
+[node name="Biplan2" parent="." instance=ExtResource("3_wp27j")]
+transform = Transform3D(-0.0491111, 0, 0.998793, 0, 1, 0, -0.998793, 0, -0.0491111, -89.2691, 8.79795, 53.3185)
+
+[node name="Biplan3" parent="." instance=ExtResource("3_wp27j")]
+transform = Transform3D(-0.0491111, 0, 0.998793, 0, 1, 0, -0.998793, 0, -0.0491111, -83.7131, 8.79795, 61.062)
+
+[node name="Biplan4" parent="." instance=ExtResource("3_wp27j")]
+transform = Transform3D(0.822065, 0, -0.569394, 0, 1, 0, 0.569394, 0, 0.822065, -79.6031, 8.79795, 60.0757)