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...
Programování pohybu hráčem
In this lesson, we'll add player movement, animation, and set it up to detect collisions.
Nyní musíme přidat některé funkce, které nám připravený uzel neposkytuje, proto je naprogramujeme pomocí skriptu. Klikněte na uzel Player
a dále na tlačítko "Připojit skript":

V okně nastavení skriptu můžete ponechat výchozí nastavení. Stačí kliknout na „Vytvořit“:
Poznámka
Pokud vytváříte skript v C# nebo v jiném jazyku, vyberte požadovaný jazyk z rozbalovací nabídky jazyk před tím, než kliknete vytvořit.

Poznámka
If this is your first time encountering GDScript, please read Skriptovací jazyky before continuing.
Začněte deklarováním členských proměnných, které bude tento objekt potřebovat:
extends Area2D
@export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.
using Godot;
public partial class Player : Area2D
{
[Export]
public int Speed { get; set; } = 400; // How fast the player will move (pixels/sec).
public Vector2 ScreenSize; // Size of the game window.
}
Using the export
keyword on the first variable speed
allows us to set
its value in the Inspector. This can be handy for values that you want to be
able to adjust just like a node's built-in properties. Click on the Player
node and you'll see the property now appears in the Inspector in a new section
with the name of the script. Remember, if you change the value here, it will
override the value written in the script.
Varování
If you're using C#, you need to (re)build the project assemblies whenever you want to see new export variables or signals. This build can be manually triggered by clicking the Build button at the top right of the editor.


Your player.gd
script should already contain
a _ready()
and a _process()
function.
If you didn't select the default template shown above,
create these functions while following the lesson.
Funkce _ready ()
se volá, když uzel vstoupuje do stromu scény, a to je dobrý čas k nalezení velikosti okna hry:
func _ready():
screen_size = get_viewport_rect().size
public override void _Ready()
{
ScreenSize = GetViewportRect().Size;
}
Nyní můžeme pomocí funkce _process ()
definovat, co bude hráč dělat. _process ()
se volá v každém framu(snímku) takže tuto funkci použijeme k aktualizaci takových prvků naší hry, které se často změní. Pro hráče musíme udělat následující:
Zkontrolovat vstup.
Pohnout se v požadovaném směru.
Přehrát příslušnou animaci.
Nejprve musíme zkontrolovat vstup - tiskne hráč klávesu? Pro tuto hru máme k dispozici 4 vstupy pro směr. Vstupní akce jsou definovány v Nastavení projektu v části „Mapa vstupů“. Zde můžete definovat vlastní události a přiřadit jim různé klávesy, události myši nebo jiné vstupy. Pro toto demo použijeme výchozí události, které jsou přiřazeny kurzorovým klávesám klávesnice.
Click on Project -> Project Settings to open the project settings window and
click on the Input Map tab at the top. Type "move_right" in the top bar and
click the "Add" button to add the move_right
action.

We need to assign a key to this action. Click the "+" icon on the right, to open the event manager window.

The "Listening for Input..." field should automatically be selected. Press the "right" key on your keyboard, and the menu should look like this now.

Select the "ok" button. The "right" key is now associated with the move_right
action.
Repeat these steps to add three more mappings:
move_left
mapped to the left arrow key.move_up
mapped to the up arrow key.And
move_down
mapped to the down arrow key.
Karta vstupní mapy by měla vypadat takto:

