Учебный проект № 2 «Камень, ножницы, бумага» в Godot
Вот что получилось:
Ниже я рассказываю, как я это делал.
Дисклеймер.
Сделал пост после того как все получилось. Формат я еще не освоил, пробую разное, еще не понял как лучше.
1. Начало: создаём проект
Я запустил Godot и нажал New Project. Назвал его «rock-paper-scissors», выбрал удобную папку и кликнул Create & Edit.
2. Подготавливаю ресурсы
Мне понадобились три картинки (камень, ножницы, бумага) и два звука — для победы и поражения. Я нашёл в интернете простые иконки в формате PNG с прозрачностью и два коротких звуковых файла WAV. Всё сложил в папку assets внутри папки проекта. В Godot эта папка отображается в FileSystem.
Содержимое папки проекта (уже на момент завершения). Проект крохотный, поэтому все в одном. Выделил картинки о которых идет речь выше.
3. Создаю главную сцену
Я нажал Scene → New Scene и добавил корневой узел Control. Это будет основа интерфейса.
Теперь нужны кнопки. Я кликнул правой кнопкой по узлу Control → Add Child Node → выбрал Button. Назвал её b_rock. В инспекторе у кнопки нашёл свойство Text и ввёл «Камень». Точно так же создал ещё две кнопки: b_paper с текстом «Бумага» и b_scissors с текстом «Ножницы». Расставил их внизу экрана.
Дерево сцены с узлами (уже на момент завершения) Control, b_rock, b_paper, b_scissors. По иконкам можно различить класс объекта.
*если кому надо, можно легко открыть документацию объекта, в этом плане программа балует.
4. Добавляю места для картинок
Мне нужно показать, что выбрал игрок, а что — компьютер. Добавил два узла TextureRect (Add Child Node → TextureRect). Один назвал PlayerTexture, другой — ComputerTexture. Разместил их слева и справа над кнопками.
5. Создаю текстовые метки
Для слов считалки и результата нужны два Label. Добавил их: CountdownLabel (расположил сверху) и ResultLabel (чуть ниже). Пока они пустые, но это нормально (на скрине выше вы их не видите, но они суслик).
6. Таймер для анимации
Мне нужен таймер, который будет каждые полсекунды менять слово в считалке. Добавил узел Timer (Add Child Node → Timer), назвал CountdownTimer. В инспекторе установил Wait Time = 0.5 и One Shot = false (чтобы срабатывал многократно).
7. Звуки победы и поражения
Добавил два AudioStreamPlayer (Add Child Node → AudioStreamPlayer). Назвал их sound_win и sound_lose. В инспекторе у каждого загрузил соответствующий звуковой файл в поле Stream. Нашел просто системные звуки в винде.
8. Пишу скрипт
Теперь самое интересное — код. Я выделил корневой узел Control и нажал Attach Script. Оставил настройки по умолчанию и нажал Create. Открылся редактор кода.
Я полностью заменил содержимое на свой скрипт. Вот он, с комментариями, чтобы вы понимали, что делает каждая часть:
Весь код целиком (если кто научит вставлять с сохранением форматирования, буду благодарен):
extends Control
enum Choice {ROCK, PAPER, SCISSORS} # исправил опечатку SCISSARS -> SCISSORS
@onready var computer_chose_pik = $Sprite2D # если хочешь оставить для фона или чего-то ещё
@onready var sound_win = $sound_win
@onready var sound_lus = $sound_lus
@export var countdown_label: Label
@export var result_label: Label
@export var player_texture: TextureRect
@export var computer_texture: TextureRect
var rock_texture = preload("res://rock.png")
var paper_texture = preload("res://paper.png")
var scissors_texture = preload("res://scissors.png")
var countdown_words = ["камень", "ножницы", "бумага", "раз", "два", "три"]
var current_word_index = 0
var is_counting = false
var player_choice = -1
var computer_choice = -1
func _ready() -> void:
randomize() # для случайности
# Запуск считалки
func start_countdown(p_choice: int, c_choice: int):
result_label.text = ""
computer_texture.texture = null
if is_counting:
return
is_counting = true
player_choice = p_choice
computer_choice = c_choice
current_word_index = 0
countdown_label.text = countdown_words[current_word_index]
$CountdownTimer.start()
# Сигнал таймера (имя должно быть таким, как создалось при подключении)
func _on_countdown_timer_timeout():
current_word_index += 1
if current_word_index < countdown_words.size():
countdown_label.text = countdown_words[current_word_index]
else:
$CountdownTimer.stop()
is_counting = false
show_result()
# Установка текстуры для TextureRect
func set_choice_texture(texture_rect: TextureRect, choice: int):
match choice:
0:
texture_rect.texture = rock_texture
1:
texture_rect.texture = paper_texture
2:
texture_rect.texture = scissors_texture
# Показ результата после считалки
func show_result():
# Устанавливаем картинки
set_choice_texture(player_texture, player_choice)
set_choice_texture(computer_texture, computer_choice)
# Определяем победителя и выводим текст
if player_choice == computer_choice:
result_label.text = "Ничья!"
elif (player_choice == 0 and computer_choice == 2) or \
(player_choice == 2 and computer_choice == 1) or \
(player_choice == 1 and computer_choice == 0):
result_label.text = "Вы выиграли!"
sound_win.play() # звук победы
else:
result_label.text = "Вы проиграли!"
sound_lus.play() # звук поражения
# Обработчики кнопок (теперь запускают считалку)
func _on_b_rock_pressed() -> void:
computer_choice = randi_range(0, 2) # случайный выбор компьютера
start_countdown(Choice.ROCK, computer_choice)
func _on_b_paper_pressed() -> void:
computer_choice = randi_range(0, 2)
start_countdown(Choice.PAPER, computer_choice)
func _on_b_scissors_pressed() -> void:
computer_choice = randi_range(0, 2)
start_countdown(Choice.SCISSORS, computer_choice)
9. Подключаю сигналы
Теперь нужно связать кнопки и таймер с функциями в скрипте.
Кнопки:
Я выбрал в дереве сцены кнопку b_rock, перешёл во вкладку Node (рядом с Inspector), нашёл сигнал pressed() и нажал Connect. В появившемся окне оставил цель — Control, нажал Connect. Так я подключил сигнал к функции _on_b_rock_pressed. То же самое сделал для b_paper и b_scissors.
Функция сразу встает в скрипт главного нода.
Подключение сигнала к кнопкам (это делалось немного ранее, я сразу подключал и тестировал, переделывал их срабатывание, прошу прощения за рванное повествование)
Таймер:
Выбрал узел CountdownTimer, во вкладке Node нашёл сигнал timeout() и подключил его к Control. В скрипте автоматически появляется функция _on_countdown_timer_timeout(), когда вы ее добавляете
10. Связываю экспортированные переменные
В скрипте я объявил @export var countdown_label, result_label, player_texture, computer_texture. Теперь нужно указать, каким узлам они соответствуют.
Я выделил корневой узел Control и в инспекторе увидел поля с этими именами. Перетащил мышкой:
CountdownLabel → в поле Countdown Label
ResultLabel → в поле Result Label
spr_computer_chose → в поле Player Texture и в поле Computer Texture
11. Проверяю пути к ресурсам
В коде я указал пути "res://assets/rock.png" и т.д. Убедился, что файлы лежат именно там. Звуки уже загружены в узлы sound_win и sound_lose, так что в коде просто вызываю play().
Вызов sound_lus.play() в коде.
12. Запускаю и наслаждаюсь
Нажал F5 — игра запустилась. Нажимаю на кнопку «Камень» — появляется считалка, потом картинка камня у меня и случайная у компьютера, и результат. Звуки работают. Идеально!
Что я понял и что можно улучшить
Не бояться экспериментировать. Я получил именно то, что хотел: анимированную считалку, картинки, звуки.
Если вы захотите повторить, рекомендую внимательно следить за привязкой узлов и подключением сигналов.
Удачи в разработке!


















