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...
Hlavní scéna hry
Je načase vše, co jsme dosud udělali, propojit v jedné hratelné scéně.
Vytvořte novou scénu a přidejte Node ("uzel") s názvem Main ("hlavni"). (Důvod, proč používáme Node místo Node2D, je ten, že tento uzel bude kontejnerem pro zpracování herní logiky. Sám o sobě 2D funkcionalitu nevyžaduje.)
Klikněte na tlačítko Instancovat (reprezentované ikonou řetězového článku) a vyberte svůj uložený player.tscn.
Nyní přidejte následující uzly jako potomky Main a pojmenujte je následovně:
Timer ("časovač", pojmenovaný
MobTimer) - pro ovládání času generování nepřátelTimer (pojmenovaný
ScoreTimer) - pro zvýšení skóre za každou sekundu beze srážkyTimer (pojmenovaný
StartTimer) - pro zpoždění před spuštěnímMarker2D ("2D značka" pojmenovaná
StartPosition) - pro označení počáteční pozice hráče
Nastavte vlastnost Čas čekání každého z uzlů Timer takto (hodnoty jsou v sekundách):
MobTimer:0.5ScoreTimer:1StartTimer:2
Dále nastavte vlastnost Jednorázový uzlu StartTimer na "Zapnuto" a nastavte Pozice v uzlu StartPosition na (240, 450).
Vytváření nepřátel
The Main node will be spawning new mobs, and we want them to appear at a random
location on the edge of the screen. Click the Main node in the Scene dock, then
add a child Path2D node named MobPath. When you select
Path2D, you will see some new buttons at the top of the editor:
Vyberte prostřední ("Přidat bod") a nakreslete cestu přidáním bodů tím, že kliknete na zobrazené rohy. Chcete-li, aby se body přichytávaly k mřížce, ujistěte se, že máte vybrány možnosti "Použít přichytávání k mřížce" a "Použít chytré přichytávání". Tyto možnosti, které najdete pod ikonami s magnetem a tečkami, případně mřížkou, se nachází nalevo od tlačítka "Zamknout".
Důležité
Cestu kreslete ve směru hodinových ručiček, jinak budou vaši nepřátelé natočení směrem ven místo dovnitř!
Poté, co do obrázku umístíte bod 4, klikněte na tlačítko „Zavřít křivku“ a vaše cesta bude úplná.
Nyní, když máte cestu definovanou, přidejte uzel PathFollow2D ("2D následování cesty") jako potomka MobPath a pojmenujte jej MobSpawnLocation ("pozice vytvoření nepřítele"). Tento uzel se bude automaticky otáčet a pohybovat se po cestě, takže jej můžeme použít k výběru náhodné polohy a směru podél cesty.
Vaše scéna by měla vypadat takto:
Hlavní skript
Přidejte k uzlu Main skript. V horní části skriptu použijeme @export var mob_scene: PackedScene, což nám umožní zvolit scénu Mob, pro kterou chceme vytvořit instanci.
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;
}
Klikněte na uzel Main a uvidíte vlastnost Mob Scene v Inspektoru pod "Main.gd".
Hodnotu této vlastnosti můžete přiřadit dvěma způsoby:
Přetáhnutím
mob.tscnz doku "Souborový systém" do vlastnosti Mob Scene.Kliknutím na šipku dolů vedle „[prázdné]“ a volbou "Načíst", kde vyberete
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.
Měli byste vidět seznam signálů pro uzel Player. Vyhledejte signál hit ("zasažen"), pravým tlačítkem na něj klikněte a vyberte "Připojit ..." (nebo na něj jednoduše dvojklikněte). Otevře se dialogové okno pro připojení signálu. Chceme vytvořit novou funkci s názvem game_over ("konec hry"), která se postará o to, co se musí stát, až hra skončí. Do pole "Přijímací mMetoda" ve spodní části dialogového okna pro připojení signálu zadejte "game_over" a klikněte na "Připojit". K nové funkci přidejte následující kód a také funkci new_game ("nová hra"), která vše připraví pro novou hru:
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();
}
Now we'll connect the timeout() signal of each Timer node (StartTimer,
ScoreTimer, and MobTimer) to the main script. For each of the three
timers, select the timer in the Scene dock, open the Signals tab of the Node
dock, then double-click the timeout() signal in the list. This will open a new
signal connection dialog. The default settings in this dialog should be fine, so
select Connect to create a new signal connection.
Once all three timers have this set up, you should be able to see each timer
have a Signal connection for their respective timeout() signal, showing in
green, within their respective Signals tabs:
(For MobTimer):
_on_mob_timer_timeout()(For ScoreTimer):
_on_score_timer_timeout()(For StartTimer):
_on_start_timer_timeout()
Now we define how each of these timers operate by adding the code below. Notice
that StartTimer will start the other two timers, and that ScoreTimer
will increment the score by 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();
}
Ve funkci _on_mob_timer_timeout() vytvoříme instanci nepřítele, vybereme náhodné počáteční místo podél Path2D a dáme nepřítele do pohybu. Uzel PathFollow2D se bude při pohybu podél cesty automaticky otáčet, takže jej použijeme k výběru směru a polohy nepřátel. Při vynoření nepřítele vybereme náhodnou hodnotu mezi 150.0 a 250.0. Ta určí, jak rychle se budou jednotliví nepřátelé pohybovat (kdyby se všichni pohybovali stejnou rychlostí, bylo by to nudné).
Všimněte si, že do scény musí být nová instance vložena pomocí add_child() ("přidat potomka").
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);
}
Důležité
Proč PI? Ve funkcích vyžadujících úhly používá GDScript radiány, nikoli stupně. Pí představuje půl otáčky v radiánech, přibližně 3.1415 (existuje také TAU, které je rovno 2 * PI). Pokud se vám pracuje pohodlněji se stupni, budete muset použít převodní funkce deg_to_rad() ("stupně na radiány") a rad_to_deg() ("radiány na stupně").
Testování scény
Otestujme nyní scénu, abychom se ujistili, že vše funguje. Přidejte toto zavolání new_game do _ready():
func _ready():
new_game()
public override void _Ready()
{
NewGame();
}
Pojďme také přiřadit Main jako "Hlavní scénu" - to je ta, která se spustí automaticky při spuštění hry. Stiskněte tlačítko "Spustit" a po zobrazení výzvy vyberte main.tscn.
Tip
Pokud jste již jako "Hlavní scénu" nastavili jinou, můžete kliknout pravým tlačítkem na main.tscn v doku Souborový systém a vybrat "Nastavit jako hlavní scénu".
Měli byste teď být schopni pohybovat hráčem do všech směrů, vidět objevující se nepřátele a jak hráč zmizí, když ho nepřítel zasáhne.
Až se ujistíte, že vše funguje, jak má, odeberte volání new_game() z _ready() a místo něj zadejte pass.
Co naší hře chybí? Nějaké uživatelské rozhraní. V příští lekci přidáme úvodní obrazovku a zobrazíme hráčovo skóre.