Questo "valore Y" viene considerato prendendo il centro della "croce" che appare sull'oggetto (cioè la posizione del suo "posizionamento").
Appunti di programmazione: pezzi di codice, note, soluzioni e algoritmi.
venerdì 26 settembre 2025
GODOT - Elemento dietro e davanti ad un oggetto
Questo "valore Y" viene considerato prendendo il centro della "croce" che appare sull'oggetto (cioè la posizione del suo "posizionamento").
lunedì 22 settembre 2025
GODOT - Uso dei timer
Mi è capitato di dover inserire un timer nel codice GDScript per gestire degli eventi temporizzati senza utilizzare un singolo elemento aggiunto ai nodi (cosa che potrebbe essere problematica se si devono temporizzare più effetti/azioni).
La definizione/inizializzazione del timer è abbastanza semplice:
self.timer = Timer.new() # creo una nuova variabile di tipo Timer
self.timer.autostart = false # indico che non deve partire in automatico
self.timer.one_shot = true # indico che deve "girare" solo una volta
self.timer.paused = false # indico che il timer non è "in pausa"
Però... per poter partire, il timer dev'essere inserito in un nodo per poter funzionare!
$NodeInTree.add_child(self.timer)self.timer.timeout.connect(self._on_timeout)
self.timer.start(5)
func _on_timeout() -> void:
passlunedì 15 settembre 2025
GODOT - Risoluzione per cellulari Android
Per avere l'applicazione a schermo intero, la risoluzione corretta è: 2400x1080 (almeno, sul mio OnePlus Nord CE 5G).
E' possibile ovviamente utilizzare il viewport per definire delle risoluzioni minori, che "scalino".
Usando la 1200x540, l'effetto è gradevole e non è "pixellato".
domenica 14 settembre 2025
GODOT - Icone per Android
Provando a realizzare un'icona adatta ad un'app per android, mi sono accorto che il sistema "taglia" l'immagine con il contenuto centrale anziché utilizzare l'intera immagine.
In pratica, producendo un'icona 432x432 (come da guida dell'Export), si ottiene un'icona più piccola. Per cui bisogna considerare visibile esclusivamente il campo 284x284 all'interno dell'icona.
Per un'analisi dettagliata su come creare le icone per Android, andare qui.
mercoledì 10 settembre 2025
LOVE2D - Formattazione di stringhe
In Love2d è possibile utilizzare la funzione string.format per stampare (o, in generale, formattare) i dati. Considerando la mancanza di una funzione di serializzazione per il salvataggio dei dati, è bene sapere come funziona questa formattazione.
In pratica, funziona come la printf in C: utilizza una combinazione di caratteri particolari per indicare che tipologia di dato si vuole stampare.
Ecco una lista delle tipologie di dato stampabili:
%s stringa
%d decimale o intero
%f formattazione a virgola mobile (ad esempio, per math.pi stampa 3.141593)
%e formattazione scientifica (ad esempio, per math.pi stampa 3.141593e+00)
$E formattazione scientifica (ad esempio, per math.pi stampa 3.141593E+00)
%g formattazione "automatica" (ad esempio, per math.pi stampa 3.141593)
%G formattazione "automatica" (ad esempio, per math.pi stampa 3.141593)
%o valore in base ottale
%x valore in base esadecimale
%c carattere
%q codice Lua
Da notare l'ultima tipologia, utilissima per stampare esattamente quello che ha interpretato il codice Lua!
Altre informazioni si possono trovare a questo indirizzo:
martedì 9 settembre 2025
GODOT - Animare un contatore "score"
Per animare un contatore di score, bisogna usare uno script e sfruttare il "delta" fornito dalla funzione "process".
Questo lo script di esempio:
var finished = true
var time = 0.0
var time_to_current_score = 2.0
var a = true
var score = 1001
func _process(delta):
if finished and a:
time += delta
text = str(int(time / time_to_current_score * score))
if time >= time_to_current_score:
a = false
text = str(score)
GODOT - Impostare i font per la Pixel Art
Ho ricreato un giochetto sviluppato con il framework Love2d in Godot ed uno dei problemi più grossi che ho riscontrato è stato... come visualizzare correttamente il font senza antialiasing o altre "storture grafiche".
In Love2d, una volta impostato il filtro come "nearest", il gioco è fatto. Il rendering è perfetto.
In Godot non è così: il solo filtro non è sufficiente...
Dopo aver impostato la scena (per i test ho inserito uno sfondo, una label ed ho impostato una risoluzione di 384x216 in fullscreen), questi sono i passaggi da fare:
Gestione del file con il font
- doppio click sul file .ttf
- impostare Antialiasing a "None"
- cliccare "Reimport"
Gestione del progetto
- nella schermata General > Display > Window, impostare lo stretch mode a "viewport"
- nella schermata General > Rendering > Textures, impostare su Default Texture Filter il valore "Nearest"
Se questo non basta, ci sono altre impostazioni... che al momento non ho trovato nei menu (e che avevo visto mentre impazzivo per risolvere questo problema).
GODOT - Esportare per Windows
Per prima cosa, va installato il template di progetto.
Bisogna cliccare sul link "manage" nella "guida agli errori" (in basso), poi cliccare su "Go online" e quindi scaricare il pacchetto appropriato.
Altra cosa da scaricare e configurare: il file RCEdit.
Si trova a questo indirizzo: https://github.com/electron/rcedit/releases
Per configurarlo, cliccare su Editor > Editor Settings... > Export > Windows ed inserire il collegamento al file nella casella "rcedit".
A questo punto, è possibile impostare i vari parametri e quindi cliccare su "Export Project...".
A questo punto, Godot genererà un file eseguibile. Da notare che è di... dimensioni considerevoli!
GODOT - Esportare per Android
Con Godot è possibile sviluppare dei giochi che possono essere caricati sul cellulare (nel mio caso, un device Android).
Per farlo i passaggi sono molteplici, vediamo quindi la lista dettagliata.
No Export Template
Quando compare questo errore, bisogna cliccare su "Manage Export Template" (in basso), apparirà la schermata del Manager. Per prima cosa, bisogna assicurarsi di "essere online" (c'è un'opzione sotto la gestione del download), quindi si seleziona "Download and Install".
Java SDK e Android SDK
Per la produzione di un pacchetto Android corretto, è necessario che sulla macchina sia installato sia Android Studio che l'openSDK di Java, che si può trovare a questo indirizzo:
https://adoptium.net/temurin/releases/?variant=openjdk17
Una volta installati i due software, impostare i percorsi degli SDK in:
Editor > Editor Settings... > Export > Android
Nota:
Godot produce giochi per l'SDK 34, quindi è necessario scaricare il corretto SDK da Android Studio.
Impostare il Preset
Queste sono le opzioni da impostare:
- dopo aver aggiunto la tipologia "Android", bisogna assicurarsi che sia selezionata l'architettura "arm64"
- bisogna indicare la versione del codice (un numero intero)
- come "Export Format", impostare "Export APK"
- bisogna indicare un "Unique Name" nella forma del "dominio inverso" (ad esempio: com.google.NomeProgetto)
- il name sarà il nome dell'applicazione
Nota:
per la pubblicazione sul Play Store, bisogna registrare una chiave ".keystore" tramite questo comando:
keytool -v -genkey -keystore NOMEAPP.keystore -alias NOMEAPP -keyalg RSA -validity 10000
Il comando chiederà alcune info, bisognerà compilare ogni elemento.
Chiave di debug
La chiave di debug dovrebbe già essere attiva e presente nel gestore (presumo sia un'impostazione di base di Godot 4.4).
Se non è così, bisogna usare questo comando:
keytool -keyalg RSA -genkeypair -alias USERNAMEDEBUG -keypass PASSWORD -keystore debug.keystore -storepass PASSWORD -dname "CN=Android Debug,O=Android,C=US" -validity 9999 -deststoretype pkcs12
A questo punto, questi comandi si caricano negli Editor Settings.
Se tutto è a posto (e se il cellulare è collegato ed ha il Debug Sviluppatore attivo), comparirà il tasto "deploy" in alto a destra.
In questo modo, è possibile debuggare il gioco direttamente sul cellulare!
Problemi nel debug
Può capitare che, tentando il deploy di un gioco, il programma restituisce un errore di installazione. Se restituisce l'errore:
INSTALL_PARSE_FAILED_NO_CERTIFICATES
C:\Users\[UTENTE PC]\AppData\Local\Android\Sdk\build-tools
Per un bug, il programma cerca di usare la prima versione che trova (sul mio PC usava la 29) e questo dava problemi nella "firma" del pacchetto, rendendolo "invalido" e quindi non installabile.
Eliminando le cartelle precedenti alla 34, il problema si risolve.
lunedì 8 settembre 2025
GODOT - Nascondere la visualizzazione all'esterno di margini desiderati
- Clip Content (sotto Layout)
- Clip Children -da impostare a "Clip Only"- (sotto "Visibility")
venerdì 5 settembre 2025
GODOT - creazione di nodi dinamicamente a runtime
Lo spawning di elementi è una costante di moltissimi giochi (si usa anche nei giochi più semplici).
Per fare questo ci serviranno le seguenti scene:
- l'oggetto da riprodurre
- l'oggetto che gestisce la produzione di elementi
Prendiamo ad esempio un gioco di carte. Ho l'oggetto Carta, l'oggetto Mazzo ed il Board (sono tre scene con i dettagli all'interno).
Ognuno di questi nodi avrà il suo script collegato.
Carta
Nello script, andrà creata la funzione initialize() che configurerà il contenuto, ma al momento non concentriamoci sul suo funzionamento.
Mazzo
Lo script del mazzo prevede alcuni elementi importanti (il numero di carte, l'array con le carte all'interno, etc) ed una variabile fondamentale:
@export var cardNode: PackedScene
Questa variabile contiene la scena che andrà "duplicata" per generare tutte le carte del mazzo, e con @export sarà possibile associare il nodo desiderato direttamente dall'editor della scena.
Questa è la funzione per la creazione di tutte le carte (nell'esempio, facciamo 32 carte):
func initialize() -> void:
for i in range(self.numMaxCards):
var c: Node2D = self.cardNode.instantiate()
# al momento passo il contatore, ma si può caricare tutti i dati
# per "generare" la carta
c.initialize(i)
c.visible = false
# aggiunge la carta al mazzo
self.cardsInDeck.append(c)
# mescola il mazzo
self.cardsInDeck.shuffle()
Ci sono poi altre funzioni che servono a gestire il mazzo (numero di carte rimaste / pesca una carta / etc).
Board
Per la scena Board, nella funzione _ready() (o reset, se serve), bisogna instanziare il mazzo e poi inizializzarlo.
A questo punto, il mazzo conterrà il numero di nodi Carta voluto, ognuno con il suo dettaglio.
GODOT - uso di GDscript e scene globali
Nello sviluppo di un videogame, è utile avere delle strutture (variabili, funzioni, etc) che siano accessibili da ogni pezzo di codice.
Le "variabili globali", insomma.
In Godot è possibile farlo, piazzando variabili e codice in uno script e poi aggiungendolo all'autoload.
Procedura:
- nella sezione "FileSystem", cliccare con il tasto destro e selezionare "New Script..."
- impostare il nome dello script (global o environment o generic_functions)
- inserire nello script tutto ciò che il sistema deve sapere e "condividere"
- aprire "Project" > "Project Settings..."
- selezionare la voce di menu "Globals"
- selezionare la sezione "Autoload"
- inserire lo script nel campo "Path" (o cliccare sulla cartellina e selezionarlo con il mouse)
- inserire il nome del nodo a cui si avrà accesso nel resto degli script (occhio a maiuscole e minuscole)
- cliccare "+ Add"
E' anche possibile inserire anche delle scene nella sezione "Globals > autoload", per facilitare l'accesso ai componenti.
Potrebbe essere interessante per passare dati tra una scena e l'altra.
giovedì 4 settembre 2025
LOVE2D - Abilitare il fullscreen dal gioco
E' possibile far partire il gioco in "fullscreen" oppure si può impostarlo da codice. Vediamo come impostarlo da codice.
Partiamo dal passaggio al fullscreen.
I passaggi sono:
- recuperare i "mode" della finestra (dimensioni e flag)
- salvare la posizione attuale in una variabile (opzionale, serve per il ripristino)
- impostare mode con il fullscreen
local w, h, flags = love.window.getMode()
local pos_x, pos_y, pos_index = love.window.getPosition()
Position = {
x = pos_x ,
y = pos_y ,
displayindex = pos_index
}
flags.fullscreen = true
love.window.setMode(0, 0, flags)
Se invece vogliamo tornare alla finestra, i passaggi sono più o meno "inversi":
flags.fullscreen = false
flags.borderless = false
flags.resizable = false
love.window.setMode(DIM.W, DIM.H, flags)
love.window.setPosition( Position.x, Position.y, Position.displayindex )
Nota: DIM.W e DIM.H sono le dimensioni "originali" della finestra (vedere post precedente sullo "scalare" la grafica).
LOVE2D - Grafica "scalare"
Ho creato uno o due giochetti in pixelart ed uno dei problemi che ho dovuto affrontare è stato... "scalare" la grafica per evitare di fare un "gioco per formiche".
Questo è un piccolo tutorial che spiega cosa serve fare per gestire "scaling" (più avanti ci sarà un post sul fullscreen).
Per prima cosa, è necessario scegliere una dimensione "standard" da cui partire. Per ottenere il miglior effetto di "scaling", bisogna che la grafica di arrivo (ad esempio, la risoluzione -ormai standard- 1920x1080) sia un moltiplicatore esatto della grafica di partenza.
Le due opzioni più sensate sono il x3 ed il x4, che hanno rispettivamente le seguenti dimensioni:
x3 640x360
x4 480x270
x5 384x216
Nota: si può ovviamente lavorare ad un progetto con risoluzioni iniziali "standard", ad esempio 640x480, bisogna però "ridurre" il moltiplicatore e gestire il translate.
La prima cosa da fare è inserire il filtro "nearest" per disegnare i pixel senza "blur". Questo va fatto prima di qualsiasi aggiunta di asset (creati con newImage), in questo modo tutto sarà "nitido":
love.graphics.setDefaultFilter("nearest", "nearest")
Definiamo, a questo punto, una dimensione standard (quella utilizzata per il gioco, di norma indicata come "virtuale" -VR-) e la dimensione "finale" in finestra:
DIM_VR = {
W = 384 ,
H = 216
}
DIM = {
W = 384 * 2 ,
H = 216 * 2
}
Per far partire il gioco alla dimensione corretta, impostare gli stessi valori di DIM anche nel love.conf(t):
function love.conf(t)
t.window.width = 384
t.window.height = 216
end
Eventualmente, si possono impostare gli stessi valori nel love.load():
function love.load()
local w, h, flags = love.window.getMode()
if w ~= DIM.W or h ~= DIM.H then
love.window.setMode(DIM.W, DIM.H, flags)
end
end
Concentriamoci sul love.draw(t), dove andremo a "sistemare" la scala della grafica.
Per disegnare correttamente il tutto, useremo un "buffer" e "scaleremo" quello.
I passaggi necessari sono questi:
- recupero delle dimensioni della finestra
- creazione del canvas
- calcolo della scala come "moltiplicatore intero"
- impostazione del canvas
- impostazione della scala
- eventuale traslazione per disegnare il tutto
- disegno della schermata di gioco
- chiusura del canvas
- disegno a video del tutto
Il codice, quindi, è:
function love.draw(dt)
-- dimensioni finestra
local screenSizeX, screenSizeY = love.graphics.getDimensions()
-- creazione canvas
local framebuffer = love.graphics.newCanvas(screenSizeX, screenSizeY)
-- calcolo scala
local scale = math.floor(math.min(screenSizeX / DIM_VR.W, screenSizeY / DIM_VR.H))
-- gestione del canvas e scala
love.graphics.setCanvas(framebuffer)
love.graphics.push()
love.graphics.scale(scale, scale)
-- gestione del translate
trans_x = (screenSizeX - DIM_VR.W * scale) / 2
trans_y = (screenSizeY - DIM_VR.H * scale) / 2
love.graphics.translate(trans_x, trans_y)
-- DISEGNO DELLA SCHERMATA DI GIOCO
love.graphics.draw(Asset)
-- reset
love.graphics.setCanvas()
love.graphics.pop()
-- disegno del canvas
love.graphics.draw(framebuffer, 0, 0)
end
Innestando questo codice, il sistema disegnerà la pagina della dimensione voluta in modo "scalato". Se la scala effettiva non è "intera", il disegno verrà centrato nella schermata.
Nota: a questo punto bisogna solo ricordarsi che l'intera grafica è "scalata", per cui se si utilizza il mouse come input, per ottenere la posizione corretta è necessario dividere il valore di X e Y per la scala!
martedì 2 settembre 2025
LOVE2D - Garbage collector
Facendo un test con un videogame che sto sviluppando, mi sono accorto che il quantitativo di RAM "mangiata" dal programma aumentava a vista d'occhio, fino a rendere instabile il sistema.
A quanto pare, il garbage collector non veniva chiamato correttamente dal framework, quindi l'ho impostato manualmente nella sezione love.update() di main.lua:
function love.update(dt)
collectgarbage("collect")
end
In questo modo il sistema pulisce costantemente ed infatti la RAM non è più stata "fagocitata".