venerdì 26 settembre 2025

GODOT - Elemento dietro e davanti ad un oggetto

In alcuni giochi è necessario che il personaggio passi davanti all'oggetto, oppure dietro. Ad esempio, davanti ad un albero o dietro ad esso.
E questo deve dipendere dal punto in cui l'oggetto è "attraversato".
Godot ha un sistema per generare questo effetto: l'ordinamento sull'asse Y.

In pratica, per gli oggetti che hanno lo stesso "Z index", il sistema disegna prima gli oggetti che hanno un valore di Y più "basso" (cioè verso il fondo dello schermo) e poi quelli con un valore più alto.

Per abilitare questo, si entra nel "nodo root" della scena -che dev'essere ereditato da CanvasItem (tipo Node2D), si apre il menu "ordering", si controlla che lo Z Index sia 0 e poi si abilita "Y Sort Enabled".

Facendo questo, quando l'elemento (ad esempio, il protagonista) ha un valore Y più "basso" del secondo oggetto (ad esempio, un ostacolo), il sistema lo rappresenterà davanti. Se invece è più alto, il sistema lo disegnerà prima, e quindi apparirà "dietro" all'ostacolo.
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"
A questo punto, è tutto pronto.
Però... per poter partire, il timer dev'essere inserito in un nodo per poter funzionare!
$NodeInTree.add_child(self.timer)
A questo punto, si può collegare il signal "timeout" ad una funzione e poi far partire il timer, indicando il tempo entro cui raggiungere il timeout:
self.timer.timeout.connect(self._on_timeout)
self.timer.start(5)

func _on_timeout() -> void:
    pass

lunedì 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:

https://tui.ninja/lua/strings/formatting/

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
Aprite la seguente cartella (che contiene l'SDK di Android):
C:\Users\[UTENTE PC]\AppData\Local\Android\Sdk\build-tools
All'interno controllate se sono presenti versioni (cartelle) precedenti alla 34.
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

Sviluppando "Space Bricks", mi è capitato di dover "nascondere" parte della visualizzazione di un'animazione che "usciva dai bordi" (per la cronaca, quella dell'esplosione). Sono riuscito a farlo in Love2D, ma in Godot... ho dovuto sbatterci parecchio la testa.

Per risolvere il problema, bisogna inserire nella scena un controllo che abbia una posizione ed una dimensione.
La scelta è ricaduta sul nodo "ColorRect", che ha entrambi le voci di configurazione impostabili.

Nota: anche questo fa parte dei nodi "Control", non è un problema inserirlo come figlio di un "semplice" Node2D. L'editor mostrerà un avviso, ma il funzionamento sarà comunque corretto.

Una volta inserito il nodo ed impostato "Size" e "Position" (sotto Transform), questo nodo andrà utilizzato per l'inserimento degli elementi "da mascherare" (nel mio caso, è un inserimento dinamico).
Per abilitare la visualizzazione dei "nodi figli" in modo che utilizzi il ColorRect come maschera, bisognerà attivare (flag "On") i seguenti due parametri di configurazione:
  • Clip Content (sotto Layout)
  • Clip Children -da impostare a "Clip Only"- (sotto "Visibility")
La prima effettuerà il vero e proprio "clipping", mentre la seconda visualizzerà solo i nodi figli senza mostrare a video l'oggetto ColorRect (e cioè un pannello a tinta unita).

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".

giovedì 28 agosto 2025

WINDOWS - Variabili d'ambiente

 Mi è appena capitato di entrare in un server, cercare di aprire il pannello per la gestione delle variabili d'ambiente e ricevere un errore rundll32.exe:

Il sistema, però, funziona... quindi si può aprire direttamente il pannello lanciando questo comando da un prompt con permessi di amministratore:

"C:\Windows\system32\rundll32.exe" sysdm.cpl,EditEnvironmentVariables

Questo aprirà il pannello e, da lì, è possibile gestire le variabili di ambiente (come il PATH).

mercoledì 27 agosto 2025

LOVE2D - Pubblicazione sul web

Ho iniziato lo sviluppo di un videogame in lua/love2d e volevo ospitarlo su itch.io, che offre anche il sistema per "giocarlo online" direttamente sul browser.

La procedura per ottenere un file giocabile (usa love.js come estensione di node.js) non è propriamente semplice e richiede (su windows) un po' di lavoro.

