/
ulupapi
/
Race_game
Обзор
Документация
Войти
/
ulupapi
/
Race_game
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scripts/StartScreen.gd
297 строк
9 KB
ulupapi
Game
26 дек 2025, 19:34
26 дек 2025, 19:34
7574b6d
Код
Авторство
О чём код?
# res://scripts/StartScreen.gd extends CanvasLayer class_name StartScreen @export var car_path: NodePath @export var ai_group_name: String = "aidriver" @export var track_name: String = "Grand Prix Circuit" @export var default_compound: String = "Medium" # Soft / Medium / Hard @export var default_ai: String = "Medium" # Easy / Medium / Hard @export var ui_font_px: int = 28 @export var title_font_px: int = 56 @export var digit_font_px: int = 240 @export var start_font_px: int = 160 var _car: Node = null # Основной UI var _ui_root: Control var _bg: ColorRect var _top_bar: HBoxContainer var _left_box: HBoxContainer var _right_box: HBoxContainer var _title: Label var _tyre_label: Label var _tyre_select: OptionButton var _ai_label: Label var _ai_select: OptionButton var _bottom: CenterContainer var _start_btn: Button # Оверлей отсчёта (отдельный, чтобы можно было скрыть основной UI) var _count_overlay: ColorRect var _count_center: CenterContainer var _count_label: Label var _is_counting: bool = false func _ready() -> void: _build_ui() # слой поверх всего layer = 100 visible = true _bg.color = Color(0, 0, 0, 1.0) # найти машину if car_path != NodePath(""): _car = get_node_or_null(car_path) if _car == null: var pcs: Array = get_tree().get_nodes_in_group("player_car") if pcs.size() > 0: _car = pcs[0] # запретить автозапуск до отсчёта if _car != null: _set_prop_if_exists(_car, "require_start", true) _set_prop_if_exists(_car, "started", false) if _car is CharacterBody3D: var cb := _car as CharacterBody3D cb.velocity = Vector3.ZERO _select_option_text(_tyre_select, default_compound) _select_option_text(_ai_select, default_ai) func _build_ui() -> void: # --- основной UI (будем скрывать на время отсчёта) --- _ui_root = Control.new() _ui_root.anchor_left = 0.0 _ui_root.anchor_top = 0.0 _ui_root.anchor_right = 1.0 _ui_root.anchor_bottom = 1.0 add_child(_ui_root) _bg = ColorRect.new() _bg.anchor_left = 0.0 _bg.anchor_top = 0.0 _bg.anchor_right = 1.0 _bg.anchor_bottom = 1.0 _bg.color = Color(0, 0, 0, 1.0) _ui_root.add_child(_bg) var main := VBoxContainer.new() main.anchor_left = 0.0 main.anchor_top = 0.0 main.anchor_right = 1.0 main.anchor_bottom = 1.0 main.size_flags_horizontal = Control.SIZE_EXPAND_FILL main.size_flags_vertical = Control.SIZE_EXPAND_FILL main.add_theme_constant_override("separation", 24) _ui_root.add_child(main) _top_bar = HBoxContainer.new() _top_bar.custom_minimum_size = Vector2(0, 110) _top_bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL _top_bar.add_theme_constant_override("separation", 24) main.add_child(_top_bar) _left_box = HBoxContainer.new() _left_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL _left_box.alignment = BoxContainer.ALIGNMENT_BEGIN _left_box.add_theme_constant_override("separation", 12) _top_bar.add_child(_left_box) _title = Label.new() _title.text = track_name _title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _title.size_flags_horizontal = Control.SIZE_EXPAND_FILL _title.add_theme_font_size_override("font_size", title_font_px) _top_bar.add_child(_title) _right_box = HBoxContainer.new() _right_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL _right_box.alignment = BoxContainer.ALIGNMENT_END _right_box.add_theme_constant_override("separation", 12) _top_bar.add_child(_right_box) _tyre_label = Label.new() _tyre_label.text = "Tyres:" _tyre_label.add_theme_font_size_override("font_size", ui_font_px) _left_box.add_child(_tyre_label) _tyre_select = OptionButton.new() _tyre_select.add_item("Soft") _tyre_select.add_item("Medium") _tyre_select.add_item("Hard") _tyre_select.add_theme_font_size_override("font_size", ui_font_px) _left_box.add_child(_tyre_select) _ai_label = Label.new() _ai_label.text = "AI:" _ai_label.add_theme_font_size_override("font_size", ui_font_px) _right_box.add_child(_ai_label) _ai_select = OptionButton.new() _ai_select.add_item("Easy") _ai_select.add_item("Medium") _ai_select.add_item("Hard") _ai_select.add_theme_font_size_override("font_size", ui_font_px) _right_box.add_child(_ai_select) _bottom = CenterContainer.new() _bottom.custom_minimum_size = Vector2(0, 140) _bottom.size_flags_horizontal = Control.SIZE_EXPAND_FILL main.add_child(_bottom) _start_btn = Button.new() _start_btn.text = "START" _start_btn.custom_minimum_size = Vector2(360, 96) _start_btn.add_theme_font_size_override("font_size", ui_font_px + 8) _start_btn.pressed.connect(_on_start_pressed) _bottom.add_child(_start_btn) # --- отдельный слой отсчёта (НЕ часть _ui_root) --- _count_overlay = ColorRect.new() _count_overlay.anchor_left = 0.0 _count_overlay.anchor_top = 0.0 _count_overlay.anchor_right = 1.0 _count_overlay.anchor_bottom = 1.0 _count_overlay.color = Color(0, 0, 0, 1.0) # Полностью чёрный фон _count_overlay.visible = false add_child(_count_overlay) # важно: не под _ui_root! _count_center = CenterContainer.new() _count_center.size_flags_horizontal = Control.SIZE_EXPAND_FILL _count_center.size_flags_vertical = Control.SIZE_EXPAND_FILL _count_overlay.add_child(_count_center) _count_label = Label.new() _count_label.text = "" _count_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _count_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _count_label.add_theme_font_size_override("font_size", digit_font_px) _count_center.add_child(_count_label) func _on_start_pressed() -> void: if _is_counting: return _is_counting = true _start_btn.disabled = true var comp: String = _get_selected_text(_tyre_select) if comp == "": comp = "Medium" _apply_player_tyres(comp) var ai_lvl: String = _get_selected_text(_ai_select) if ai_lvl == "": ai_lvl = "Medium" _broadcast_ai_difficulty(ai_lvl) # скрываем ВЕСЬ стартовый UI _ui_root.visible = false # показываем чёрный оверлей и отсчитываем _count_overlay.visible = true await _show_digit("3", digit_font_px, 1.0) await _show_digit("2", digit_font_px, 1.0) await _show_digit("1", digit_font_px, 1.0) await _show_digit("START", start_font_px, 0.9) # запуск машины if _car != null: _set_prop_if_exists(_car, "started", true) # всё, стартовый экран не нужен queue_free() func _show_digit(txt: String, size_px: int, hold_s: float) -> void: # фон держим чёрным, меняем только надпись _count_label.text = txt _count_label.add_theme_font_size_override("font_size", size_px) _count_label.scale = Vector2(0.82, 0.82) var tw := create_tween() tw.tween_property(_count_label, "scale", Vector2(1.0, 1.0), 0.28) await get_tree().create_timer(hold_s).timeout # ---------- helpers ---------- func _get_selected_text(ob: OptionButton) -> String: if ob == null: return "" var idx: int = ob.selected if idx < 0: return "" return ob.get_item_text(idx) func _select_option_text(ob: OptionButton, text_value: String) -> void: if ob == null: return var count: int = ob.item_count var i: int = 0 var found: bool = false while i < count: var t: String = ob.get_item_text(i) if t == text_value: ob.select(i) found = true break i += 1 if not found: ob.select(0) func _apply_player_tyres(compound: String) -> void: if _car == null: return if _car.has_method("apply_tyre_choice"): _car.call("apply_tyre_choice", compound) return _set_prop_if_exists(_car, "tyre_compound", compound) func _broadcast_ai_difficulty(level: String) -> void: var nodes: Array = get_tree().get_nodes_in_group(ai_group_name) var n: int = nodes.size() var i: int = 0 while i < n: var nd: Object = nodes[i] if nd.has_method("apply_difficulty"): nd.call("apply_difficulty", level) else: var aggression: float = 0.55 var consistency: float = 0.60 var mistake_rate: float = 0.04 var speed_scale: float = 1.00 if level == "Easy": aggression = 0.35 consistency = 0.80 mistake_rate = 0.08 speed_scale = 0.92 elif level == "Hard": aggression = 0.80 consistency = 0.45 mistake_rate = 0.02 speed_scale = 1.05 _set_prop_if_exists(nd, "aggression", aggression) _set_prop_if_exists(nd, "consistency", consistency) _set_prop_if_exists(nd, "mistake_rate", mistake_rate) _set_prop_if_exists(nd, "speed_scale", speed_scale) i += 1 func _set_prop_if_exists(obj: Object, prop_name: String, value) -> void: if obj == null: return var plist: Array = obj.get_property_list() var found: bool = false var i: int = 0 while i < plist.size(): var d = plist[i] if typeof(d) == TYPE_DICTIONARY: if d.has("name"): var nm: String = String(d["name"]) if nm == prop_name: found = true break i += 1 if found: obj.set(prop_name, value)