Race 2 «Верхние ярусы», фаза 1: карта, вентиляторы, ускоряющие полосы, правила общие с Race
- maps/race2/: сцена, race2.gd (ворота со световыми столбами, лазеры, вентиляторы, полосы, срыв, дождь, гул), данные и модель из Blender models/race_02 (раскладка layout.py -> build_race.py -> bake.py; свет пока запечён грубо, небо и музыка — копии Race) - objects/vent.gd, objects/boost_strip.gd + хуки apply_vent()/apply_boost() в player/player.gd (MAX_SPEED и остальная физика не тронуты) - race_rules.gd берёт данные у карты (общий код правил для Race и Race 2) - портал RACE 2 в хабе, пресет экспорта Race2 + export_all.sh, Net.TIMED_MAPS/MAP_TITLES для race2 (RPC не менялись; серверу нужен deploy_server.sh) - тесты: race_game_test.sh с MAP=race2 (бот проводит игроков телепортами), race2_check.txt - README: раздел «Карта Race 2», результаты ботов по веткам Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
extends Area3D
|
||||
# Ускоряющая полоса: на ней игрок, бегущий вдоль неё, быстро добирает скорость (до speed, но не выше MAX_SPEED игрока).
|
||||
# Направление — локальная ось -Z узла (rotation.y = рыскание, как у ворот гонки). Эффект локальный, в сеть не уходит.
|
||||
# Начало координат — центр полосы на полу. Рисуется кодом: бирюзовые стрелки, бегущие по ходу.
|
||||
|
||||
@export var size := Vector2(6.0, 10.0) # ширина (X) и длина (Z), м
|
||||
@export var speed := 22.0
|
||||
@export var color := Color(0.35, 0.9, 0.85) # холодный бирюзовый: отличается от жёлтых стрелок-подсказок трассы
|
||||
|
||||
const SHADER := """
|
||||
shader_type spatial;
|
||||
render_mode unshaded, blend_add, cull_disabled, depth_draw_never;
|
||||
uniform vec4 tint : source_color = vec4(0.35, 0.9, 0.85, 1.0);
|
||||
uniform vec2 dims = vec2(6.0, 10.0);
|
||||
varying vec2 uv;
|
||||
void vertex() { uv = VERTEX.xz; }
|
||||
void fragment() {
|
||||
// uv.y < 0 — вперёд (-Z); шевроны бегут вперёд
|
||||
float across = abs(uv.x) / (dims.x * 0.5);
|
||||
float t = -uv.y * 0.35 - TIME * 2.6 + across * 0.9;
|
||||
float chev = smoothstep(0.55, 0.62, fract(t)) * (1.0 - smoothstep(0.78, 0.9, fract(t)));
|
||||
float edge = smoothstep(0.86, 0.94, across);
|
||||
float end = smoothstep(0.0, 0.1, 0.5 - abs(uv.y) / dims.y);
|
||||
ALBEDO = tint.rgb;
|
||||
ALPHA = clamp((chev * 0.85 + edge * 0.7 + 0.1) * end, 0.0, 1.0);
|
||||
}
|
||||
"""
|
||||
|
||||
func _ready() -> void:
|
||||
collision_layer = 0
|
||||
collision_mask = 1
|
||||
var cs := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = Vector3(size.x, 2.4, size.y)
|
||||
cs.shape = shape
|
||||
cs.position.y = 1.2
|
||||
add_child(cs)
|
||||
var sh := Shader.new()
|
||||
sh.code = SHADER
|
||||
var sm := ShaderMaterial.new()
|
||||
sm.shader = sh
|
||||
sm.set_shader_parameter("tint", color)
|
||||
sm.set_shader_parameter("dims", size)
|
||||
var pm := PlaneMesh.new()
|
||||
pm.size = size
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = pm
|
||||
mi.material_override = sm
|
||||
mi.position.y = 0.07
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(mi)
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
var dir := -global_transform.basis.z
|
||||
for b in get_overlapping_bodies():
|
||||
if b.has_method("apply_boost"):
|
||||
b.apply_boost(dir, speed)
|
||||
@@ -0,0 +1 @@
|
||||
uid://7fr8utm135ud
|
||||
@@ -0,0 +1,89 @@
|
||||
extends Area3D
|
||||
# Вентиляционный поток: столб воздуха вверх из решётки. Пока игрок внутри, player.apply_vent() тянет его вверх
|
||||
# (вертикальная скорость растёт до VENT_MAX_RISE, горизонтальная сохраняется, двойной прыжок возвращается).
|
||||
# Эффект локальный (только на игроке), в сеть не уходит. Рисуется кодом: решётка на полу, мерцающий столб, тонкий свет.
|
||||
# Начало координат — центр решётки на полу; столб идёт вверх на height.
|
||||
|
||||
@export var size := Vector2(6.0, 6.0) # сечение столба (X, Z), м
|
||||
@export var height := 14.0 # высота столба, м
|
||||
@export var color := Color(0.45, 0.85, 0.35)
|
||||
|
||||
const SHADER := """
|
||||
shader_type spatial;
|
||||
render_mode unshaded, blend_add, cull_disabled, depth_draw_never;
|
||||
uniform vec4 tint : source_color = vec4(0.45, 0.85, 0.35, 1.0);
|
||||
uniform float height = 14.0;
|
||||
varying vec3 lp;
|
||||
void vertex() { lp = VERTEX; }
|
||||
void fragment() {
|
||||
float y = lp.y / height;
|
||||
float bands = 0.55 + 0.45 * sin((lp.y * 0.9 - TIME * 7.0) + lp.x * 1.7 + lp.z * 2.3);
|
||||
float fade = smoothstep(0.0, 0.08, y) * (1.0 - smoothstep(0.55, 1.0, y));
|
||||
ALBEDO = tint.rgb;
|
||||
ALPHA = clamp(bands * fade * 0.22, 0.0, 1.0);
|
||||
}
|
||||
"""
|
||||
|
||||
func _ready() -> void:
|
||||
collision_layer = 0
|
||||
collision_mask = 1
|
||||
var cs := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = Vector3(size.x, height, size.y)
|
||||
cs.shape = shape
|
||||
cs.position.y = height * 0.5
|
||||
add_child(cs)
|
||||
_build_visuals()
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
for b in get_overlapping_bodies():
|
||||
if b.has_method("apply_vent"):
|
||||
b.apply_vent()
|
||||
|
||||
func _build_visuals() -> void:
|
||||
# мерцающий столб
|
||||
var sh := Shader.new()
|
||||
sh.code = SHADER
|
||||
var sm := ShaderMaterial.new()
|
||||
sm.shader = sh
|
||||
sm.set_shader_parameter("tint", color)
|
||||
sm.set_shader_parameter("height", height)
|
||||
var bm := BoxMesh.new()
|
||||
bm.size = Vector3(size.x, height, size.y)
|
||||
var col := MeshInstance3D.new()
|
||||
col.mesh = bm
|
||||
col.material_override = sm
|
||||
col.position.y = height * 0.5
|
||||
col.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(col)
|
||||
# решётка на полу: тёмная плита с светящимися щелями
|
||||
var dark := StandardMaterial3D.new()
|
||||
dark.albedo_color = Color(0.02, 0.025, 0.03)
|
||||
var glow := StandardMaterial3D.new()
|
||||
glow.albedo_color = Color(0, 0, 0)
|
||||
glow.emission_enabled = true
|
||||
glow.emission = color
|
||||
glow.emission_energy_multiplier = 2.2
|
||||
var plate := BoxMesh.new()
|
||||
plate.size = Vector3(size.x - 0.3, 0.08, size.y - 0.3)
|
||||
_add(plate, dark, Vector3(0, 0.04, 0))
|
||||
var n := int((size.x - 0.8) / 0.7)
|
||||
for i in n:
|
||||
var slit := BoxMesh.new()
|
||||
slit.size = Vector3(0.22, 0.03, size.y - 1.0)
|
||||
_add(slit, glow, Vector3(-(size.x - 0.8) * 0.5 + (i + 0.5) * (size.x - 0.8) / n, 0.095, 0))
|
||||
# слабый свет над решёткой
|
||||
var l := OmniLight3D.new()
|
||||
l.light_color = color
|
||||
l.light_energy = 0.8
|
||||
l.omni_range = maxf(size.x, size.y) * 1.8
|
||||
l.position.y = 1.5
|
||||
add_child(l)
|
||||
|
||||
func _add(mesh: Mesh, mat: Material, pos: Vector3) -> void:
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = mesh
|
||||
mi.material_override = mat
|
||||
mi.position = pos
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(mi)
|
||||
@@ -0,0 +1 @@
|
||||
uid://v76iegf4bft
|
||||
Reference in New Issue
Block a user