Utilizzeremo "makelove" (il repository si trova qui: https://github.com/idbrii/love-makelove ).


1. Installazione dei requisiti necessari

Procedura:

  • installare python
  • inserire il percorso di installazione e la directory "Scripts" nel PATH
  • aprire un prompt (cmd.exe) come amministratore (sennò il sistema installa le librerie in locale)
  • lanciare il seguente comando: pip3 install makelove
  • assicurarsi che il tutto sia aggiornato, lanciando: python3 -m pip install --upgrade setuptools (i setuptools servono per la gestione dei packages)

2. Configurazione del "packet maker"

Questa è la procedura:
  • spostarsi nella directory in cui è presente il sorgente del gioco
  • lanciare il comando: makelove --init e compilare i dati richiesti

A questo punto, abbiamo un file di configurazione per la produzione dei "pacchetti".
Lovejs non è previsto di base, quindi bisogna andare ad editare il file makelove.toml.

Procediamo a modificare il file di configurazione, aprendo il file makelove.toml con un qualsiasi editor (anche VS Code).

Un esempio potrebbe essere questo:

name = "TITOLO_GIOCO"
love_version = "11.5"
default_targets = ["win64","lovejs"]
build_directory = "PUBLISH"

love_files = [
    "+*",
    "-*/.*",
    "-./PUBLISH/*",
]

[lovejs]
title = "TITOLO GIOCO"  # used on the resulting web page
memory = "20000000" # starting memory of the webpage (default is 20 MB)

Nota: memory dev'essere grande abbastanza da contenere l'intero "pacchetto del gioco" prodotto. Arrotondate per eccesso.

L'importante è includere il target "lovejs".

Ora si salva e si chiude.


3. Pacchetti

Passiamo alla produzione dei pacchetti. La procedura è:

  • aprire un prompt cmd
  • lanciare il comando: makelove --version-name 1

Nota: ho aggiunto il sistema di gestione della versione, per facilitare le varie build.

Ora abbiamo, nella cartella indicata nel conf (io ho messo PUBLISH), una cartella con la versione e con dentro i pacchetti generati.

Il pacchetto potrebbe essere completo, ma prima di pubblicare i dati conviene modificare il codice della pagina che caricherà il gioco nel browser.

Bisogna entrare nella cartella lovejs e decomprimere il file .zip contenuto qui dentro.

All'interno del file bisogna modificare il file index.html ed il file theme\love.css

Queste le modifiche al file index.html:

eliminare il tag <h1> dentro al <center> in alto

modificare le dimensioni iniziali del canvas "loadingCanvas" con le dimensioni del gioco

eliminare il <footer> con i credits (presente un overlay che interferisce con il gioco)

modificare la funzione js function drawLoadingText(text) in questo modo:

var canvas = loadingContext.canvas;

loadingContext.fillStyle = "rgb(0, 0, 0)";
loadingContext.fillRect(0, 0, canvas.scrollWidth, canvas.scrollHeight);

loadingContext.font = '1em helvetica';
loadingContext.textAlign = 'center'
loadingContext.fillStyle = "rgb( 200, 200, 200 )";
loadingContext.fillText(text, canvas.scrollWidth / 2, canvas.scrollHeight / 2);

Queste invece le modifiche al file love.css:

  • eliminare l'immagine di sfondo dal tag "body" (background-image)
  • cambiare il colore di sfondo e del testo con uno "consono" al gioco (ad esempio... nero e bianco)
  • cambiare i colori delle ancore (link, visited, hover), anche se non dovrebbero essercene

A questo punto, si sostituiscono i due file nello zip e si carica su itch.io.

martedì 26 agosto 2025

WINDOWS 11 - Refresh nelle cartelle

Da quando ho iniziato ad usare Windows 11, mi sono imbattuto in un problema abbastanza noioso: quando copio un file / creo una nuova directory / rinomino qualcosa, la modifica non appare. Devo premere F5 per vedere la modifica.

Per risolvere il problema, si deve eseguire questa procedura:

  1. si apre una cartella qualsiasi
  2. si clicca sui tre pallini orizzontali nel menu in alto (si trovano alla fine del menu, non totalmente a destra)
  3. si clicca su "Opzioni"
  4. si clicca sulla linguetta "Visualizzazione"
  5. nelle "Impostazioni avanzate:" (sotto), si scende fino a "Riquadro di spostamento
  6. si mette la spunta su "Espandi fino a cartella aperta"

Dopo aver salvato, il sistema fare il refresh automatico nelle cartelle (e nel desktop).

Ormai non mi chiedo più che c'entri quest'impostazione con il problema riscontrato, ma... è Windows. Solo chi l'ha programmato lo sa.

giovedì 7 agosto 2025

LUA+LOVE2D - Creare un eseguibile del gioco

Lua (in combinazione con Love2d) è un linguaggio di programmazione che permette di creare "facilmente" dei videogame.

Essendo un linguaggio "scriptato", però, non è possibile compilare un eseguibile.

E' possibile, però, creare un eseguibile in pochi, semplici passaggi:

  1. zippare tutti i file del programma, assicurandosi che il file main.lua sia nella root dello zip
  2. aprire un prompt di CMD nella directory in cui è presente lo .zip (CMD, non PowerShell!)
  3. lanciare questo comando:
copy /b "C:\Program Files\lua\LOVE\love.exe"+AirFighters.zip AirFighters.exe

In questo modo, verrà creato un file .exe che permetterà di lanciare il gioco.

Per poter "distribuire" il gioco, però, non basta l'eseguibile. Dovrete copiare anche tutte le .dll contenute nella directory di love.exe .

Per la modifica dell'icona, è necessario invece utilizzare uno strumento esterno.

lunedì 4 agosto 2025

SQL SERVER - ricerca di % tramite LIKE

In SQL Server, il carattere '%' si utilizza come wildcard per le ricerche. E se volessi cercare proprio quel carattere?

La soluzione è metterlo "tra quadre":

...
WHERE campo LIKE '%testo [%]%'
In questo modo, il sistema lo riconoscerà come "carattere" e la ricerca funzionerà correttamente.

lunedì 28 luglio 2025

SQL Server - aggiornamento password utenza

 A volte, la modifica ad un'utenza non può andare a buon fine, e compare un errore che fa riferimento a "MUST_CHANGE set to ON".

Per risolvere il problema, basta usare uno script:

USE Master
GO
ALTER LOGIN utenteDaVariare WITH PASSWORD = 'password123'
GO
ALTER LOGIN utenteDaVariare WITH
      CHECK_POLICY = OFF,
      CHECK_EXPIRATION = OFF;

lunedì 23 giugno 2025

LINUX - reload dei mount caricati in /etc/fstab

 Invece di riavviare il PC/server per caricare le nuove modifiche a /etc/fstab (o in caso qualcosa si sia incastrato), basta lanciare il seguente comando:

# mount -a

Questo comando ricarica tutti i mount presenti.

mercoledì 23 aprile 2025

POWERSHELL - Eseguire uno script non firmato con le policy

In windows 11, gli script di powershell sono "bloccati" se non hanno una firma digitale.

E' possibile bypassare questo blocco impostando una policy meno restrittiva per l'esecuzione dei comandi.

Per prima cosa, vediamo quali sono le policy impostate:

Get-ExecutionPolicy -List

Se non c'è una ExecutionPolicy impostata, verrà indicato Undefined.

Per permettere l'esecuzione, bisogna cambiare la policy per gli scope "CurrentUser" o "LocalMachine".

Per impostare una policy, si utilizza questo comando:

Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser

Nota: questo serve solo ad eseguire "temporaneamente" uno script senza la necessità di firmarlo. Una volta conclusa l'attività, conviene reimpostare la policy a "Undefined" per mantenere integra la sicurezza.

martedì 1 aprile 2025

WINDOWS - Accesso ad un singolo PC per un utente di dominio

 E' possibile limitare l'accesso ad una singola postazione (o ad un numero limitato di postazioni) per un utente di dominio.

Bisogna accedere al server che gestisce l'Active Directory, aprire le proprietà dell'utente, andare nella TAB "Account", cliccare su "Log On To..." e quindi indicare il nome della macchina (o delle macchine) a cui garantire l'accesso.

lunedì 17 marzo 2025

WINDOWS - Link ad un RDP specifico

E' possibile generare un link ad un RDP (Remote Desktop Protocol o Connessione al Desktop Remoto) che punta ad una postazione specifica.

Per farlo, si crea un link con "crea collegamento" e quindi si inserisce questo comando:

mstsc.exe /v:PC-name

Nota: bisogna sostituire "PC-name" con il nome del PC (ma va bene pure l'IP).

giovedì 6 febbraio 2025

SQL SERVER - Controllare se un indice esiste

Una delle attività quotidiane è il ripristino di un DB da un backup remoto che mi permette di fare statistiche senza passare per l'interfaccia del fornitore.
Ovviamente questo ha i suoi pro ed i suoi contro, ma il problema più grosso è che, nel passaggio, alcune strutture "si perdono" o non sono proprio presenti.
Una di queste è una lista di indici utili a velocizzare le estrazioni.

Ho quindi realizzato uno script che viene lanciato subito dopo il ripristino del backup. Il problema è che, a parte dei log non del tutto "puliti", non ho modo di sapere se l'effetto è quello voluto o meno.
E dato che lo script fa anche altro, non sono certo che tutti i comandi vengano eseguiti.

Per cui ho cercato un modo per controllare l'esistenza di un indice nel DB: se l'indice è presente, lo script ha funzionato.

Questo è il codice "generico" per il controllo della presenza di un indice:


use DATABASE_NAME;

if exists (
    select top (1) object_id as index_exists
    from sys.indexes
    where name = N'index_name'
        and object_id = object_id(N'schema_name.table_name')
)
    begin
        print 'Found index "index_name" on "schema_name.table_name".';
    end;
else
    begin
        print 'Did not find index "index_name" on "schema_name.table_name".';
    end;
basta cambiare index_name, schema_name (di solito è dbo) e table_name per effettuare il controllo specifico.

giovedì 30 gennaio 2025

ANDROID STUDIO - Ridurre la RAM dell'emulatore

Utilizzare Android Studio su un computer con Windows 11 e 8 giga di RAM è possibile, anche se bisogna scendere a qualche compromesso.

Contesto
Partiamo del contesto: mantenendo aperti solo alcuni programmi (pochi), la RAM utilizzata si aggira attorno al 56% del totale.
Android Studio "mangia" da solo circa 1,5 GB di RAM ed avviare un qualsiasi "device virtualizzato" di fatto ammazza il PC (a parte rari casi...), che praticamente smette di rispondere ai comandi finché non si riesce (a fatica) a chiudere l'AVD e poi Android Studio.

Soluzione
C'è però una soluzione di compromesso: abbassare la RAM utilizzata dall'AVD.

Di base, questo valore non è modificabile dall'interfaccia, ma si può modificare dentro i file di configurazione del dispositivo virtualizzato.

Per prima cosa, creiamo un nuovo device virtualizzato con le caratteristiche che vogliamo. Nel mio caso, ho simulato il mio cellulare personale con Android 13 (quindi Android SDK Platform 33).

A questo punto, bisogna cliccare sul burger menu, quindi su Tools e poi su Device Manager. Si aprirà un riquadro a destra.
Bisogna cliccare sui tre pallini accanto al nuovo device virtualizzato e quindi su Show on disk.
Una volta aperta la directory con i file del device, bisogna:

  1. cancellare (se presente) la cartella hardware-qemu.ini.lock
  2. aprire il file hardware-qemu.ini, trovare la riga hw.ramSize e cambiare il valore (io ho impostato 2048)
  3. aprire il file config.ini, trovare la riga hw.ramSize e cambiare il valore (io ho impostato 2048)

A questo punto, dopo aver riavviato Android Studio per caricare le modifiche, controllando i dettagli del device virtualizzato (identificate il device sotto Device Manager, cliccate sui tre pallini e selezionate View Details) dovreste trovare il nuovo quantitativo di RAM che avete impostato.

La macchina quindi si avvierà con la RAM specificata, che così non andrà ad appesantire il sistema operativo. Ovvio che risulterà un po' inchiodata... però almeno il sistema continuerà a rispondere ai comandi!

mercoledì 29 gennaio 2025

JAVASCRIPT - Replace di tutte le istanze trovate

Con javascript o jquery capita a volte di dover utilizzare la funzione .replace() per cambiare il contenuto di qualche stringa.
Il problema è che il funzionamento "base" prevede la modifica solo della prima "istanza" trovata, ed il resto viene lasciato inalterato.

Per eseguire il replace su tutte le istanze presenti nella stringa, bisogna utilizzare un'espressione regolare:
const stringToEdit = 'tom;jack;jones';
const newString = stringToEdit.replace(/;/g, ' &nbsp;|&nbsp; ');

giovedì 23 gennaio 2025

SQL SERVER - arrotondare i DATETIME

In SQL Server, per arrotondare i DATETIME si utilizzano le funzioni DATEADD e DATEDIFF, in pratica si sottrae il tipo di valore che si vuole arrotondare a 0 (minuti, secondi, ore, etc).

Questo è un esempio:

DECLARE @dt DATETIME

SET @dt = '09-22-2007 15:07:38.850'

SELECT DATEADD(mi, DATEDIFF(mi, 0, @dt), 0)     -- ritorna 2007-09-22 15:07:00.000
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, @dt), 0) -- ritorna 2007-09-22 15:00:00.000

