
Hai mai pensato di inserire un'**intelligenza artificiale** conversazionale come Gemini direttamente nel tuo progetto **Construct 3?** Un recente video mi ha ispirato a rispolverare questa idea e a creare una guida aggiornata.
Avevo già esplorato l'integrazione dell'IA in passato, ma le tecnologie si evolvono rapidamente. Gli strumenti di un tempo sono obsoleti, superati oggi da modelli potenti come Gemini. Oggi, l'attenzione è tutta sui ChatBot come ChatGPT e **Gemini**, strumenti potentissimi nonostante i loro limiti. L'idea di integrare questi modelli in Construct 3, usando JavaScript per la logica e il canvas per la grafica, apre possibilità creative affascinanti per giochi e applicazioni interattive.
In questa guida passo-passo, imparerai a collegare il tuo progetto a Gemini, inviare richieste e gestire le risposte in un formato pronto per essere utilizzato.
### Il tuo pass per Gemini: Ottenere l'API Key

Per iniziare, la prima cosa che ti serve è una **API Key**. Si tratta di un codice segreto che identifica noi e la nostra applicazione. E la parte migliore? Per iniziare a sperimentare non dovrai spendere un centesimo. Google, infatti, offre API Key gratuite con limiti di utilizzo talmente ampi da coprire senza problemi tutta la fase di sviluppo e test.
Ottenere la chiave è semplice:
1. Vai sul sito di [**Google AI Studio**](https://aistudio.google.com).
2. Clicca sul pulsante **"Get API Key"**.
3. Nella schermata successiva, clicca su **"Create API Key"**.
Verrà generata una chiave alfanumerica. **Trattala come una password**: non condividerla e non inserirla mai direttamente in un codice visibile al pubblico.
### Impostazione del Progetto

Avendo scelto di usare Construct 3, la nostra applicazione girerà su browser, quindi la scelta del linguaggio ricade naturalmente su **JavaScript** e **TypeScript**.
Per questo primo esperimento, l'interfaccia grafica sarà minimale. Ci bastano due elementi:
- Una **casella di testo** per inserire la domanda (il "prompt").
- Uno **spazio** in cui visualizzare la risposta di Gemini.
### La Struttura del Codice

Per tenere il progetto ordinato, useremo tre script:
- `Gemini.ts`: Il cuore del nostro sistema. Conterrà il codice per interrogare Gemini e ricevere le risposte.
- `importsForEvents.ts`: Un file di supporto che permette a Construct di "vedere" e utilizzare le funzioni definite in `Gemini.ts`.
- `main.ts`: Il file principale eseguito da Construct, che useremo per inizializzare alcune variabili globali.
I file `importsForEvents.ts` e `main.ts` sono molto semplici.
**`importsForEvents.ts`** contiene una sola riga per importare le nostre funzioni:
```ts
import * as Gemini from "./Gemini.js";
```
**`main.ts`** ci serve principalmente per rendere la variabile `runtime` di Construct facilmente accessibile in tutto il progetto:
```ts
declare global {
var runtime: IRuntime;
}
runOnStartup(async (runtime: IRuntime) => {
globalThis.runtime = runtime;
})
```
### Primo Contatto con Gemini: Una Richiesta senza Memoria
È il momento di scrivere il codice che conta, quello nel file `Gemini.ts`. Per comunicare con Gemini, come indicato [nella documentazione ufficiale](https://ai.google.dev/api/generate-content#method:-models.generatecontent) di Google, dobbiamo inviare una richiesta `POST` a un endpoint specifico.
Il processo si articola in quattro passaggi fondamentali::
1. **Costruire l'URL** per la richiesta, includendo il modello che vogliamo usare (es. `gemini-1.5-flash`) e la nostra API Key.
2. **Impacchettare la domanda nel `payload`**: si tratta di creare un oggetto JSON standardizzato che contenga il testo della nostra richiesta.
3. **Eseguire la richiesta** usando la `Fetch API` di JavaScript.
4. **Interpretare la risposta** JSON per estrarre il testo generato da Gemini.
Combinando questi passaggi, possiamo scrivere una funzione `ask` pulita e riutilizzabile che gestisce l'intera comunicazione:
```ts
const modelGemini = "gemini-2.5-flash"
export async function ask (obj :{key: string, question: string, runtime: IRuntime}):Promise<string> {
const {key, question, runtime} = {...obj};
const url = `https://generativelanguage.googleapis.com/v1/models/${modelGemini}:generateContent?key=${key}`;
// const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelGemini}:generateContent?key=${key}`;
const payload = {
contents: [{
parts: [{
text: question
}]
}]
};
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`Errore dal server: ${response.status}`);
}
const data = await response.json();
const answer = data.candidates[0].content.parts[0].text;
return answer;
} catch (error) {
console.error("Dettaglio errore:", error);
return `Si è verificato un errore. (${(error as Error).message})`;
}
}
```
Ora passiamo alla pratica. Per collegare la nostra funzione `ask` all'interfaccia di Construct, la richiamiamo dall'event sheet. L'ideale è associarla a un'azione dell'utente, come la pressione del tasto 'Invio' dopo aver digitato la domanda:

```ts
// Codice TypeScript in un Event Sheet di Construct 3
const key = runtime.globalVars.API_KEY;
const question = runtime?.objects?.Question?.getFirstInstance()?.text || "";
const answer = question.trim().length > 0 ? await Gemini.ask({key, question, runtime}) : "Missing Question!";
const textAnswer = runtime.objects.Answer.getFirstInstance();
if (textAnswer) { textAnswer.text = answer};
```
### Dare una Memoria all'IA: Dalla Domanda Singola alla Conversazione Reale
La nostra chat funziona, ma ha un grosso limite: **è smemorata**. Ogni domanda è un'interazione a sé stante, come se l'IA resettasse la sua conoscenza a ogni invio. Impossibile, in questo modo, costruire un dialogo coerente. Per trasformarla in una conversazione reale, dobbiamo darle una memoria.
Il trucco, [come spiegato nella documentazione](https://ai.google.dev/gemini-api/docs/text-generation#multi-turn-conversations), è sorprendentemente semplice: dobbiamo inviare, a ogni richiesta, **l'intera cronologia della conversazione**, specificando per ogni messaggio il ruolo (`role`) di chi ha parlato: `user` per noi e `model` per Gemini. È un po' come dare a qualcuno un riassunto di tutto ciò che vi siete detti prima di porre una nuova domanda, assicurandosi che abbia sempre il quadro completo. Invece di trattare ogni domanda come un nuovo inizio, forniamo a Gemini l'intera trascrizione della conversazione avvenuta fino a quel momento.
#### Mettiamo le Mani sul Codice
Per prima cosa, modifichiamo `main.ts` per creare e inizializzare una variabile globale che conterrà la cronologia. Approfittiamo di questo passaggio per inserire un "prompt di sistema": un'istruzione iniziale che definisce la personalità e il comportamento della nostra IA sin dal principio, guidando il tono di tutte le sue risposte future.
```ts
declare global {
var runtime: IRuntime;
var chatHistory: {role: "user"|"model", parts: [ q: {text: string}] }[];
}
runOnStartup(async (runtime: IRuntime) => {
globalThis.runtime = runtime;
globalThis.chatHistory = [
{
role: "user",
parts: [{
text: "You're a helpful and precise virtual assistant. Your responses are clear and concise."
}]
},
{
role: "model",
parts:[{
text: "Instructions received. I'm ready."
}]
}
];
})
```
Per implementare la memoria, facciamo evolvere la nostra funzione `ask` in una nuova, più potente, chiamata `chat`. Questa non si limiterà a inviare una singola domanda, ma gestirà l'intera cronologia della conversazione.
Il flusso di lavoro si arricchisce di alcuni passaggi chiave:
1. Prima di inviare la richiesta, aggiungiamo la domanda dell'utente (`user`) alla cronologia (`chatHistory`).
2. Il `payload` della richiesta non conterrà più una singola domanda, ma l'intero array `chatHistory`.
3. Dopo aver ricevuto una risposta valida, la aggiungiamo alla cronologia con il ruolo `model`.
4. In caso di errore, rimuoviamo l'ultima domanda dell'utente dalla cronologia per mantenerla consistente.
Vediamo come questi passaggi si traducono nella funzione `chat` completa:
```ts
export async function chat (obj :{key: string, question: string, chatHistory: {role: string, parts: [ q: {text: string}] }[], runtime: IRuntime}):Promise<string> {
const {key, question, chatHistory, runtime} = {...obj};
const url = `https://generativelanguage.googleapis.com/v1/models/${modelGemini}:generateContent?key=${key}`;
// const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelGemini}:generateContent?key=${key}`;
// Aggiungi la domanda dell'utente alla cronologia
chatHistory.push({
role: "user",
parts: [{ text: question }]
});
const payload = {
contents: chatHistory
};
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok)
// Se c'è un errore, rimuovi la domanda che non ha avuto risposta
chatHistory.pop();
throw new Error(`Errore dal server: ${response.status}`);
}
const data = await response.json();
const answer = data.candidates[0].content.parts[0].text;
// Aggiungi la risposta del modello alla cronologia
chatHistory.push({
role: "model",
parts: [{ text: answer }]
});
return answer;
} catch (error) {
console.error("Dettaglio errore:", error);
return `Si è verificato un errore. (${(error as Error).message})`;
}
}
```
L'ultimo passo è aggiornare l'event sheet di Construct 3: basterà sostituire la chiamata a `Gemini.ask` con la nostra nuova funzione `Gemini.chat`. Fatto questo, il nostro sistema è completo!
```ts
const answer = question.trim().length > 0 ? await Gemini.chat({key, question, chatHistory, runtime}) : "Missing Question!";
```
### Da Chatbot a Compagno di Gioco: I Prossimi Passi
Congratulazioni! Hai appena sbloccato una funzionalità potentissima: una vera e propria chat conversazionale con memoria, basata su Gemini, è ora integrata nel tuo progetto Construct 3.
Immagina di poter usare l'IA non solo per chiacchierare, ma per generare dinamicamente l'inventario di un personaggio, creare dialoghi per PNG che reagiscono agli eventi del gioco, o persino costruire interi livelli in tempo reale. La chiave per tutto questo è l['output strutturato](https://ai.google.dev/gemini-api/docs/structured-output)... Esploreremo come implementare questa tecnica avanzata nel prossimo articolo. Resta sintonizzato!