Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Головна сцена гри
Тепер настав час об'єднати все, що ми зробили, разом, в ігрову сцену.
Створіть нову сцену та додайте Node з ім'ям Main. (Ми використовуємо Node, а не Node2D, тому що саме цей вузол буде контейнером для обробки логіки гри. Він не вимагає 2D-функціональності.)
Клацніть кнопку Створити екземпляр дочірньої сцени (іконка ланцюжка) і виберіть збережений player.tscn.
Тепер додайте наступні вузли як дочірні вузли Main і назвіть їх, як показано:
Timer (названий
MobTimer) - щоб контролювати, як часто виникають мобиTimer (названий
ScoreTimer) - збільшуватиме рахунок щосекундиTimer (названий
StartTimer) - щоб дати затримку перед запускомMarker2D (названий
StartPosition) - для визначення стартової позиції гравця
Встановіть властивість Час очікування кожного з вузлів Таймер наступним чином (значення в секундах):
MobTimer:0.5ScoreTimer:1StartTimer:2
Крім того, встановіть властивість One Shot в StartTimer на "включено" і задайте Position в StartPosition на (240, 450).
Поява мобів
Головний вузол породжуватиме нових мобів, і ми хочемо, щоб вони з'являлися у випадковому місці на краю екрана. Клацніть вузол Головний на панелі сцени, потім додайте дочірній вузол Path2D з назвою MobPath. Коли ви виберете Path2D, ви побачите кілька нових кнопок у верхній частині редактора:
Виберіть середню ("Додати точку") та намалюйте шлях, клацнувши, щоб додати точки у вказаних кутах. Щоб точки прикріпилися до сітки (ґратки), переконайтеся, що опції "Використати до ґратки" та "Використати прив'язку", обидві включені. Ці опції можна знайти зліва від кнопки «Блокування», виглядає як магніт з кількома лініями, що перетинаються.
Важливо
Малюйте доріжку за годинниковою стрілкою , інакше ваші моби будуть виникати спрямовані назовні, а не всередину!
Після розміщення точки 4 на зображенні натисніть кнопку «Закрити криву», і ваша крива буде завершена.
Тепер, коли шлях визначений, додайте вузол PathFollow2D (слідувати за шляхом 2D) нащадком MobPath та назвіть його MobSpawnLocation. Цей вузол автоматично обертатиметься та рухатиметься по шляху, тому ми можемо використовувати його для вибору випадкової позиції та напрямку вздовж шляху.
Ваше сцена має виглядати так:
Головний скрипт
Додайте скрипт до вузла Main. У верхній частині скрипту ми використовуємо @export var mob_scene: PackedScene, щоб ми могли вибрати сцену Mob, екземпляр якої ми хочемо створити.
extends Node
@export var mob_scene: PackedScene
var score
using Godot;
public partial class Main : Node
{
// Don't forget to rebuild the project so the editor knows about the new export variable.
[Export]
public PackedScene MobScene { get; set; }
private int _score;
}
Натисніть вузол Main, і ви побачите властивість Mob Scene в інспекторі під "Main.gd".
Ви можете змінити значення цієї властивості двома шляхами:
Перетягнути
mob.tscnз панелі "Файлова система" у властивість Mob Scene.Клацнути стрілочку вниз поряд з "[empty]"(пусто) і вибрати "Load" (Завантажити). Виберіть
mob.tscn.
Next, select the instance of the Player scene under Main node in the Scene dock,
and access the Signals dock on the sidebar.
Ви повинні побачити список сигналів для вузла Гравець. Знайдіть і двічі клацніть сигнал hit у списку (або клацніть його правою кнопкою миші та виберіть «Підключити...»). Відкриється діалогове вікно підключення сигналу. Ми хочемо створити нову функцію під назвою game_over, яка оброблятиме те, що має статися після закінчення гри. Введіть «game_over» у полі «Метод приймача» внизу діалогового вікна підключення сигналу та натисніть «Підключити». Ви прагнете, щоб сигнал хіт надсилався з Player і оброблявся в Main сценарії. Додайте наступний код до нової функції, а також функцію new_game, яка налаштує все для нової гри:
func game_over():
$ScoreTimer.stop()
$MobTimer.stop()
func new_game():
score = 0
$Player.start($StartPosition.position)
$StartTimer.start()
public void GameOver()
{
GetNode<Timer>("MobTimer").Stop();
GetNode<Timer>("ScoreTimer").Stop();
}
public void NewGame()
{
_score = 0;
var player = GetNode<Player>("Player");
var startPosition = GetNode<Marker2D>("StartPosition");
player.Start(startPosition.Position);
GetNode<Timer>("StartTimer").Start();
}
Тепер ми підключимо сигнал timeout() кожного вузла Таймера (StartTimer, ScoreTimer та MobTimer) до основного скрипта. Для кожного з трьох таймерів виберіть таймер на панелі Сцена, відкрийте вкладку Сигнали на панелі Вузлів, потім двічі клацніть сигнал timeout() у списку. Це відкриє діалогове вікно підключення нового сигналу. Налаштування за замовчуванням у цьому діалоговому вікні мають бути влаштовними, тому виберіть Підключити, щоб створити нове підключення сигналу.
Після того, як усі три таймери налаштовано, ви зможете побачити, що кожен таймер має підключення Signal для відповідного сигналу timeout(), що відображається зеленим кольором, у відповідних вкладках Signals:
(Для MobTimer):
_on_mob_timer_timeout()(Для ScoreTimer):
_on_score_timer_timeout()(Для StartTimer):
_on_start_timer_timeout()
Тепер ми визначимо, як працює кожен із цих таймерів, додавши наведений нижче код. Зверніть увагу, що StartTimer запустить два інших таймери, а ScoreTimer збільшить рахунок на 1.
func _on_score_timer_timeout():
score += 1
func _on_start_timer_timeout():
$MobTimer.start()
$ScoreTimer.start()
// We also specified this function name in PascalCase in the editor's connection window.
private void OnScoreTimerTimeout()
{
_score++;
}
// We also specified this function name in PascalCase in the editor's connection window.
private void OnStartTimerTimeout()
{
GetNode<Timer>("MobTimer").Start();
GetNode<Timer>("ScoreTimer").Start();
}
У функції _on_mob_timer_timeout() ми створимо екземпляр моба, підберемо випадкове початкове місце уздовж Path2D та приведемо моба у рух. Вузол PathFollow2D буде автоматично обертатися по мірі просування по шляху, так що ми будемо використовувати його, щоб вибрати напрямок моба, а також його позицію. Під час появи моба, ми вибираємо випадкове значення між 150.0 і 250.0 визначаючи швидкість руху (було б нудно, якби всі вони рухалися з однаковою швидкістю).
Зауважте, що новий екземпляр потрібно додати до сцени за допомогою add_child().
func _on_mob_timer_timeout():
# Create a new instance of the Mob scene.
var mob = mob_scene.instantiate()
# Choose a random location on Path2D.
var mob_spawn_location = $MobPath/MobSpawnLocation
mob_spawn_location.progress_ratio = randf()
# Set the mob's position to the random location.
mob.position = mob_spawn_location.position
# Set the mob's direction perpendicular to the path direction.
var direction = mob_spawn_location.rotation + PI / 2
# Add some randomness to the direction.
direction += randf_range(-PI / 4, PI / 4)
mob.rotation = direction
# Choose the velocity for the mob.
var velocity = Vector2(randf_range(150.0, 250.0), 0.0)
mob.linear_velocity = velocity.rotated(direction)
# Spawn the mob by adding it to the Main scene.
add_child(mob)
// We also specified this function name in PascalCase in the editor's connection window.
private void OnMobTimerTimeout()
{
// Create a new instance of the Mob scene.
Mob mob = MobScene.Instantiate<Mob>();
// Choose a random location on Path2D.
var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
mobSpawnLocation.ProgressRatio = GD.Randf();
// Set the mob's direction perpendicular to the path direction.
float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
// Set the mob's position to a random location.
mob.Position = mobSpawnLocation.Position;
// Add some randomness to the direction.
direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
mob.Rotation = direction;
// Choose the velocity.
var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
mob.LinearVelocity = velocity.Rotated(direction);
// Spawn the mob by adding it to the Main scene.
AddChild(mob);
}
Важливо
Чому PI? У функціях, що вимагають кутів, Godot використовує радіани , а не градуси. Пі являє собою половину повороту в радіанах, близько 3.1415 (є також TAU, що дорівнює 2 * PI). Якщо вам зручніше працювати з градусами, вам потрібно буде скористатися функціями deg_to_rad() та rad_to_deg() для перетворення між ними.
Тестування сцени
Давайте протестуємо сцену, щоб переконати, що все працює. Додайте new_game до _ready():
func _ready():
new_game()
public override void _Ready()
{
NewGame();
}
Також визначимо Main, як нашу "Головну Сцену" - вона буде запускатися автоматично при кожному запуску гри. Натисніть кнопку "Відтворити" і виберіть main.tscn при появі запиту.
Порада
Якщо ви вже встановили іншу сцену як "Головну сцену", ви можете клацнути правою кнопкою мишки по main.tscn на панелі Файлова система і вибрати "Встановити головною сценою".
Ви мали би успішно переміщати повсюди гравця, спостерігати, як народжуються моби, і бачити, як зникає гравець при зіткненні з мобом.
Переконавшись, що все працює вилучає виклик new_game() з _ready() і замініть його на pass.
Чого не вистачає нашій грі? Деякого інтерфейсу. На наступному уроці ми додамо титульний екран і відображення рахунку гравця.