mercoledì 22 gennaio 2025

PYTHON - Uscire da 2 o più loop annidati

Facendo esercizi di programmazione (soprattutto Advent of Code e CodeWars) mi sono imbattuto in alcuni casi in cui, trovato un valore -ad esempio in una matrice- sarei dovuto uscire da due loop annidati.

Guardando in rete ci sono parecchi metodi per effettuare questa operazione (uso di try/catch, refactoring della funzione in modo che a quel punto esca dalla stessa, etc), però quello che mi è parso più sensato è l'utilizzo di una variabile di controllo.

Prendiamo ad esempio la ricerca della prima coppia di numeri contenuti in una lista che sono perfettamente divisibili (quindi senza resto) tra loro, con lo scopo di trovare il risultato di questa divisione.

Il codice è questo:

result = -1
el = [9, 4, 5, 7, 12, 3]

# variabile di controllo
found = False

for i in range(len(el)):
    for j in range(i + 1, len(el)):
        if el[i] % el[j] == 0:
            result = el[i] // el[j]
            found = True
            break
    if found:
        break

print(result)

Utilizziamo la variabile found per indicare che il valore è stato trovato e mettiamo dentro ogni ciclo un controllo seguito da un break.
In questo modo, una volta trovato il risultato siamo sicuri di uscire da entrambi i cicli.