Click the "Close" button to close the project settings.
Poznámka
We only mapped one key to each input action, but you can map multiple keys, joystick buttons, or mouse buttons to the same input action.
Zda je klávesa stisknuta můžete zjistit pomocí funkce Input.is_action_pressed()
, která vrací true
, pokud je stisknuta, nebo false
, pokud není.
func _process(delta):
var velocity = Vector2.ZERO # The player's movement vector.
if Input.is_action_pressed("move_right"):
velocity.x += 1
if Input.is_action_pressed("move_left"):
velocity.x -= 1
if Input.is_action_pressed("move_down"):
velocity.y += 1
if Input.is_action_pressed("move_up"):
velocity.y -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
public override void _Process(double delta)
{
var velocity = Vector2.Zero; // The player's movement vector.
if (Input.IsActionPressed("move_right"))
{
velocity.X += 1;
}
if (Input.IsActionPressed("move_left"))
{
velocity.X -= 1;
}
if (Input.IsActionPressed("move_down"))
{
velocity.Y += 1;
}
if (Input.IsActionPressed("move_up"))
{
velocity.Y -= 1;
}
var animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
if (velocity.Length() > 0)
{
velocity = velocity.Normalized() * Speed;
animatedSprite2D.Play();
}
else
{
animatedSprite2D.Stop();
}
}
Začneme nastavením velocity
na (0, 0)
- ve výchozím nastavení by se hráč neměl pohybovat. Pak zkontrolujeme každý vstup a přidáme / odečteme z velocity
, abychom získali celkový směr. Například pokud podržíte right
a down
současně, bude výsledný vektor velocity
(1, 1)
. V tomto případě, protože přidáváme vodorovný a svislý pohyb, by se hráč pohyboval rychleji diagonálně, než kdyby se pohyboval vodorovně.
Můžeme tomu zabránit, pokud normalizujeme vektor rychlosti, což znamená, že nastavíme jeho velikost na 1
, a vynásobíme jej velikostí požadované rychlostí. To znamená, už žádné rychlé diagonální pohyby.
Tip
Pokud jste nikdy předtím nepoužívali vektorovou matematiku, nebo potřebujete málé opáčko, používání vektorů v Godotu najdete na Vektorová matematika. Je dobré vektory znát, ale po zbytek tohoto tutoriálu to nebude nutné.
We also check whether the player is moving so we can call play()
or
stop()
on the AnimatedSprite2D.
Tip
$
is shorthand for get_node()
. So in the code above,
$AnimatedSprite2D.play()
is the same as
get_node("AnimatedSprite2D").play()
.
In GDScript, $
returns the node at the relative path from the
current node, or returns null
if the node is not found. Since
AnimatedSprite2D is a child of the current node, we can use
$AnimatedSprite2D
.
Nyní, když máme směr pohybu, můžeme aktualizovat pozici hráče. Můžeme také použít clamp()
, abychom zabránili opuštění obrazovky. Clamp hodnotu znamená omezit ji na určitý rozsah. Na konec funkce _process
přidejte následující (ujistěte se, že není odsazeno jako pokračování větve else):
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)
Position += velocity * (float)delta;
Position = new Vector2(
x: Mathf.Clamp(Position.X, 0, ScreenSize.X),
y: Mathf.Clamp(Position.Y, 0, ScreenSize.Y)
);
Tip
Parametr delta
ve funkci _process()
označuje délku snímku - dobu, kterou předchozí snímek potřeboval k dokončení. Použitím této hodnoty zajistíte, že pohyb zůstane konzistentní, i když se změní snímková frekvence.
Click "Run Current Scene" (F6, Cmd + R on macOS) and confirm you can move the player around the screen in all directions.
Varování
Pokud se zobrazí následující chyba v panelu "Debugger"
Pokus o volání funkce 'play' v základní 'null instance' na null instance
this likely means you spelled the name of the AnimatedSprite2D node
wrong. Node names are case-sensitive and $NodeName
must match
the name you see in the scene tree.
Výběr animací
Now that the player can move, we need to change which animation the
AnimatedSprite2D is playing based on its direction. We have the "walk" animation,
which shows the player walking to the right. This animation should be flipped
horizontally using the flip_h
property for left movement. We also have the
"up" animation, which should be flipped vertically with flip_v
for downward
movement. Let's place this code at the end of the _process()
function:
if velocity.x != 0:
$AnimatedSprite2D.animation = "walk"
$AnimatedSprite2D.flip_v = false
# See the note below about the following boolean assignment.
$AnimatedSprite2D.flip_h = velocity.x < 0
elif velocity.y != 0:
$AnimatedSprite2D.animation = "up"
$AnimatedSprite2D.flip_v = velocity.y > 0
if (velocity.X != 0)
{
animatedSprite2D.Animation = "walk";
animatedSprite2D.FlipV = false;
// See the note below about the following boolean assignment.
animatedSprite2D.FlipH = velocity.X < 0;
}
else if (velocity.Y != 0)
{
animatedSprite2D.Animation = "up";
animatedSprite2D.FlipV = velocity.Y > 0;
}
Poznámka
Booleovská přiřazení ve výše uvedeném kódu jsou běžnými zkratkami při programování. Protože provádíme porovnání (booleovsky) a pak přiřazujeme booleovskou hodnotu, můžeme oba kroky provést současně. Porovnejte tento kód s jednořádkovým přiřazením výše:
if velocity.x < 0:
$AnimatedSprite2D.flip_h = true
else:
$AnimatedSprite2D.flip_h = false
if (velocity.X < 0)
{
animatedSprite2D.FlipH = true;
}
else
{
animatedSprite2D.FlipH = false;
}
Zahrajte si scénu znovu a zkontrolujte, zda jsou animace ve všech směrech odpovídající.
Tip
Častou chybou je nesprávné zadání názvů animací. Názvy animací v panelu SpriteFrames se musí shodovat s tím, co zadáte v kódu. Pokud jste animaci pojmenovali "Walk"
, musíte v kódu také použít velké písmeno "W".
Pokud jste si jisti, že pohyb funguje správně, přidejte tento řádek do ``_ready () ``, takže hráč bude při spuštění hry skrytý:
hide()
Hide();
Příprava na kolize
Chceme, aby Player
detekoval, když je zasažen nepřítelem, ale zatím jsme žádného nepřítele nevytvořili! To je v pořádku, protože zde použijeme Gotovi signály.
Add the following at the top of the script. If you're using GDScript, add it after
extends Area2D
. If you're using C#, add it after public partial class Player : Area2D
:
signal hit
// Don't forget to rebuild the project so the editor knows about the new signal.
[Signal]
public delegate void HitEventHandler();
Toto definuje vlastní signál zvaný "hit", který bude hráč vysílat při srážce s nepřítelem. K detekci kolize použijeme Area2D
. Vyberte uzel Player
a klikněte na záložku "Uzel" vedle záložky Inspektor, aby se zobrazil seznam signálů, které může hráč vyslat:

