Геймпад Xbox, меню паузы с настройками, новый хаб с музыкой
- Геймпад: раскладка под паркур и гонки, обзор правым стиком, навигация по меню (A/B/крестовина) - Меню паузы (Esc/Start): выход в хаб, настройки (мышь, стик, FOV, громкость, инверсия), справка по управлению - Хаб 48x32x10 м: колонны, окна с городом, голограмма, лавки; музыка «Local Forecast - Elevator» (Kevin MacLeod, CC-BY) - Тесты: pause_test, pad_menu_test, hub_tour; README обновлён Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Музыка хаба
|
||||
|
||||
«Local Forecast - Elevator» — Kevin MacLeod (incompetech.com)
|
||||
Лицензия: Creative Commons: By Attribution (CC BY) — http://creativecommons.org/licenses/by/4.0/
|
||||
Источник: https://incompetech.com/music/royalty-free/mp3-royaltyfree/Local%20Forecast%20-%20Elevator.mp3
|
||||
Обработка: моно, 32 кГц, Ogg Vorbis q1 (`ffmpeg -i исходник.mp3 -vn -map_metadata -1 -ac 1 -ar 32000 -c:a libvorbis -q:a 1 music.ogg`).
|
||||
При публикации игры нужно указывать автора.
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://ch6afg8c4xjr4"
|
||||
path="res://.godot/imported/music.ogg-07da3df5f1653a19addb3d04617388e7.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://maps/hub/audio/music.ogg"
|
||||
dest_files=["res://.godot/imported/music.ogg-07da3df5f1653a19addb3d04617388e7.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
+48
-14
@@ -1,7 +1,8 @@
|
||||
extends Node3D
|
||||
# Стартовая комната: ничего тяжёлого, только пол, стены и порталы на карты. Сами карты качаются
|
||||
# отдельными пакетами при входе в портал (World/map_loader.gd) — поэтому игра стартует быстро.
|
||||
# Чтобы добавить карту — допиши запись в PORTALS: порталы встанут в ряд по центру (влезает 6).
|
||||
# Чтобы добавить карту — допиши запись в PORTALS: порталы встанут в ряд по центру (влезает 7).
|
||||
# Оформление (колонны, окна, голограмма, лавки) — hub_decor.gd; музыка «как в лифте» играет по кругу.
|
||||
|
||||
const PORTALS := [
|
||||
{"title": "SANDBOX", "map": "world", "scene": "res://maps/sandbox/sandbox.tscn", "pack": "world.pck", "color": Color(0.2, 0.9, 1.0)},
|
||||
@@ -11,11 +12,15 @@ const PORTALS := [
|
||||
{"title": "RACE 2", "map": "race2", "scene": "res://maps/race2/race2.tscn", "pack": "race2.pck", "color": Color(0.45, 0.85, 0.35)},
|
||||
]
|
||||
|
||||
const ROOM_WIDTH := 28.0
|
||||
const ROOM_DEPTH := 18.0
|
||||
const ROOM_HEIGHT := 7.0 # +1 м: над порталами доски рейтинга, над ними — «CHOOSE A MAP»
|
||||
const ROOM_WIDTH := 48.0
|
||||
const ROOM_DEPTH := 32.0
|
||||
const ROOM_HEIGHT := 10.0 # над порталами доски рейтинга, над ними — «CHOOSE A MAP»
|
||||
const WALL := 0.5
|
||||
const PORTAL_SPACING := 4.5
|
||||
const PORTAL_SPACING := 6.0
|
||||
|
||||
const MUSIC_PATH := "res://maps/hub/audio/music.ogg"
|
||||
const MUSIC_DB := -9.0
|
||||
const DecorScript := preload("res://maps/hub/hub_decor.gd")
|
||||
|
||||
const GRID_SHADER := """
|
||||
shader_type spatial;
|
||||
@@ -37,6 +42,8 @@ func _ready() -> void:
|
||||
_build_portals()
|
||||
_build_environment()
|
||||
_build_boards()
|
||||
_build_decor()
|
||||
_start_music()
|
||||
if not Net.nick_asked:
|
||||
Overlay.open_nick.call_deferred(true) # первый запуск: спрашиваем ник
|
||||
|
||||
@@ -45,11 +52,14 @@ func _build_room() -> void:
|
||||
body.name = "Room"
|
||||
add_child(body)
|
||||
|
||||
var floor_mat := ShaderMaterial.new()
|
||||
floor_mat.shader = Shader.new()
|
||||
floor_mat.shader.code = GRID_SHADER
|
||||
var decor := DecorScript.new()
|
||||
decor.name = "Decor"
|
||||
add_child(decor)
|
||||
var floor_mat := decor.floor_material()
|
||||
var wall_mat := StandardMaterial3D.new()
|
||||
wall_mat.albedo_color = Color(0.09, 0.1, 0.14)
|
||||
wall_mat.albedo_color = Color(0.12, 0.13, 0.18)
|
||||
wall_mat.metallic = 0.3
|
||||
wall_mat.roughness = 0.6
|
||||
wall_mat.roughness = 1.0
|
||||
var ceiling_mat := StandardMaterial3D.new()
|
||||
ceiling_mat.albedo_color = Color(0.05, 0.055, 0.08)
|
||||
@@ -102,7 +112,8 @@ func _build_portals() -> void:
|
||||
label.font_size = 64
|
||||
label.outline_size = 12
|
||||
label.modulate = Color(0.7, 0.9, 1.0)
|
||||
label.position = Vector3(0, ROOM_HEIGHT - 0.35, -ROOM_DEPTH * 0.5 + 0.1)
|
||||
label.position = Vector3(0, 7.2, -ROOM_DEPTH * 0.5 + 0.1)
|
||||
label.pixel_size = 0.016
|
||||
add_child(label)
|
||||
|
||||
# ---------- доски рейтинга над порталами карт «на время» ----------
|
||||
@@ -206,15 +217,38 @@ func _build_environment() -> void:
|
||||
env.background_color = Color(0.02, 0.025, 0.04)
|
||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
|
||||
env.ambient_light_color = Color(0.5, 0.58, 0.75)
|
||||
env.ambient_light_energy = 0.55
|
||||
env.ambient_light_energy = 1.0
|
||||
env.glow_enabled = true # неон и светильники светятся (в Compatibility glow работает)
|
||||
env.glow_intensity = 0.8
|
||||
env.glow_bloom = 0.1
|
||||
var we := WorldEnvironment.new()
|
||||
we.environment = env
|
||||
add_child(we)
|
||||
|
||||
var lamp := OmniLight3D.new()
|
||||
lamp.position = Vector3(0, ROOM_HEIGHT - 0.6, 2.0)
|
||||
lamp.omni_range = 22.0
|
||||
lamp.light_energy = 0.8
|
||||
lamp.position = Vector3(0, ROOM_HEIGHT - 0.8, -6.0)
|
||||
lamp.omni_range = 30.0
|
||||
lamp.light_energy = 0.9
|
||||
lamp.light_color = Color(0.8, 0.9, 1.0)
|
||||
lamp.shadow_enabled = false
|
||||
add_child(lamp)
|
||||
|
||||
# ---------- оформление и музыка ----------
|
||||
|
||||
func _build_decor() -> void:
|
||||
var decor := get_node("Decor")
|
||||
decor.build(ROOM_WIDTH, ROOM_DEPTH, ROOM_HEIGHT)
|
||||
|
||||
func _start_music() -> void:
|
||||
var stream = load(MUSIC_PATH)
|
||||
if stream == null:
|
||||
return
|
||||
if stream is AudioStreamOggVorbis:
|
||||
stream.loop = true
|
||||
var player := AudioStreamPlayer.new()
|
||||
player.name = "Music"
|
||||
player.stream = stream
|
||||
player.volume_db = -40.0
|
||||
player.autoplay = true
|
||||
add_child(player)
|
||||
create_tween().tween_property(player, "volume_db", MUSIC_DB, 3.0) # плавно нарастает
|
||||
|
||||
+1
-1
@@ -7,4 +7,4 @@
|
||||
script = ExtResource("1_hub")
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("2_player")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.2, 5)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.2, 11)
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
extends Node3D
|
||||
# Оформление хаба: пол с кольцами, колонны, потолочные светильники, окна с ночным городом, лавки,
|
||||
# платформа с вращающейся голограммой в центре, пылинки в воздухе. Размеры берутся из hub.gd.
|
||||
# Всё строится кодом и без тяжёлых ресурсов — хаб лежит в основном пакете игры.
|
||||
|
||||
const BRASS := Color(0.85, 0.58, 0.26)
|
||||
const CYAN := Color(0.25, 0.85, 1.0)
|
||||
const DARK := Color(0.045, 0.05, 0.065)
|
||||
|
||||
const FLOOR_SHADER := """
|
||||
shader_type spatial;
|
||||
varying vec3 wpos;
|
||||
void vertex() { wpos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; }
|
||||
void fragment() {
|
||||
vec2 cell = wpos.xz / 2.0;
|
||||
vec2 g = abs(fract(cell - 0.5) - 0.5) / fwidth(cell);
|
||||
float line = 1.0 - min(min(g.x, g.y), 1.0);
|
||||
float r = length(wpos.xz);
|
||||
float ring = 1.0 - smoothstep(0.0, 0.09, min(abs(r - 7.5), abs(r - 12.5)));
|
||||
// дорожка света от платформы к порталам
|
||||
float lane = 1.0 - smoothstep(0.0, 0.07, abs(wpos.x)) ;
|
||||
lane *= step(wpos.z, -4.0) * 0.8;
|
||||
vec3 base = vec3(0.028, 0.032, 0.045);
|
||||
vec3 col = mix(base, vec3(0.08, 0.5, 0.65), line * 0.55);
|
||||
col = mix(col, vec3(0.9, 0.58, 0.24), clamp(ring + lane, 0.0, 1.0));
|
||||
ALBEDO = col * 0.55;
|
||||
EMISSION = vec3(0.03, 0.25, 0.34) * line + vec3(0.5, 0.3, 0.1) * clamp(ring + lane, 0.0, 1.0);
|
||||
ROUGHNESS = 0.35;
|
||||
METALLIC = 0.4;
|
||||
}
|
||||
"""
|
||||
|
||||
# Окно: ночной город — небо с зарёй у горизонта, силуэты домов со светящимися окнами
|
||||
const CITY_SHADER := """
|
||||
shader_type spatial;
|
||||
render_mode unshaded;
|
||||
float h(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
|
||||
void fragment() {
|
||||
float yb = 1.0 - UV.y;
|
||||
vec3 col = mix(vec3(0.015, 0.02, 0.06), vec3(0.42, 0.14, 0.30), pow(yb, 2.2) * 0.9);
|
||||
col += vec3(0.5, 0.25, 0.1) * smoothstep(0.35, 0.0, yb) * 0.6;
|
||||
for (int layer = 0; layer < 2; layer++) {
|
||||
float n = layer == 0 ? 14.0 : 24.0;
|
||||
float id = floor(UV.x * n + float(layer) * 7.0);
|
||||
float top = (layer == 0 ? 0.42 : 0.26) * (0.35 + 0.65 * h(vec2(id, float(layer) + 1.0)));
|
||||
if (yb < top) {
|
||||
vec3 body = layer == 0 ? vec3(0.02, 0.02, 0.035) : vec3(0.05, 0.03, 0.07);
|
||||
vec2 wc = vec2(UV.x * n * 3.0, yb * (layer == 0 ? 34.0 : 22.0));
|
||||
vec2 wi = floor(wc);
|
||||
vec2 wf = fract(wc);
|
||||
float lit = step(0.62, h(wi + id)) * step(0.2, wf.x) * step(wf.x, 0.8) * step(0.25, wf.y) * step(wf.y, 0.75);
|
||||
vec3 wcol = h(wi + 3.0) > 0.55 ? vec3(1.0, 0.7, 0.3) : vec3(0.4, 0.8, 1.0);
|
||||
col = body + wcol * lit * (layer == 0 ? 0.9 : 0.5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALBEDO = col;
|
||||
}
|
||||
"""
|
||||
|
||||
var _spin: Array = [] # [узел, ось, скорость]
|
||||
var _pulse_mat: StandardMaterial3D
|
||||
var _t := 0.0
|
||||
|
||||
func build(width: float, depth: float, height: float) -> void:
|
||||
_build_pillars(width, depth, height)
|
||||
_build_ceiling(width, depth, height)
|
||||
_build_windows(width, depth, height)
|
||||
_build_benches(width, depth)
|
||||
_build_dais()
|
||||
_build_signs(width, depth, height)
|
||||
_build_dust(width, depth, height)
|
||||
|
||||
func floor_material() -> ShaderMaterial:
|
||||
var m := ShaderMaterial.new()
|
||||
m.shader = Shader.new()
|
||||
m.shader.code = FLOOR_SHADER
|
||||
return m
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_t += delta
|
||||
for s in _spin:
|
||||
(s[0] as Node3D).rotate(s[1], s[2] * delta)
|
||||
if _pulse_mat:
|
||||
_pulse_mat.emission_energy_multiplier = 1.6 + 0.5 * sin(_t * 1.6)
|
||||
|
||||
# ---------- вспомогательное ----------
|
||||
|
||||
func _emissive(c: Color, energy := 2.0) -> StandardMaterial3D:
|
||||
var m := StandardMaterial3D.new()
|
||||
m.albedo_color = c.darkened(0.6)
|
||||
m.emission_enabled = true
|
||||
m.emission = c
|
||||
m.emission_energy_multiplier = energy
|
||||
return m
|
||||
|
||||
func _matte(c: Color, metallic := 0.0, rough := 0.7) -> StandardMaterial3D:
|
||||
var m := StandardMaterial3D.new()
|
||||
m.albedo_color = c
|
||||
m.metallic = metallic
|
||||
m.roughness = rough
|
||||
return m
|
||||
|
||||
func _box(size: Vector3, pos: Vector3, mat: Material, collide := false) -> MeshInstance3D:
|
||||
var mesh := BoxMesh.new()
|
||||
mesh.size = size
|
||||
mesh.material = mat
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = mesh
|
||||
mi.position = pos
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(mi)
|
||||
if collide:
|
||||
var body := StaticBody3D.new()
|
||||
var cs := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = size
|
||||
cs.shape = shape
|
||||
body.add_child(cs)
|
||||
mi.add_child(body)
|
||||
return mi
|
||||
|
||||
func _label(text: String, size: float, color: Color, pos: Vector3, yaw := 0.0) -> Label3D:
|
||||
var l := Label3D.new()
|
||||
l.text = text
|
||||
l.font_size = 64
|
||||
l.pixel_size = size
|
||||
l.outline_size = 10
|
||||
l.modulate = color
|
||||
l.shaded = false
|
||||
l.position = pos
|
||||
l.rotation.y = yaw
|
||||
add_child(l)
|
||||
return l
|
||||
|
||||
# ---------- колонны ----------
|
||||
|
||||
func _build_pillars(width: float, depth: float, height: float) -> void:
|
||||
var steel := _matte(Color(0.07, 0.075, 0.09), 0.6, 0.4)
|
||||
var glow := _emissive(CYAN, 2.2)
|
||||
var brass := _emissive(BRASS, 1.4)
|
||||
var px := width * 0.5 - 8.0
|
||||
for sx in [-1.0, 1.0]:
|
||||
for z in [-6.0, 2.0, 10.0]:
|
||||
var p := Vector3(sx * px, 0.0, z)
|
||||
_box(Vector3(1.1, height, 1.1), p + Vector3(0, height * 0.5, 0), steel, true)
|
||||
_box(Vector3(1.2, 0.18, 1.2), p + Vector3(0, 0.09, 0), brass)
|
||||
_box(Vector3(1.2, 0.18, 1.2), p + Vector3(0, height - 0.09, 0), brass)
|
||||
# светящаяся прорезь на каждой грани по высоте
|
||||
_box(Vector3(0.12, height - 1.6, 1.14), p + Vector3(0, height * 0.5, 0), glow)
|
||||
_box(Vector3(1.14, height - 1.6, 0.12), p + Vector3(0, height * 0.5, 0), glow)
|
||||
|
||||
# ---------- потолок ----------
|
||||
|
||||
func _build_ceiling(width: float, depth: float, height: float) -> void:
|
||||
var strip := _emissive(Color(1.0, 0.86, 0.62), 2.4)
|
||||
var y := height - 0.08
|
||||
for x in [-18.0, -9.0, 0.0, 9.0, 18.0]:
|
||||
_box(Vector3(0.35, 0.12, depth - 4.0), Vector3(x, y, 0), strip)
|
||||
for z in [-8.0, 0.0, 8.0]:
|
||||
_box(Vector3(width - 4.0, 0.1, 0.22), Vector3(0, y + 0.02, z), _emissive(BRASS, 1.2))
|
||||
# большое кольцо над платформой
|
||||
var ring := TorusMesh.new()
|
||||
ring.inner_radius = 3.9
|
||||
ring.outer_radius = 4.1
|
||||
ring.material = _emissive(CYAN, 2.0)
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = ring
|
||||
mi.position = Vector3(0, height - 0.5, 0)
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(mi)
|
||||
# мягкий свет зон: тёплый у порталов, холодный в центре
|
||||
for x in [-14.0, 14.0]:
|
||||
var l := OmniLight3D.new()
|
||||
l.position = Vector3(x, height - 1.0, 4.0)
|
||||
l.omni_range = 20.0
|
||||
l.light_energy = 0.7
|
||||
l.light_color = Color(1.0, 0.82, 0.6)
|
||||
l.shadow_enabled = false
|
||||
add_child(l)
|
||||
|
||||
# ---------- окна в боковых стенах ----------
|
||||
|
||||
func _build_windows(width: float, depth: float, height: float) -> void:
|
||||
var city := ShaderMaterial.new()
|
||||
city.shader = Shader.new()
|
||||
city.shader.code = CITY_SHADER
|
||||
var frame := _matte(Color(0.05, 0.05, 0.06), 0.7, 0.35)
|
||||
var brass := _emissive(BRASS, 0.9)
|
||||
var hw := width * 0.5
|
||||
var win := Vector2(8.5, 5.6)
|
||||
var y := 4.6
|
||||
for side in [-1, 1]:
|
||||
var yaw := PI * 0.5 * float(-side) # лицом в комнату
|
||||
var x := float(side) * (hw - 0.04)
|
||||
for z in [-8.5, 0.5, 9.5]:
|
||||
var q := QuadMesh.new()
|
||||
q.size = win
|
||||
q.material = city
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = q
|
||||
mi.position = Vector3(x, y, z)
|
||||
mi.rotation.y = yaw
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(mi)
|
||||
# рама и переплёты
|
||||
var inner_x := float(side) * (hw - 0.12)
|
||||
_box(Vector3(0.1, win.y, 0.18), Vector3(inner_x, y, z - win.x * 0.25), frame)
|
||||
_box(Vector3(0.1, win.y, 0.18), Vector3(inner_x, y, z + win.x * 0.25), frame)
|
||||
_box(Vector3(0.1, 0.18, win.x), Vector3(inner_x, y - 0.5, z), frame)
|
||||
for e in [-1.0, 1.0]:
|
||||
_box(Vector3(0.12, win.y + 0.5, 0.2), Vector3(inner_x, y, z + e * (win.x * 0.5 + 0.1)), frame)
|
||||
var wl := OmniLight3D.new() # отсвет города на полу и лавках
|
||||
wl.position = Vector3(float(side) * (hw - 3.0), y - 1.0, z)
|
||||
wl.light_color = Color(0.75, 0.45, 0.9)
|
||||
wl.light_energy = 1.1
|
||||
wl.omni_range = 11.0
|
||||
wl.shadow_enabled = false
|
||||
add_child(wl)
|
||||
_box(Vector3(0.12, 0.08, win.x + 0.5), Vector3(inner_x, y - win.y * 0.5 - 0.3, z), brass)
|
||||
_box(Vector3(0.12, 0.08, win.x + 0.5), Vector3(inner_x, y + win.y * 0.5 + 0.3, z), brass)
|
||||
|
||||
# ---------- лавки ----------
|
||||
|
||||
func _build_benches(width: float, _depth: float) -> void:
|
||||
var seat := _matte(Color(0.16, 0.08, 0.06), 0.0, 0.55)
|
||||
var metal := _matte(Color(0.08, 0.08, 0.1), 0.8, 0.3)
|
||||
var x := width * 0.5 - 2.0
|
||||
for side in [-1.0, 1.0]:
|
||||
for z in [-8.5, 0.5, 9.5]:
|
||||
var base := Vector3(side * x, 0.0, z)
|
||||
_box(Vector3(1.0, 0.45, 5.0), base + Vector3(0, 0.225, 0), seat, true)
|
||||
_box(Vector3(0.25, 0.9, 5.0), base + Vector3(side * 0.5, 0.7, 0), seat)
|
||||
_box(Vector3(0.95, 0.08, 4.8), base + Vector3(0, 0.02, 0), metal)
|
||||
_box(Vector3(0.05, 0.04, 4.8), base + Vector3(-side * 0.5, 0.3, 0), _emissive(BRASS, 1.0))
|
||||
|
||||
# ---------- центр: платформа и голограмма ----------
|
||||
|
||||
func _build_dais() -> void:
|
||||
var body := StaticBody3D.new()
|
||||
add_child(body)
|
||||
var cyl := CylinderMesh.new()
|
||||
cyl.top_radius = 3.6
|
||||
cyl.bottom_radius = 3.7
|
||||
cyl.height = 0.32
|
||||
cyl.material = _matte(Color(0.06, 0.065, 0.08), 0.7, 0.3)
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = cyl
|
||||
mi.position.y = 0.16
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
body.add_child(mi)
|
||||
var cs := CollisionShape3D.new()
|
||||
var shape := CylinderShape3D.new()
|
||||
shape.radius = 3.65
|
||||
shape.height = 0.32
|
||||
cs.shape = shape
|
||||
cs.position.y = 0.16
|
||||
body.add_child(cs)
|
||||
var rim := TorusMesh.new()
|
||||
rim.inner_radius = 3.45
|
||||
rim.outer_radius = 3.6
|
||||
rim.material = _emissive(BRASS, 1.8)
|
||||
var rim_mi := MeshInstance3D.new()
|
||||
rim_mi.mesh = rim
|
||||
rim_mi.position.y = 0.33
|
||||
rim_mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(rim_mi)
|
||||
|
||||
_pulse_mat = _emissive(CYAN, 2.0)
|
||||
var holo := Node3D.new()
|
||||
holo.position = Vector3(0, 2.6, 0)
|
||||
add_child(holo)
|
||||
var core := SphereMesh.new()
|
||||
core.radius = 0.5
|
||||
core.height = 1.0
|
||||
core.material = _pulse_mat
|
||||
var core_mi := MeshInstance3D.new()
|
||||
core_mi.mesh = core
|
||||
core_mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
holo.add_child(core_mi)
|
||||
var specs := [[1.1, CYAN, Vector3(1, 0, 0), 0.7], [1.5, BRASS, Vector3(0, 0, 1), -0.5], [1.9, CYAN, Vector3(0, 1, 0), 0.35]]
|
||||
for i in specs.size():
|
||||
var t := TorusMesh.new()
|
||||
t.inner_radius = specs[i][0] - 0.035
|
||||
t.outer_radius = specs[i][0] + 0.035
|
||||
t.rings = 48
|
||||
t.material = _emissive(specs[i][1], 2.2)
|
||||
var pivot := Node3D.new()
|
||||
holo.add_child(pivot)
|
||||
pivot.rotation = Vector3(0.4 * i, 0.9 * i, 0.0)
|
||||
var tm := MeshInstance3D.new()
|
||||
tm.mesh = t
|
||||
tm.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
pivot.add_child(tm)
|
||||
_spin.append([pivot, specs[i][2], specs[i][3]])
|
||||
var light := OmniLight3D.new()
|
||||
light.position = Vector3(0, 2.6, 0)
|
||||
light.light_color = CYAN
|
||||
light.light_energy = 1.4
|
||||
light.omni_range = 11.0
|
||||
light.shadow_enabled = false
|
||||
add_child(light)
|
||||
var l := Label3D.new()
|
||||
l.text = "AMAN'S WORLD"
|
||||
l.billboard = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
l.font_size = 64
|
||||
l.pixel_size = 0.009
|
||||
l.outline_size = 12
|
||||
l.modulate = Color(1.0, 0.9, 0.7)
|
||||
l.shaded = false
|
||||
l.position = Vector3(0, 4.9, 0)
|
||||
add_child(l)
|
||||
|
||||
# ---------- вывески ----------
|
||||
|
||||
func _build_signs(width: float, depth: float, height: float) -> void:
|
||||
var hd := depth * 0.5
|
||||
# южная стена: «лифтовая» панель этажа
|
||||
_box(Vector3(10.0, 2.2, 0.15), Vector3(0, 5.0, hd - 0.08), _matte(Color(0.03, 0.03, 0.04), 0.5, 0.4))
|
||||
_box(Vector3(10.3, 0.08, 0.2), Vector3(0, 6.15, hd - 0.1), _emissive(BRASS, 1.2))
|
||||
_box(Vector3(10.3, 0.08, 0.2), Vector3(0, 3.85, hd - 0.1), _emissive(BRASS, 1.2))
|
||||
_label("ЛОББИ", 0.02, Color(1.0, 0.82, 0.5), Vector3(0, 5.4, hd - 0.2), PI)
|
||||
_label("ЭТАЖ 0 · ВСЕ КАРТЫ ВНИЗУ ПО КОРИДОРУ", 0.008, Color(0.7, 0.9, 1.0), Vector3(0, 4.4, hd - 0.2), PI)
|
||||
# северная стена: фриз над порталами
|
||||
_box(Vector3(width - 6.0, 0.12, 0.3), Vector3(0, 8.9, -hd + 0.15), _emissive(BRASS, 1.3))
|
||||
_box(Vector3(width - 6.0, 0.12, 0.3), Vector3(0, 8.0, -hd + 0.15), _emissive(BRASS, 1.3))
|
||||
|
||||
# ---------- пыль в лучах света ----------
|
||||
|
||||
func _build_dust(width: float, depth: float, height: float) -> void:
|
||||
var p := GPUParticles3D.new()
|
||||
p.amount = 140
|
||||
p.lifetime = 14.0
|
||||
p.preprocess = 14.0
|
||||
p.visibility_aabb = AABB(Vector3(-width * 0.5, 0, -depth * 0.5), Vector3(width, height, depth))
|
||||
var pm := ParticleProcessMaterial.new()
|
||||
pm.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
|
||||
pm.emission_box_extents = Vector3(width * 0.5 - 1.0, height * 0.5 - 0.5, depth * 0.5 - 1.0)
|
||||
pm.gravity = Vector3.ZERO
|
||||
pm.direction = Vector3(0.2, 1.0, 0.1)
|
||||
pm.spread = 180.0
|
||||
pm.initial_velocity_min = 0.05
|
||||
pm.initial_velocity_max = 0.25
|
||||
p.process_material = pm
|
||||
var q := QuadMesh.new()
|
||||
q.size = Vector2(0.05, 0.05)
|
||||
var m := StandardMaterial3D.new()
|
||||
m.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
m.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
m.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
m.albedo_color = Color(1.0, 0.9, 0.7, 0.35)
|
||||
q.material = m
|
||||
p.draw_pass_1 = q
|
||||
p.position = Vector3(0, height * 0.5, 0)
|
||||
p.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(p)
|
||||
@@ -0,0 +1 @@
|
||||
uid://or5cgrfhp73l
|
||||
Reference in New Issue
Block a user