PYTHON - Split senza elementi vuoti

Ci sono dei casi in cui usare la funzione .split() può generare dei valori "vuoti" all'interno della lista prodotta.

In alcuni casi, questo è utile e quell'elemento va mantenuto. Potrebbe però esserci la necessità di processare solo i valori presenti in una stringa e magari in questa stringa ci sono degli "spazi di troppo"... lo split (che non utilizza espressioni regolari) non riesce a distinguere un singolo carattere " " (uno spazio)  da un "blocco di spazi".

Come si tolgono, quindi, gli elementi vuoti? Con la funzione filter.

Funzionamento di base
La funzione applica ad ogni elemento "iterable" una funzione passata, usando questo formato:

f = filter(lambda x: x > 18, userAgeList)

Rimozione delle stringhe vuote
Se però noi al posto della funzione passiamo None, la funzione rimuove tutti i valori che sono considerati "nulli" da Python (per cui la stringa vuota, gli zeri, il valore None):

f = filter(None, userAgeList)

Se invece abbiamo una lista "mista" e vogliamo togliere esclusivamente i valori "" (e non 0 o None), bisogna specificare una funzione precisa:

f = filter(lambda x: x != '', s)

martedì 21 gennaio 2025

WINDOWS - Spegnere il PC da riga di comando