Notice our custom "hit" signal is there as well! Since our enemies are going to
be RigidBody2D
nodes, we want the body_entered(body: Node2D)
signal. This
signal will be emitted when a body contacts the player. Click "Connect.." and
the "Connect a Signal" window appears.
Godot will create a function with that exact name directly in script for you. You don't need to change the default settings right now.
Varování
If you're using an external text editor (for example, Visual Studio Code), a bug currently prevents Godot from doing so. You'll be sent to your external editor, but the new function won't be there.
In this case, you'll need to write the function yourself into the Player's script file.

Note the green icon indicating that a signal is connected to this function; this does not mean the function exists, only that the signal will attempt to connect to a function with that name, so double-check that the spelling of the function matches exactly!
Next, add this code to the function:
func _on_body_entered(_body):
hide() # Player disappears after being hit.
hit.emit()
# Must be deferred as we can't change physics properties on a physics callback.
$CollisionShape2D.set_deferred("disabled", true)
// We also specified this function name in PascalCase in the editor's connection window.
private void OnBodyEntered(Node2D body)
{
Hide(); // Player disappears after being hit.
EmitSignal(SignalName.Hit);
// Must be deferred as we can't change physics properties on a physics callback.
GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred(CollisionShape2D.PropertyName.Disabled, true);
}
Pokaždé, když hráče zasáhne nepřítel, bude vyslán signál. Musíme pak vypnout kolizi hráče, abychom nespustili signál „hit“ více než jednou.
Poznámka
Zakázání kolizního tvaru uzlu může způsobit chybu, pokud k tomu dojde uprostřed zpracovávání kolize enginem. Pomocí set_deferred()
řekneme Godotovi, aby počkal, s vypnutím kolizního tvaru, dokud to nebude bezpečné.
Posledním kusem je přidání funkce, kterou můžeme nazvat resetem hráče při spuštění nové hry.
func start(pos):
position = pos
show()
$CollisionShape2D.disabled = false
public void Start(Vector2 position)
{
Position = position;
Show();
GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
}
With the player working, we'll work on the enemy in the next lesson.