mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
feat: add Nebula and Asteroid Field arenas
Introduce arena_02 (Nebula) and arena_03 (Asteroid Field) alongside arena_01, listed in a new ArenaRegistry (scripts/arena_registry.gd) as the single source of truth for available arenas. Free Play lets the player pick an arena from the main menu; Match/Spectate each pick one at random per session; Training keeps its own fixed arena_01. Nebula gets an RL-style visual treatment: a near-invisible glass boundary material shared by all arenas, a baked equirect nebula sky texture, a drifting-dust particle system, and a Blender-modeled station/debris/planet decoration set. GameMode gains a _get_arena_scene_path() hook so modes can instantiate their arena in code instead of hardcoding it in the scene.
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://d2mrvrt1h305x"
|
||||
path="res://.godot/imported/nebula_decoration.blend-2f6fe244eedad258eac5faa9b1684142.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/blender_models/nebula_decoration.blend"
|
||||
dest_files=["res://.godot/imported/nebula_decoration.blend-2f6fe244eedad258eac5faa9b1684142.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
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=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
blender/nodes/visible=0
|
||||
blender/nodes/active_collection_only=false
|
||||
blender/nodes/punctual_lights=true
|
||||
blender/nodes/cameras=true
|
||||
blender/nodes/custom_properties=true
|
||||
blender/nodes/modifiers=1
|
||||
blender/meshes/vertex_colors=1
|
||||
blender/meshes/uvs=true
|
||||
blender/meshes/normals=true
|
||||
blender/meshes/export_geometry_nodes_instances=false
|
||||
blender/meshes/gpu_instances=false
|
||||
blender/meshes/tangents=true
|
||||
blender/meshes/skins=2
|
||||
blender/meshes/export_bones_deforming_mesh_only=false
|
||||
blender/materials/unpack_enabled=true
|
||||
blender/materials/export_materials=1
|
||||
blender/animation/limit_playback=true
|
||||
blender/animation/always_sample=true
|
||||
blender/animation/group_tracks=true
|
||||
gltf/naming_version=2
|
||||
gltf/texture_map_mode=1
|
||||
Binary file not shown.
@@ -0,0 +1,45 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://fc85v6374c1"
|
||||
path="res://.godot/imported/nebula_debris.glb-39af77a17c0998b5b73172577d906cb9.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/nebula_debris.glb"
|
||||
dest_files=["res://.godot/imported/nebula_debris.glb-39af77a17c0998b5b73172577d906cb9.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
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=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
Binary file not shown.
@@ -0,0 +1,45 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bpfwv7fse77wk"
|
||||
path="res://.godot/imported/nebula_planet.glb-2431590907ff85cf1057e7d6ad614ed7.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/nebula_planet.glb"
|
||||
dest_files=["res://.godot/imported/nebula_planet.glb-2431590907ff85cf1057e7d6ad614ed7.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
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=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cr3feyv7yldjc"
|
||||
path="res://.godot/imported/nebula_planet_planet_surface.png-4b68cca75d2d22d25510c7eb277c0b85.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "44a2e26010b529339e264deecde957e0"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/nebula_planet_planet_surface.png"
|
||||
dest_files=["res://.godot/imported/nebula_planet_planet_surface.png-4b68cca75d2d22d25510c7eb277c0b85.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
@@ -0,0 +1,45 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dn8tj3iw0io7e"
|
||||
path="res://.godot/imported/nebula_station.glb-fa9a6dd87ae3789d04205b52215e2e76.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/nebula_station.glb"
|
||||
dest_files=["res://.godot/imported/nebula_station.glb-fa9a6dd87ae3789d04205b52215e2e76.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
mesh_library/use_node_names_as_mesh_names=false
|
||||
array_mesh/deduplicate_surfaces=true
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
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=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
gltf/texture_map_mode=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://damajpvo00prm"
|
||||
path="res://.godot/imported/particle_glow.png-e6c07ae4c700896e29ebe96adf51fa5c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/textures/particle_glow.png"
|
||||
dest_files=["res://.godot/imported/particle_glow.png-e6c07ae4c700896e29ebe96adf51fa5c.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bxsadfd04v6r2"
|
||||
path="res://.godot/imported/planet_surface.png-d3aaf8310508e4c85b26a3d093bcf9c8.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/textures/planet_surface.png"
|
||||
dest_files=["res://.godot/imported/planet_surface.png-d3aaf8310508e4c85b26a3d093bcf9c8.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 MiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dc445qysyxwwv"
|
||||
path="res://.godot/imported/sky_nebula.png-70f974ab520aaa5206eef67a936b662e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/textures/sky_nebula.png"
|
||||
dest_files=["res://.godot/imported/sky_nebula.png-70f974ab520aaa5206eef67a936b662e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -10,7 +10,7 @@ roughness = 0.6
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_field"]
|
||||
transparency = 1
|
||||
shading_mode = 0
|
||||
albedo_color = Color(0, 0.368627, 1, 0.08)
|
||||
albedo_color = Color(0.6, 0.75, 1, 0.035)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_floor"]
|
||||
material = SubResource("StandardMaterial3D_floor")
|
||||
|
||||
@@ -118,4 +118,5 @@ common/physics_interpolation=true
|
||||
|
||||
[autoload]
|
||||
|
||||
|
||||
GameSettings="*res://scripts/game_settings.gd"
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
[gd_scene load_steps=15 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/arena.gd" id="1_iywne"]
|
||||
[ext_resource type="PackedScene" path="res://objects/arena_boundary.tscn" id="2_bndry"]
|
||||
[ext_resource type="PackedScene" uid="uid://cofdcxo5170rs" path="res://objects/goal.tscn" id="6_p57ef"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/sky_nebula.png" id="7_nebula"]
|
||||
[ext_resource type="PackedScene" path="res://assets/models/nebula_station.glb" id="8_station"]
|
||||
[ext_resource type="PackedScene" path="res://assets/models/nebula_debris.glb" id="9_debris"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/particle_glow.png" id="10_dust"]
|
||||
[ext_resource type="PackedScene" path="res://assets/models/nebula_planet.glb" id="11_planet"]
|
||||
|
||||
[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_nebula"]
|
||||
panorama = ExtResource("7_nebula")
|
||||
|
||||
[sub_resource type="Sky" id="Sky_nebula"]
|
||||
sky_material = SubResource("PanoramaSkyMaterial_nebula")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_nebula"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_nebula")
|
||||
ambient_light_source = 2
|
||||
ambient_light_color = Color(0.65, 0.35, 0.75, 1)
|
||||
ambient_light_energy = 0.45
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_dust"]
|
||||
transparency = 1
|
||||
shading_mode = 0
|
||||
billboard_mode = 1
|
||||
vertex_color_use_as_albedo = true
|
||||
albedo_color = Color(1, 0.75, 0.9, 1)
|
||||
albedo_texture = ExtResource("10_dust")
|
||||
emission_enabled = true
|
||||
emission = Color(1, 0.6, 0.85, 1)
|
||||
emission_energy_multiplier = 2.5
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_dust"]
|
||||
material = SubResource("StandardMaterial3D_dust")
|
||||
size = Vector2(0.4, 0.4)
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_dust"]
|
||||
offsets = PackedFloat32Array(0, 0.15, 0.85, 1)
|
||||
colors = PackedColorArray(1, 1, 1, 0, 1, 1, 1, 0.55, 1, 1, 1, 0.55, 1, 1, 1, 0)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_dust"]
|
||||
gradient = SubResource("Gradient_dust")
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_dust"]
|
||||
emission_shape = 3
|
||||
emission_box_extents = Vector3(26, 11, 28)
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 180.0
|
||||
initial_velocity_min = 0.1
|
||||
initial_velocity_max = 0.45
|
||||
gravity = Vector3(0, 0, 0)
|
||||
turbulence_enabled = true
|
||||
turbulence_noise_strength = 0.7
|
||||
turbulence_noise_scale = 0.6
|
||||
turbulence_influence_min = 0.35
|
||||
turbulence_influence_max = 0.65
|
||||
scale_min = 0.5
|
||||
scale_max = 1.6
|
||||
color_ramp = SubResource("GradientTexture1D_dust")
|
||||
|
||||
[node name="Arena" type="Node3D"]
|
||||
script = ExtResource("1_iywne")
|
||||
|
||||
[node name="Boundary" parent="." instance=ExtResource("2_bndry")]
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||
transform = Transform3D(0.866025, -0.383022, 0.321394, 0, 0.642788, 0.766044, -0.5, -0.663414, 0.55667, 0, 11, 0)
|
||||
light_color = Color(0.85, 0.75, 1, 1)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_nebula")
|
||||
|
||||
[node name="GoalTeam0" parent="." instance=ExtResource("6_p57ef")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.79, 18)
|
||||
|
||||
[node name="GoalTeam1" parent="." instance=ExtResource("6_p57ef")]
|
||||
transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.79, -18)
|
||||
team = 1
|
||||
|
||||
[node name="BallSpawn" type="Marker3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
|
||||
|
||||
[node name="SpawnsTeam0" type="Node3D" parent="."]
|
||||
|
||||
[node name="Spawn1" type="Marker3D" parent="SpawnsTeam0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.8, 13.5)
|
||||
|
||||
[node name="SpawnsTeam1" type="Node3D" parent="."]
|
||||
|
||||
[node name="Spawn1" type="Marker3D" parent="SpawnsTeam1"]
|
||||
transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 2.8, -13.5)
|
||||
|
||||
[node name="NebulaDust" type="GPUParticles3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5, 0)
|
||||
amount = 500
|
||||
lifetime = 22.0
|
||||
preprocess = 8.0
|
||||
randomness = 1.0
|
||||
visibility_aabb = AABB(-30, -14, -32, 60, 28, 64)
|
||||
process_material = SubResource("ParticleProcessMaterial_dust")
|
||||
draw_pass_1 = SubResource("QuadMesh_dust")
|
||||
|
||||
[node name="Decoration" type="Node3D" parent="."]
|
||||
|
||||
[node name="NebulaStationA" parent="Decoration" instance=ExtResource("8_station")]
|
||||
transform = Transform3D(1.8, 0, 0, 0, 1.8, 0, 0, 0, 1.8, 28, 5, -6)
|
||||
rotation = Vector3(0, -0.436332, 0)
|
||||
|
||||
[node name="NebulaDebrisA" parent="Decoration" instance=ExtResource("9_debris")]
|
||||
transform = Transform3D(1.2, 0, 0, 0, 1.2, 0, 0, 0, 1.2, -22, 9, 11)
|
||||
rotation = Vector3(0.261799, 0.698132, 0.087266)
|
||||
|
||||
[node name="NebulaDebrisB" parent="Decoration" instance=ExtResource("9_debris")]
|
||||
transform = Transform3D(0.8, 0, 0, 0, 0.8, 0, 0, 0, 0.8, 34, 3, 15)
|
||||
rotation = Vector3(-0.174533, 1.919862, 0.349066)
|
||||
|
||||
[node name="NebulaDebrisC" parent="Decoration" instance=ExtResource("9_debris")]
|
||||
transform = Transform3D(1.5, 0, 0, 0, 1.5, 0, 0, 0, 1.5, -30, 11, -21)
|
||||
rotation = Vector3(0.087266, -1.047198, -0.261799)
|
||||
|
||||
[node name="NebulaStationB" parent="Decoration" instance=ExtResource("8_station")]
|
||||
transform = Transform3D(1.5, 0, 0, 0, 1.5, 0, 0, 0, 1.5, -32, 7, 9)
|
||||
rotation = Vector3(0.05, 1.134464, -0.02)
|
||||
|
||||
[node name="NebulaStationC" parent="Decoration" instance=ExtResource("8_station")]
|
||||
transform = Transform3D(1.3, 0, 0, 0, 1.3, 0, 0, 0, 1.3, 9, 8, 42)
|
||||
rotation = Vector3(-0.03, 2.740167, 0.06)
|
||||
|
||||
[node name="NebulaDebrisD" parent="Decoration" instance=ExtResource("9_debris")]
|
||||
transform = Transform3D(1.0, 0, 0, 0, 1.0, 0, 0, 0, 1.0, 13, 15, -35)
|
||||
rotation = Vector3(0.4, 1.3, -0.2)
|
||||
|
||||
[node name="NebulaDebrisE" parent="Decoration" instance=ExtResource("9_debris")]
|
||||
transform = Transform3D(0.9, 0, 0, 0, 0.9, 0, 0, 0, 0.9, -11, 4, 33)
|
||||
rotation = Vector3(-0.3, -0.9, 0.15)
|
||||
|
||||
[node name="NebulaPlanet" parent="Decoration" instance=ExtResource("11_planet")]
|
||||
transform = Transform3D(30, 0, 0, 0, 30, 0, 0, 0, 30, -55, 38, -80)
|
||||
@@ -0,0 +1,54 @@
|
||||
[gd_scene load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/arena.gd" id="1_iywne"]
|
||||
[ext_resource type="PackedScene" path="res://objects/arena_boundary.tscn" id="2_bndry"]
|
||||
[ext_resource type="PackedScene" uid="uid://cofdcxo5170rs" path="res://objects/goal.tscn" id="6_p57ef"]
|
||||
[ext_resource type="Shader" path="res://shaders/sky_asteroid_field.gdshader" id="7_asteroid"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_asteroid"]
|
||||
shader = ExtResource("7_asteroid")
|
||||
|
||||
[sub_resource type="Sky" id="Sky_asteroid"]
|
||||
sky_material = SubResource("ShaderMaterial_asteroid")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_asteroid"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_asteroid")
|
||||
ambient_light_source = 2
|
||||
ambient_light_color = Color(0.6, 0.45, 0.3, 1)
|
||||
ambient_light_energy = 0.3
|
||||
|
||||
[node name="Arena" type="Node3D"]
|
||||
script = ExtResource("1_iywne")
|
||||
|
||||
[node name="Boundary" parent="." instance=ExtResource("2_bndry")]
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||
transform = Transform3D(0.866025, -0.383022, 0.321394, 0, 0.642788, 0.766044, -0.5, -0.663414, 0.55667, 0, 11, 0)
|
||||
light_color = Color(1, 0.85, 0.65, 1)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_asteroid")
|
||||
|
||||
[node name="GoalTeam0" parent="." instance=ExtResource("6_p57ef")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.79, 18)
|
||||
|
||||
[node name="GoalTeam1" parent="." instance=ExtResource("6_p57ef")]
|
||||
transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.79, -18)
|
||||
team = 1
|
||||
|
||||
[node name="BallSpawn" type="Marker3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
|
||||
|
||||
[node name="SpawnsTeam0" type="Node3D" parent="."]
|
||||
|
||||
[node name="Spawn1" type="Marker3D" parent="SpawnsTeam0"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.8, 13.5)
|
||||
|
||||
[node name="SpawnsTeam1" type="Node3D" parent="."]
|
||||
|
||||
[node name="Spawn1" type="Marker3D" parent="SpawnsTeam1"]
|
||||
transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 2.8, -13.5)
|
||||
|
||||
[node name="Decoration" type="Node3D" parent="."]
|
||||
@@ -1,12 +1,10 @@
|
||||
[gd_scene load_steps=4 format=3]
|
||||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/free_play.gd" id="1_fp"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/arena_01.tscn" id="2_fp"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8kak2l3m4n5" path="res://scenes/HUD.tscn" id="3_fp"]
|
||||
|
||||
[node name="FreePlay" type="Node3D"]
|
||||
script = ExtResource("1_fp")
|
||||
|
||||
[node name="Arena" parent="." instance=ExtResource("2_fp")]
|
||||
|
||||
[node name="HUD" parent="." instance=ExtResource("3_fp")]
|
||||
|
||||
@@ -53,6 +53,20 @@ theme_override_font_sizes/font_size = 13
|
||||
text = "Solo practice — no timer, R resets the ball"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ArenaRow" type="HBoxContainer" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="ArenaLabel" type="Label" parent="CenterContainer/VBoxContainer/ArenaRow"]
|
||||
layout_mode = 2
|
||||
text = "Arena"
|
||||
|
||||
[node name="ArenaDropdown" type="OptionButton" parent="CenterContainer/VBoxContainer/ArenaRow"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MatchSeparator" type="HSeparator" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
[gd_scene load_steps=4 format=3]
|
||||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/match_mode.gd" id="1_m"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/arena_01.tscn" id="2_m"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8kak2l3m4n5" path="res://scenes/HUD.tscn" id="3_m"]
|
||||
|
||||
[node name="Match" type="Node3D"]
|
||||
script = ExtResource("1_m")
|
||||
bot_model_path = "res://bots/promoted/easy.json"
|
||||
|
||||
[node name="Arena" parent="." instance=ExtResource("2_m")]
|
||||
|
||||
[node name="HUD" parent="." instance=ExtResource("3_m")]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[gd_scene load_steps=4 format=3]
|
||||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/spectate_mode.gd" id="1_s"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/arena_01.tscn" id="2_s"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8kak2l3m4n5" path="res://scenes/HUD.tscn" id="3_s"]
|
||||
|
||||
[node name="Spectate" type="Node3D"]
|
||||
@@ -9,6 +8,5 @@ script = ExtResource("1_s")
|
||||
bot_a_model_path = "res://bots/promoted/easy.json"
|
||||
bot_b_model_path = "res://bots/promoted/easy.json"
|
||||
|
||||
[node name="Arena" parent="." instance=ExtResource("2_s")]
|
||||
|
||||
[node name="HUD" parent="." instance=ExtResource("3_s")]
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
class_name ArenaRegistry
|
||||
|
||||
# Single source of truth for available arenas: the Free Play menu dropdown
|
||||
# lists these, and Match/Spectate pick one at random each session. Training
|
||||
# is exempt — training.tscn keeps its own fixed arena_01 child.
|
||||
const ARENAS := [
|
||||
{"name": "Starfield", "path": "res://scenes/arena_01.tscn"},
|
||||
{"name": "Nebula", "path": "res://scenes/arena_02.tscn"},
|
||||
{"name": "Asteroid Field", "path": "res://scenes/arena_03.tscn"},
|
||||
]
|
||||
|
||||
|
||||
static func random_path() -> String:
|
||||
return ARENAS[randi() % ARENAS.size()]["path"]
|
||||
@@ -0,0 +1 @@
|
||||
uid://orkh5g5828cv
|
||||
@@ -4,6 +4,12 @@ extends GameMode
|
||||
# Rocket League's free play. R resets the ball, Esc returns to the menu.
|
||||
|
||||
|
||||
func _get_arena_scene_path() -> String:
|
||||
if not GameSettings.selected_arena_path.is_empty():
|
||||
return GameSettings.selected_arena_path
|
||||
return ArenaRegistry.ARENAS[0]["path"]
|
||||
|
||||
|
||||
func _start() -> void:
|
||||
spawn_ball()
|
||||
var player_ship := spawn_ship(0, 0, PlayerShipController.new())
|
||||
|
||||
@@ -26,6 +26,11 @@ func _ready():
|
||||
if child is Arena:
|
||||
arena = child
|
||||
break
|
||||
if not arena:
|
||||
var path := _get_arena_scene_path()
|
||||
if not path.is_empty():
|
||||
arena = (load(path) as PackedScene).instantiate()
|
||||
add_child(arena)
|
||||
if not arena:
|
||||
push_error("GameMode: scene has no Arena child")
|
||||
return
|
||||
@@ -34,6 +39,13 @@ func _ready():
|
||||
_start()
|
||||
|
||||
|
||||
# Virtual: subclasses whose scene has no fixed Arena child override this to
|
||||
# pick which arena scene to instantiate in code (see ArenaRegistry). Modes
|
||||
# that keep a fixed Arena child (training.tscn) never call this.
|
||||
func _get_arena_scene_path() -> String:
|
||||
return ""
|
||||
|
||||
|
||||
# Virtual: subclasses spawn their ball/ships/camera here.
|
||||
func _start() -> void:
|
||||
pass
|
||||
|
||||
@@ -21,3 +21,7 @@ var dev_bot_override_path: String = ""
|
||||
# Spectate (bot vs bot) policies chosen in the main menu; empty = mode export.
|
||||
var spectate_bot_a_path: String = ""
|
||||
var spectate_bot_b_path: String = ""
|
||||
|
||||
# Arena chosen in the main menu for Free Play; empty = ArenaRegistry's default.
|
||||
# Match/Spectate ignore this and pick a random arena each session instead.
|
||||
var selected_arena_path: String = ""
|
||||
|
||||
@@ -18,6 +18,7 @@ const DIFFICULTIES := [
|
||||
]
|
||||
|
||||
@onready var difficulty_dropdown: OptionButton = %DifficultyDropdown
|
||||
@onready var arena_dropdown: OptionButton = %ArenaDropdown
|
||||
@onready var dev_section: Control = %DevSection
|
||||
@onready var dev_bot_dropdown: OptionButton = %DevBotDropdown
|
||||
@onready var bot_a_dropdown: OptionButton = %BotADropdown
|
||||
@@ -26,6 +27,7 @@ const DIFFICULTIES := [
|
||||
|
||||
func _ready() -> void:
|
||||
_populate_difficulty_dropdown()
|
||||
_populate_arena_dropdown()
|
||||
dev_section.visible = OS.is_debug_build()
|
||||
if dev_section.visible:
|
||||
var bots := _list_bots()
|
||||
@@ -47,6 +49,19 @@ func _populate_difficulty_dropdown() -> void:
|
||||
difficulty_dropdown.select(selected)
|
||||
|
||||
|
||||
# Free Play only — Match/Spectate pick a random arena in code instead.
|
||||
func _populate_arena_dropdown() -> void:
|
||||
arena_dropdown.clear()
|
||||
for tier in ArenaRegistry.ARENAS:
|
||||
arena_dropdown.add_item(tier["name"])
|
||||
var selected := 0
|
||||
for i in ArenaRegistry.ARENAS.size():
|
||||
if ArenaRegistry.ARENAS[i]["path"] == GameSettings.selected_arena_path:
|
||||
selected = i
|
||||
break
|
||||
arena_dropdown.select(selected)
|
||||
|
||||
|
||||
# Lists bot checkpoints as paths relative to BOTS_DIR, including the
|
||||
# top-level curriculum checkpoints and anything under promoted/.
|
||||
func _list_bots() -> Array[String]:
|
||||
@@ -93,6 +108,8 @@ func _selected_path(dropdown: OptionButton) -> String:
|
||||
|
||||
|
||||
func _on_free_play_pressed() -> void:
|
||||
var chosen: Dictionary = ArenaRegistry.ARENAS[0] if arena_dropdown.selected < 0 else ArenaRegistry.ARENAS[arena_dropdown.selected]
|
||||
GameSettings.selected_arena_path = chosen["path"]
|
||||
get_tree().change_scene_to_file("res://scenes/free_play.tscn")
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ var match_timer: Timer
|
||||
var _in_overtime := false
|
||||
|
||||
|
||||
func _get_arena_scene_path() -> String:
|
||||
return ArenaRegistry.random_path()
|
||||
|
||||
|
||||
func _start() -> void:
|
||||
spawn_ball()
|
||||
var player_ship := spawn_ship(0, 0, PlayerShipController.new())
|
||||
|
||||
@@ -19,6 +19,10 @@ signal score_changed(score: Dictionary)
|
||||
var score := {0: 0, 1: 0}
|
||||
|
||||
|
||||
func _get_arena_scene_path() -> String:
|
||||
return ArenaRegistry.random_path()
|
||||
|
||||
|
||||
func _start() -> void:
|
||||
spawn_ball()
|
||||
# The main menu's dropdown selections (GameSettings autoload) win over the
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
shader_type sky;
|
||||
|
||||
// Procedural dusty void: dim rocky-belt haze with a warm horizon glow, sparser
|
||||
// stars than arena_01 for a grittier, more enclosed feel.
|
||||
void sky() {
|
||||
vec3 dir = EYEDIR;
|
||||
vec3 col = vec3(0.015, 0.012, 0.01);
|
||||
col += vec3(0.09, 0.05, 0.02) * pow(1.0 - abs(dir.y), 4.0);
|
||||
vec3 cell = floor(dir * 220.0);
|
||||
float h = fract(sin(dot(cell, vec3(12.9898, 78.233, 45.164))) * 43758.5453);
|
||||
if (h > 0.9985) {
|
||||
vec3 in_cell = fract(dir * 220.0) - 0.5;
|
||||
float star = smoothstep(0.35, 0.0, length(in_cell));
|
||||
col += vec3(star * (0.4 + fract(h * 999.0)) * 0.8);
|
||||
}
|
||||
COLOR = col;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://bg7diy2ywqkbd
|
||||
@@ -22,5 +22,30 @@ The training pipeline is built — see `TRAINING.md` (self-play PPO via the vend
|
||||
|
||||
## General
|
||||
|
||||
- [x] `GameSettings` autoload (`scripts/game_settings.gd`) added for exactly the cross-scene case this item anticipated — passing menu selections (bot/difficulty, spectate matchup) into match/spectate modes. Still the only autoload; keep it that way unless another cross-scene need shows up.
|
||||
- [ ] More arenas: `arena_01.tscn` is the template — an arena is a setting (sky, lighting, decoration) + two team-tagged goals on the standard goal lines + spawn markers + an instance of the shared `objects/arena_boundary.tscn`, with no rules or state. All arenas share the standard play volume defined by `ArenaBoundary`'s constants.
|
||||
- [x] More arenas: `arena_02.tscn` (Nebula) and `arena_03.tscn` (Asteroid Field) added alongside `arena_01.tscn`, all following the same template (goals/spawns/`arena_boundary.tscn` unchanged) and listed in `scripts/arena_registry.gd`. Free Play lets the player pick an arena from the main menu; Match/Spectate each pick one at random per session; Training keeps its own fixed `arena_01.tscn` untouched.
|
||||
- [x] arena_02 (Nebula) redone RL-style: `arena_boundary.tscn`'s field material is now near-invisible glass (shared by all arenas), the sky is a baked photoreal-style equirect nebula texture (`assets/textures/sky_nebula.png`, procedurally generated offline — no copied astrophotography) instead of a procedural cell-shader, a `NebulaDust` `GPUParticles3D` gives a drifting-motes "weather" effect, and the `Decoration` node holds a station/debris/planet environment (Blender-modeled, `assets/blender_models/nebula_decoration.blend` → `assets/models/nebula_*.glb`) visible in multiple directions. `arena_01`/`arena_03` still use the older embedded-shader sky — same treatment for those is unstarted follow-up work.
|
||||
|
||||
## Visual quality pass (arena_02 critique follow-up)
|
||||
|
||||
A subagent ran the game and critiqued arena_02 head-on against Rocket League 2 in Unreal Engine 5: verdict was "a flat-shaded, unlit-looking blockout dressed up with two nice noise textures" — not fundamentally unfixable in Godot (SDFGI/Nanite-tier GI and hero-sculpted geometry aside), but several concrete gaps. Work through these one at a time, in this order (cheapest/highest-impact first). Each item has a ready-to-use prompt — paste it into a fresh session to tackle just that piece. The texture-generation Python scripts used for `sky_nebula.png`/`planet_surface.png` currently only exist in an ephemeral scratchpad, not the repo — the first item that touches them should commit a copy into the repo (e.g. `tools/textures/`) so they're reproducible.
|
||||
|
||||
- [ ] **Post-processing pipeline** (no models needed — cheapest, highest-impact item on the list). Nothing currently sits on top of the raw render: no bloom/glow, no AA, no color grading, no vignette beyond the floor's baked fade.
|
||||
Prompt: "In Cosmic Clash (Godot 4.7), the arena Environment resources (start with `Game/scenes/arena_02.tscn`'s `Environment_nebula`, then apply consistently across `arena_01`/`arena_03` too) currently have no post-processing. Enable and tune Glow (bloom) so the nebula core, emissive accent strips on the station model, and bright stars actually bleed light; enable Adjustments (`adjustment_enabled`) for a subtle contrast/saturation grade. Godot's `Environment` has no built-in vignette property — if that's still wanted, it needs a small custom full-screen shader (a `CompositorEffect` or a screen-space `ColorRect` overlay), not a toggle; treat it as a separate, smaller sub-task rather than assuming it's free. Also enable FXAA and/or TAA via `project.godot`'s `rendering/anti_aliasing/quality/*` settings project-wide to fix the aliased hard edges visible on ship geometry. Motion blur and depth-of-field are deliberately out of scope here — Godot has no built-in equivalent, and faking either well needs a custom `CompositorEffect`, which is a much bigger task than this pass. Verify with before/after screenshots via godot-mcp (`run_project` on `free_play.tscn` with the Nebula arena selected) — don't just eyeball the editor, actually run the game."
|
||||
|
||||
- [ ] **De-duplicate the nebula sky's star sprites**. Every bright star in `sky_nebula.png` is the exact same diffraction-spike stamp at the same size/brightness, just relocated — called out as "the single most amateur-looking tell in the whole scene" once you look for more than a second.
|
||||
Prompt: "Commit the nebula sky texture generator (currently only in an ephemeral scratchpad — recreate it if needed: numpy/Pillow script generating a 4096x2048 equirect nebula via layered FFT/domain-warped noise, color-graded, with drawn hero stars) into the repo at `tools/textures/gen_nebula_sky.py`. Fix the hero-star drawing loop so each star's diffraction-spike stamp gets randomized rotation, spike length, and brightness instead of reusing one identical stamp at every location. Regenerate `Game/assets/textures/sky_nebula.png`, reimport, and confirm via an in-game screenshot that the repeated-stamp tell is gone."
|
||||
|
||||
- [ ] **Real PBR lighting + materials, fresnel glass boundary** (no new models — material/lighting setup only). Everything is flat-shaded/unshaded with a single directional light: no GI, no reflections, no bounce/AO, and the "glass" boundary is a flat unshaded tinted overlay with no fresnel rim or reflection.
|
||||
Prompt: "In Cosmic Clash, enable SDFGI and add reflection probes to the arena scenes (start with `arena_02.tscn`) so surfaces get real bounce lighting/reflections instead of flat ambient. Convert `arena_boundary.tscn`'s `StandardMaterial3D_field` from unshaded to a proper shaded material with a fresnel-based rim highlight (bright at grazing angles, near-invisible face-on) so it reads as glass rather than a tinted overlay — check the performance impact of moving it off unshaded, given it's a large always-visible surface. Verify visually via godot-mcp screenshots, and confirm headless runs (`free_play`/`match`/`spectate`/`training`) still show zero errors."
|
||||
|
||||
- [ ] **Greeble/detail pass on the station + debris models** (Blender remodel — explicit models to redo). `nebula_station.glb`/`nebula_debris.glb` are unmistakably Blender primitive kitbashes up close: plain boxes/cylinders with bevel modifiers, no panel lines, damage decals, or surface detail, and debris chunks look undifferentiated from the station.
|
||||
Prompt: "Using the blender MCP, rebuild `nebula_station` (source in `Game/assets/blender_models/nebula_decoration.blend`) with actual surface detail: greebled panel-line insets via bmesh inset-and-extrude on selected faces (more scriptable and robust than chained boolean cuts — prefer this over booleans for the main detailing pass), and 2-3 emissive window/light strips distinct from the existing single accent strip. A full normal-map/AO bake pipeline (low-poly + high-poly pair, UV unwrap, bake settings) is the ideal AAA-style finish but is fragile to script end-to-end in one pass — attempt it, but if it proves too unreliable, fall back to the inset/extrude geometric detail alone plus a simple tiled trim-sheet-style texture rather than forcing a bake that doesn't work. Give `nebula_debris` chunks a rockier/damaged material distinct from the station's clean hull (darker, rougher, maybe scorch-mark variation) so they read as separate debris rather than clones of the station's material. Re-export both to `Game/assets/models/nebula_station.glb`/`nebula_debris.glb`, re-save the shared `.blend`, and re-verify placement/transforms in `arena_02.tscn` still look right (screenshot check)."
|
||||
|
||||
- [ ] **Richer nebula sky + planet surface textures** (extends the existing procedural generation — no new asset types). `sky_nebula.png` is essentially one noise-filter pass over a bright core; `planet_surface.png` is a flat gradient with a single vortex swirl and no bands, craters, or day/night terminator.
|
||||
Prompt: "Extend the nebula/planet texture generators (see `tools/textures/` once committed, per the star-sprite TODO item above) with more detail layers: for the sky, add a second/third dust-lane layer at a different scale plus subtler color variation within the bright core (real nebulae aren't one flat color); for the planet, add a proper lit/unlit terminator gradient (the side facing the arena's directional light should read brighter), more band variation at different latitudes, and a couple more storm-vortex features so it doesn't read as a single gradient with one twist. Regenerate both textures, reimport, and screenshot-verify."
|
||||
|
||||
- [ ] **Particle lighting response for the nebula dust ("weather") effect** (no models — particle material/shader only). The `NebulaDust` `GPUParticles3D` motes are generic soft glow sprites with no lighting interaction, no depth-based fade, and no secondary motion (no sparkle/color shift).
|
||||
Prompt: "In `arena_02.tscn`'s `NebulaDust` particle system, replace the current unshaded glow-sprite material with a custom shader that fakes lighting response — note the sprites are camera-facing billboards, so they have no fixed world-space normal and true per-pixel PBR lighting won't behave like it would on a solid mesh. A practical approach: derive a fake per-pixel normal from the quad's local UV (as if each sprite were a small sphere/puff, same trick used for 2D lit particle effects) and light that against the directional light + a fixed 'glow color' bias toward the nebula core direction, rather than relying on the mesh's real (camera-facing) normal. Add soft-particle depth fade (compare particle depth to the depth buffer) so motes don't hard-clip through the boundary/decoration geometry, and add subtle per-particle brightness flicker via the color ramp or shader for sparkle. Verify visually via godot-mcp screenshots that the dust reads as lit rather than flat-glowing, from a couple of different camera angles (billboard lighting tricks can look wrong from some angles even when right from others)."
|
||||
|
||||
- [ ] **Ship + ball visual pass** (Blender remodel — explicit models to make). The ship (`Game/objects/ship.tscn`) is *not* a single placeholder box — it's already 5 separate primitive `MeshInstance3D` parts (`Hull` a `BoxMesh`, `Nose` a `PrismMesh`, `Canopy` and `EngineGlow` boxes, `TailFin` a thin box), several with emission already (the cyan canopy, the orange `EngineGlow`, team-tinted `Nose`/`TailFin`). It's the same underlying critique as everything else, though: plain primitives, no greebling/panel detail, no imported model — just built further already than the write-up above implied.
|
||||
Prompt: "Using the blender MCP, model an actual low-poly-but-detailed ship hull (greebled fuselage panel lines, a proper cockpit canopy shape, 1-2 engine nacelles with emissive exhaust glow) to replace `Game/objects/ship.tscn`'s current 5 primitive `MeshInstance3D` parts (`Hull`/`Nose`/`Canopy`/`TailFin`/`EngineGlow`), built the same procedural way as the arena decoration (bpy/bmesh primitives + bevels + inset/extrude detail — kept original, no borrowed/asset-store art). Two hard constraints this must respect: (1) **`scripts/ship.gd`'s `_apply_team_color()` recolors two child nodes by exact name, `get_node_or_null(\"Nose\")` and `get_node_or_null(\"TailFin\")`, at runtime per-team** (team 0 blue, team 1 orange, set via `game_mode.gd`'s `ship.team = team`) — either keep two `MeshInstance3D` parts of the new model literally named `Nose` and `TailFin` so this keeps working untouched, or update `_apply_team_color()`'s node-name list in the same change if the new model's parts are named differently; don't let this silently break. (2) **Do not touch `ship.tscn`'s single `CollisionShape3D` (`BoxShape3D`, size `1 x 1 x 4`) or the `RigidBody3D`'s `mass = 5.0`/`inertia = (1,1,1)`/`physics_material_override`** — those are exactly what the trained RL bots and flight feel are tuned against (per CLAUDE.md). Keep the new visual mesh's silhouette reasonably close to that `1 x 1 x 4` box so the ship doesn't visually stick out of or float inside its own collision bounds. Separately, audit the existing gold ball's material (`Game/assets/models/gold_ball.glb`/`.res`, referenced from `Game/objects/ball.tscn`'s `MeshInstance3D` with no scene-level material override, so its look currently comes entirely from whatever's embedded in the imported mesh) and upgrade it to a proper metallic/emissive-accent PBR look if it turns out flat/unshaded once the lighting pass above lands — don't assume it needs work without checking first. Verify both visually (screenshot, including a look at team-color recoloring in Match/Spectate) and via a headless run of `free_play.tscn`/`training.tscn` to confirm physics/RL behavior is unaffected."
|
||||
|
||||
Reference in New Issue
Block a user