Spegnere un PC Windows da riga di comando potrebbe tornare utile quando si è connessi e si sta lavorando in desktop remoto (anche perché l'opzione non è presente nel menu start).

Il comando è:

shutdown /s /t 0

venerdì 17 gennaio 2025

SQL SERVER - Più valori in un singolo campo con STUFF

 A volte è necessario recuperare più di un valore associato ad un record, ma senza produrre più record "duplicati".

Ad esempio, vorremmo ottenere l'elenco degli utenti con i gruppi ad esso associati o un elenco di utenti (per nome) con tutti gli account associati.

Nel primo caso, potremmo avere questa lista di utenti nella tabella "sys_utenti":

utente
----------------------------------------
m.rossi
a.bianchi
p.verdi

Con questi permessi nella tabella "sys_gruppiUtente":

utente     | gruppo
----------------------------------------
m.rossi    | admin
m.rossi    | ufficioIT
a.bianchi  | ufficioAmministrazione
p.verdi    | segreteria
p.verdi    | ufficioVendite

E vorremmo ottenere:

utente     | listaGruppi
----------------------------------------
m.rossi    | admin;ufficioIT
a.bianchi  | ufficioAmministrazione
p.verdi    | segreteria;ufficioVendite

Questo risultato si può ottenere utilizzando la funzione STUFF:

SELECT DISTINCT
        sUte.utente,
        ISNULL(STUFF(( SELECT DISTINCT ';' + sUtGp.gruppo
                       FROM     sys_gruppiUtente AS sUtGp
                       WHERE    sUtGp.utente = sUte.utente
                     FOR
                       XML PATH('')
                     ), 1, 1, ''), '') AS gruppi
FROM    sys_utenti AS sUte
ORDER BY sUte.utente

La query è leggermente complessa, utilizza una subquery per costruire i valori da restituire che, grazie alla mappatura in XML, produce una tabella ridotta e poi la "impacchetta" con STUFF.

La costruzione della tabella ridotta utilizza la mappatura XML: grazie la clausola FOR XML  -che trasforma la "tabella dei dati" in una struttura XML- e poi specifica PATH('') -che indica a SQL di non usare nessun elemento per racchiudere i dati-.

La funzione STUFF, invece, consente di inserire una stringa in un'altra stringa eliminando un numero di caratteri specificato nella posizione iniziale della prima stringa e inserendo la seconda stringa in tale posizione.

I parametri sono:

  • la tabella dei dati di origine
  • la posizione da cui iniziare l'elaborazione
  • il numero di caratteri da sostituire
  • la stringa con cui fare la sostituzione

Nel nostro caso, abbiamo questi valori:

  • la tabella "mappata" in xml
  • la posizione iniziale della stringa (in SQL, si parte da 1 e non da 0!)
  • il numero dei caratteri nella stringa di prefisso (';' è un solo carattere)
  • una stringa vuota con cui fare la sostituzione (in questo modo, si fa effettivamente l'eliminazione)

Questo è un sistema flessibile e potente per ricostruire alcune informazioni utili.

Nota:
è importante notare l'inserimento di una stringa di X caratteri per separare i valori, che verrà successivamente rimossa da STUFF per il primo valore.

In pratica, da una stringa ;valoreA;valoreB otterremo valoreA;valoreB.

Nota:
il collegamento tra la subquery usata nella SELECT e la query principale è dato dal WHERE, che collega un campo della subquery ad un campo della query principale.
Questo è uno dei pochi casi (se non l'unico, forse) in cui si possono usare riferimenti esterni allo scope della query in oggetto (nel nostro caso, la subquery).

giovedì 9 gennaio 2025

PYTHON - Utilizzo di numeri complessi per la gestione di "mappe"

Analizzando le soluzioni di altri sviluppatori per risolvere i problemi dell'Advent of Code del 2024, ho trovato una soluzione semplice ed elegante per gestire le mappe.

La soluzione canonica per la gestione di una "mappa" (cioè una tabella fatta di righe e colonne) è utilizzare un dizionario con una tupla contenente le coordinate come chiave ed il dato caricato dentro come valore.

Utilizzando poi una struttura del genere, c'è bisogno di elaborare un sistema per recuperare e gestire le coordinate "x" e "y" presenti nella tupla, soprattutto nel caso dobbiamo "muoverci" all'interno della mappa o processare elementi attigui.

Ad esempio, se siamo nella casella (0, 0) e vogliamo spostarci in basso e a destra di una riga finendo in (1, 1), dobbiamo effettuare un calcolo che mappi i dati e faccia le somme:

a = (0, 0)
dest = tuple(map(sum, zip(a, (1, 1))))

Una soluzione che facilita questo genere di calcoli utilizza i numeri complessi, cioè i numeri che combinano una parte reale ed una parte immaginaria.

Dato che il dato è composto da due parti, è possibile usare il valore reale per definire la coordinata della riga ed il valore immaginario per definire la coordinata della colonna.

In questo modo, per effettuare la stessa operazione di prima basta sommare un valore "numerico" al dato:

a = complex(0, 0)
dest = a + 1 + 1j

PYTHON - Il costrutto WITH per leggere un file e processarlo

Per leggere il contenuto di un file, si può utilizzare i comandi l'apertura e la chiusura di un file e, in mezzo, il codice per leggere e processare il contenuto... oppure si sfrutta lo statement with.

Questo costrutto rimpiazza un blocco try-catch ed assicura che le risorse invocate vengano chiuse dopo il loro utilizzo.
Nel caso di un'attività su un file, si assicura che il suddetto file venga chiuso.

Questo è un esempio di utilizzo:

fileInput = "test.txt"
rows = []

with open(fileInput, "r") as file:
    for r in file.readlines():
        rows.append(r.strip())