Godot 4 per Android con C#: Movimento del Personaggio e Input Touch
Questa è la quarta bozza della serie su Godot 4 per Android usando esclusivamente C#. In questa guida creiamo un personaggio 2D, lo facciamo muovere con CharacterBody2D, configuriamo i controlli tramite Input e InputMap, aggiungiamo pulsanti/joystick virtuali per il touch e testiamo tutto su emulatore e dispositivo fisico Android.
Obiettivi della guida
Alla fine di questo articolo sarai in grado di:
- Creare un semplice personaggio 2D usando
CharacterBody2D. - Scrivere uno script C# per gestire il movimento in Godot 4.
- Usare
InputeInputMapper astrarre i comandi. - Configurare controlli touch adatti ad Android (pulsanti virtuali e joystick virtuale).
- Testare e regolare il comportamento su emulatore e dispositivo fisico.
- Applicare buone pratiche di responsività e prestazioni su mobile.
Prerequisiti
Per seguire la guida ti serve:
- Godot 4 installato (consigliata l’ultima stable 4.x).
- Modulo C#/.NET configurato con un IDE compatibile (Visual Studio, Rider, VS Code ecc.).
- Un progetto Godot 4 2D già creato per Android (firma, export template e dispositivo/emulatore configurati), come visto negli articoli precedenti.
- Conoscenze di base di C# (classi, metodi, proprietà, tipi di dato fondamentali).
- Facoltativo ma utile: un dispositivo Android reale collegato via USB con debug abilitato.
1. Creare la scena del personaggio 2D con CharacterBody2D
In Godot 4, CharacterBody2D è il nodo consigliato per personaggi 2D che si muovono e reagiscono alla fisica in modo controllato (platformer, top-down, ecc.). Offre una logica di movimento pronta, basata su Velocity e sul metodo MoveAndSlide().
1.1. Struttura base della scena del personaggio
Creiamo una nuova scena per il personaggio:
- Crea una nuova scena vuota.
- Aggiungi un nodo principale di tipo CharacterBody2D e rinominalo in
Player. - Aggiungi come figlio un Sprite2D per la grafica (puoi usare un placeholder).
- Aggiungi come figlio un CollisionShape2D con una forma (es. Rettangolo) che circonda lo sprite.
La gerarchia dovrebbe apparire circa così:
- Player (CharacterBody2D)
- Sprite2D
- CollisionShape2D
1.2. Impostare l’orientamento dello schermo
Per un gioco 2D con controlli touch è importante scegliere l’orientamento in anticipo, perché influisce sulla posizione dei pulsanti virtuali.
- Apri Project > Project Settings > Display > Window.
- Imposta Orientation su Landscape (o Portrait se preferisci un gioco verticale).
- Imposta Stretch > Mode su 2d e Aspect su keep o keep_width/keep_height in base alle tue necessità.
Per questa guida useremo Landscape, tipico per action e platformer.
2. Script C# per il movimento del personaggio
Ora colleghiamo uno script C# al nodo Player per gestire il movimento usando la proprietà Velocity e gli input definiti in InputMap.
2.1. Creare lo script C#
Seleziona il nodo Player e crea uno script C# chiamato, ad esempio, Player.cs. Assicurati che il linguaggio sia impostato su C# e che lo script si trovi in una cartella chiara, come Scripts/.
using Godot;
using System;
public partial class Player : CharacterBody2D
{
[Export]
public float Speed { get; set; } = 200.0f; // Velocità di movimento in pixel/secondo
public override void _PhysicsProcess(double delta)
{
Vector2 inputDirection = GetInputDirection();
// Normalizziamo per evitare che in diagonale sia più veloce
if (inputDirection.Length() > 1.0f)
inputDirection = inputDirection.Normalized();
Velocity = inputDirection * Speed;
MoveAndSlide();
}
private Vector2 GetInputDirection()
{
float x = 0.0f;
float y = 0.0f;
// Legge gli input mappati (tastiera, gamepad o touch virtuale)
if (Input.IsActionPressed("move_right"))
x += 1.0f;
if (Input.IsActionPressed("move_left"))
x -= 1.0f;
if (Input.IsActionPressed("move_down"))
y += 1.0f;
if (Input.IsActionPressed("move_up"))
y -= 1.0f;
return new Vector2(x, y);
}
}
Questo approccio funziona sia su desktop sia su Android, a patto che le azioni move_* siano configurate correttamente in InputMap e che i controlli touch le attivino.
3. Configurare Input e InputMap
InputMap ti permette di definire azioni astratte (es. move_left) e collegarle a tasti, mouse, gamepad o zone touch. È fondamentale per avere lo stesso codice C# che funziona su più piattaforme.
3.1. Definire le azioni di movimento
Apri Project > Project Settings > Input Map e aggiungi queste azioni:
move_leftmove_rightmove_upmove_down
Per ognuna, aggiungi almeno un tasto da tastiera per testare su desktop, ad esempio:
move_left→ freccia sinistra, tastoAmove_right→ freccia destra, tastoDmove_up→ freccia su, tastoWmove_down→ freccia giù, tastoS
Più avanti collegheremo a queste azioni i pulsanti virtuali touch.
3.2. Test rapido su desktop
Prima di passare ad Android, avvia il gioco su desktop e verifica che il personaggio si muova con tastiera. Questo ti permette di isolare gli eventuali problemi di logica di movimento, prima di complicare la scena con l’input touch.
4. Controlli touch per Android: pulsanti virtuali
Per Android puoi creare pulsanti virtuali che, quando toccati, attivano le stesse azioni di InputMap usate per tastiera e gamepad. Useremo i controlli GUI di Godot (nodi Control) in un CanvasLayer, così restano fissi sullo schermo indipendentemente dalla telecamera.
4.1. Creare il layout dei pulsanti
Apri la scena principale del gioco (es. Main.tscn) che contiene il mondo e istanzia il tuo Player. Poi:
- Aggiungi un nodo CanvasLayer chiamato
UI. - Dentro
UI, aggiungi un nodo Control (o MarginContainer) per gestire il layout. - Imposta il Layout del nodo principale UI su Full Rect (tasto destro > Layout > Full Rect).
Per creare una croce direzionale semplice:
- Aggiungi quattro pulsanti (TextureButton o Button) agli angoli inferiori, o una vera e propria croce in basso a sinistra.
- Ad esempio, crea un Control chiamato
VirtualDPadancorato in basso a sinistra, e dentro aggiungi i pulsantiBtnLeft,BtnRight,BtnUp,BtnDown.
Assicurati che i nodi Control usino ancoraggi e margini appropriati, così i pulsanti restano nella posizione corretta su dispositivi con risoluzioni diverse. Puoi testare varie risoluzioni con il pannello Test Width/Height nelle impostazioni di gioco o nella barra in alto.
4.2. Collegare i pulsanti a InputMap (pulsanti → azioni)
L’idea è: quando un pulsante viene premuto, chiamiamo Input.ActionPress("move_left"), e quando viene rilasciato chiamiamo Input.ActionRelease("move_left"). In questo modo il tuo script Player non sa se l’input arriva da tastiera o touch.
Crea uno script C# sul nodo VirtualDPad, ad esempio VirtualDPad.cs:
using Godot;
using System;
public partial class VirtualDPad : Control
{
public override void _Ready()
{
// Colleghiamo via codice i segnali dei pulsanti (in alternativa, usa l'editor di segnali)
GetNode<Button>("BtnLeft").Pressed += OnLeftPressed;
GetNode<Button>("BtnLeft").ButtonUp += OnLeftReleased;
GetNode<Button>("BtnRight").Pressed += OnRightPressed;
GetNode<Button>("BtnRight").ButtonUp += OnRightReleased;
GetNode<Button>("BtnUp").Pressed += OnUpPressed;
GetNode<Button>("BtnUp").ButtonUp += OnUpReleased;
GetNode<Button>("BtnDown").Pressed += OnDownPressed;
GetNode<Button>("BtnDown").ButtonUp += OnDownReleased;
}
private void OnLeftPressed() => Input.ActionPress("move_left");
private void OnLeftReleased() => Input.ActionRelease("move_left");
private void OnRightPressed() => Input.ActionPress("move_right");
private void OnRightReleased() => Input.ActionRelease("move_right");
private void OnUpPressed() => Input.ActionPress("move_up");
private void OnUpReleased() => Input.ActionRelease("move_up");
private void OnDownPressed() => Input.ActionPress("move_down");
private void OnDownReleased() => Input.ActionRelease("move_down");
}
Su mobile i pulsanti reagiranno al tocco; il tuo player userà sempre Input.IsActionPressed(), senza conoscere il dispositivo.
5. Joystick virtuale (opzionale ma consigliato)
I pulsanti direzionali funzionano, ma un joystick virtuale può risultare più fluido per alcuni giochi. L’idea di base è avere due elementi:
- Un background circolare (area totale del joystick).
- Un thumb (manopola) che si sposta verso la direzione del tocco.
In C#, calcoliamo la direzione dal centro al punto di tocco e la convertiamo in input normalizzati.
5.1. Struttura del joystick virtuale
Nel tuo CanvasLayer UI, aggiungi un nuovo Control chiamato VirtualJoystick, ancorato in basso a sinistra. All’interno:
Bg(TextureRect o TextureButton) → sfondo tondo del joystick.Thumb(TextureRect) → manopola.
Assicurati che VirtualJoystick sia abbastanza grande per contenere l’area di input, ma non troppo da coprire lo schermo. Usa gli ancoraggi per adattarlo a schermi diversi.
5.2. Script C# per il joystick virtuale
Crea VirtualJoystick.cs sul nodo VirtualJoystick:
using Godot;
using System;
public partial class VirtualJoystick : Control
{
[Export]
public float DeadZone { get; set; } = 0.15f; // Valore sotto cui l'input viene ignorato
private Vector2 _direction = Vector2.Zero;
private bool _isDragging = false;
private Vector2 _center;
private float _radius;
private TextureRect _thumb;
public override void _Ready()
{
_thumb = GetNode<TextureRect>("Thumb");
_center = RectSize / 2.0f;
_radius = Math.Min(RectSize.X, RectSize.Y) / 2.0f;
_thumb.Position = _center - _thumb.RectSize / 2.0f;
}
public override void _GuiInput(InputEvent @event)
{
if (@event is InputEventScreenTouch touchEvent)
{
if (touchEvent.Pressed)
{
_isDragging = true;
UpdateJoystick(touchEvent.Position);
}
else
{
_isDragging = false;
ResetJoystick();
}
}
else if (@event is InputEventScreenDrag dragEvent && _isDragging)
{
UpdateJoystick(dragEvent.Position);
}
}
private void UpdateJoystick(Vector2 touchPos)
{
Vector2 offset = touchPos - _center;
float distance = offset.Length();
if (distance > _radius)
{
offset = offset.Normalized() * _radius;
distance = _radius;
}
_thumb.Position = _center + offset - _thumb.RectSize / 2.0f;
if (_radius > 0)
{
_direction = offset / _radius; // Valori da -1 a 1
}
else
{
_direction = Vector2.Zero;
}
// Applica dead zone
if (_direction.Length() < DeadZone)
_direction = Vector2.Zero;
UpdateActions();
}
private void ResetJoystick()
{
_direction = Vector2.Zero;
_thumb.Position = _center - _thumb.RectSize / 2.0f;
UpdateActions();
}
private void UpdateActions()
{
// Rilascia tutte le azioni
Input.ActionRelease("move_left");
Input.ActionRelease("move_right");
Input.ActionRelease("move_up");
Input.ActionRelease("move_down");
// Attiva in base alla direzione
if (_direction.X < -DeadZone)
Input.ActionPress("move_left");
if (_direction.X > DeadZone)
Input.ActionPress("move_right");
if (_direction.Y < -DeadZone)
Input.ActionPress("move_up");
if (_direction.Y > DeadZone)
Input.ActionPress("move_down");
}
}
Questo joystick converte la direzione di trascinamento in azioni di movimento discrete. Puoi estenderlo per supportare anche la velocità analogica, leggendo direttamente _direction nello script del player (vedi sezione “Approfondimenti per esperti”).
6. Responsività dei controlli e buone pratiche UI su mobile
Su Android l’esperienza dell’utente dipende moltissimo da quanto i controlli sono comodi da usare, a prescindere dalla diagonale e dalla risoluzione dello schermo.
- Usa ancoraggi, non posizioni assolute: posiziona i pulsanti rispetto agli angoli (bottom-left, bottom-right) e usa margini in unità logiche (es. 64 px) per mantenere le distanze.
- Dimensioni dei pulsanti: in generale, mantieni almeno 80–100 px di lato per pulsanti principali su schermi HD. Testa su dispositivi piccoli.
- Evita di coprire il gioco: sposta i controlli agli angoli e lascia pulito il centro per la scena di gioco.
- Supporto per mancini: opzionale ma molto apprezzato. Prevedi un toggle nelle opzioni per scambiare joystick e pulsanti d’azione.
7. Buone pratiche per le prestazioni su Android
Il codice di input e movimento qui mostrato è leggero, ma su Android le prestazioni sono influenzate anche da altri aspetti del progetto. Alcuni suggerimenti:
- Limita il lavoro in
_PhysicsProcess: tieni solo la logica di movimento essenziale, come negli esempi, ed evita allocazioni di oggetti ad ogni frame. - Riduci texture e draw call: usa sprite di dimensioni ragionevoli e combina elementi statici in tilemap o sprite sheet.
- Disattiva nodi inutilizzati: nascondi o disattiva i nodi (e i relativi
Process) quando non sono visibili o rilevanti. - Profiler: usa il profiler integrato di Godot collegando il dispositivo Android per individuare colli di bottiglia reali.
8. Test su emulatore e dispositivo fisico
8.1. Test su emulatore Android
Per un primo controllo, l’emulatore Android (Android Studio o altro) è sufficiente:
- Configura un AVD con la stessa orientazione (Landscape) scelta per il gioco.
- Esporta il progetto con il preset Android configurato (come visto nelle guide precedenti).
- Installa l’APK sull’emulatore e prova i controlli touch con il mouse.
L’emulatore può avere latenze diverse da un dispositivo reale, quindi usa questo test solo per una verifica di base del layout e del funzionamento logico dei controlli.
8.2. Test su dispositivo fisico
Il vero test è sempre sul dispositivo fisico:
- Abilita il Debug USB nelle opzioni sviluppatore del telefono.
- Collega il dispositivo al PC.
- Da Godot, usa Run > Select Current Device e scegli il device Android collegato.
- Avvia il gioco e verifica: comodità dei controlli, reattività, visibilità.
Prova il gioco sia in posizione naturale di utilizzo (due mani, pollici sui controlli) sia in condizioni meno ideali (una mano sola) per capire se serve aumentare la dimensione dei pulsanti o modificarne la posizione.
9. Errori comuni e come risolverli
- Il personaggio non si muove
Controlla che:
– Lo scriptPlayer.cssia effettivamente collegato al nodoPlayer.
– Le azioni inInputMapabbiano il nome esatto usato nel codice (move_leftecc.).
–MoveAndSlide()venga chiamato in_PhysicsProcess. - I pulsanti touch non fanno nulla
Verifica che:
– Lo scriptVirtualDPad.cssia assegnato al nodo giusto.
– I nomi dei pulsanti inGetNode<Button>()corrispondano alla gerarchia della scena.
– IlCanvasLayerUI sia incluso nella scena principale che stai eseguendo. - Joystick virtuale non risponde correttamente
– Controlla cheVirtualJoystickabbiaMouse Filterimpostato a Stop per ricevere gli eventi.
– Verifica che il joystick non sia coperto da altri nodi UI.
– Stampa_directionin debug per vedere i valori effettivi. - Controlli spostati o tagliati su alcuni schermi
– Usa ancoraggi corretti (bottom-left/bottom-right) invece di posizioni assolute.
– Testa varie risoluzioni con gli strumenti di Godot.
10. FAQ
Posso usare lo stesso codice per desktop e Android?
Sì. Se usi InputMap e azioni astratte, lo script del personaggio non deve sapere da dove arrivano gli input. Su desktop userai tastiera/gamepad, su Android pulsanti o joystick virtuale, ma il codice rimane lo stesso.
Come gestisco la sensibilità del joystick virtuale?
La proprietà DeadZone ti permette di ignorare movimenti molto piccoli, che spesso sono tocchi involontari. Puoi anche usare _direction.Length() per modulare la velocità in base a quanto l’utente spinge il joystick verso il bordo.
E se volessi usare solo swipe invece di pulsanti/joystick?
È possibile: gestisci gli eventi InputEventScreenDrag globalmente (es. in un nodo Control a pieno schermo) e traduci lo swipe in direzioni e azioni Input.ActionPress()/ActionRelease(). La logica di movimento del personaggio può rimanere quasi invariata.
Serve per forza un joystick virtuale?
No. Per molti giochi bastano 2–3 pulsanti ben posizionati. Il joystick virtuale è utile per movimenti liberi 360° o per imitare il feeling dei gamepad.
11. Approfondimenti per sviluppatori esperti
- Input analogico completo
Invece di trasformare il joystick in azioni discretemove_left/move_right, puoi esporre una proprietàpublic Vector2 DirectioninVirtualJoysticke leggerla direttamente nel player. In questo modo puoi modulare la velocità (Velocity = joystick.Direction * MaxSpeed) e avere movimenti più fluidi. - Separazione input / logica
Per progetti complessi, considera di separare completamente il “controller” del player dalla logica del personaggio. Ad esempio: un nodoPlayerInputche produce un vettore di input, e unPlayerControllerche muove ilCharacterBody2D. In questo modo puoi sostituire facilmente la fonte di input (IA, rete, replay). - Configurazioni multiple di layout
Puoi definire scene UI diverse per telefoni e tablet (o orientamento diverso) e caricare dinamicamente quella corretta in base alla risoluzione o al rapporto d’aspetto. - Profilazione dell’input
Su dispositivi di fascia bassa, troppi nodi UI possono pesare. Riduci il numero di nodi nei controlli, usa texture ottimizzate e disattiva_Process/_PhysicsProcessdove non necessario.
12. Riepilogo e prossimi passi
In questa bozza abbiamo creato un personaggio 2D con CharacterBody2D, scritto uno script C# per gestire il movimento tramite Velocity e MoveAndSlide(), configurato InputMap per astrarre i controlli e collegato pulsanti e joystick virtuali per il touch su Android. Abbiamo visto come rendere i controlli responsivi e stabili su schermi diversi, come testare il comportamento su emulatore e dispositivo fisico e quali errori evitare.
Nel prossimo articolo della serie ci concentreremo sull’interfaccia Android: creeremo una HUD con pulsanti contestuali, indicatori (vita, punteggio, energia) e menù in-game, sempre usando C# e seguendo buone pratiche di design UI per dispositivi mobili.
Lascia un commento