Game Changers- from pixel to cloud

WorldandAratrika
Google Cloud - Community
12 min readAug 4, 2024

Crafting Tomorrow’s Games with Google Gemini and Google Cloud

Image source: https://www.orangemantra.com/

Google Cloud, a one stop solution to everything. With its robust technologies and flexibility on every platform, it makes itself a highly preferred choice for software developers and engineers all over the world.

In this blog, I am going to share how I managed to successfully create a mystery game bot by solely leveraging some of the functions offered by Google Cloud.

Can AI Help You Create Interactive Gaming Experiences?

As the blog suggests, yes, we can! Generative AI and natural language processing can be effectively used to your game ideas to life and provide an engaging experience for players. In this guide, you’ll learn how to integrate Google’s AI APIs into your JavaScript project, allowing the AI to dynamically interact with players and generate game content.

When you find the PERFECT platform for everything (Google Cloud in my case)

Introduction:

Have you ever thought what you can achieve with GenAI? For instance, you can achieve how to make a GenAI game from this very blog you are reading!

According to me, traditional game development methods can be time-consuming and often lack the flexibility to adapt to player inputs in real-time. The unpredictability of AI generated responses always keep the user on their toes.

By using Google’s Generative AI, you can create a game that dynamically responds to player inputs. The AI can generate customised game scenarios, plot twists, and challenges based on the player’s choices, making the game experience more engaging and interactive. From generating intricate game plots to creating unique characters and objectives, leaving everything to AI is enigmatic and exciting for the users.

TARGET AUDIENCE:

This game is meant for people in age groups more than 12 years who want to test their brains and general knowledge in any subject by solving a unique mystery according to the difficulty levels they choose. The best part of the game is that it can generate games according to the subject field you want. History? Geography? Culture? Even Maths? This little chatbot can generate a mystery based on any subject. The unexpected plot twists and clever challenges will keep the user wanting more. Moreover both experienced developers looking to enhance their games with AI capabilities can further build this into a dynamic project by integrating scenarios and advanced gaming design. Is the game fun? Yes. Is the game educational? Definitely, yes.

OUTCOME:

By following this guide, you will be able to integrate Google Cloud’s Generative AI into your game, build an interactive chatbot and deploy your web-app on Google Cloud.

Design:

  • Frontend: A responsive web application built with React to ensure a seamless user experience across devices.
  • Backend: A server-less architecture using Google Cloud Functions to handle real-time content generation, user interactions, and game state management.
  • Database: Google Cloud Firestore for storing user data, game states, and educational content, offering real-time synchronisation and scalability.
  • AI and NLP Services: Google Cloud AI and Natural Language API to power content generation and user interaction analysis.

Prerequisites:

Before diving into deploying your application, make sure you have the following:

Software and Tools:

  • Code Editor/IDE: Visual Studio Code (recommended for its extensive features and extensions, and I have used it.)
  • Node.js: Download Node.js (includes npm for managing dependencies)
  • Google Cloud SDK: Install Google Cloud SDK (necessary for deploying to Google Cloud)
  • Git: Download Git (for version control and deployment)
  • Create a Google Cloud project. Enable the required APIs and set up firebase in your Cloud console.

Languages and Frameworks:

  • JavaScript: Familiarity with JS is essential as they are used for coding the application.
  • React.js: Knowledge of React.js

APIs and Services:

  • Google Generative AI API
  • Google Natural Language API
  • Firebase and Firestore

The Reality Check, Let me Take you through the Process:

1. Setting Up Your Development Environment

Open your terminal and create a new directory for your project. Use npx command in your directory to create your react app.

npx create-react-app

2. Set Up Firebase Authentication

Since you want to store user data for further development on this idea, use firebase authentication and firestore. To integrate Firebase in your app:

In the Firebase Console, go to Project Settings. Register your app by enter your app’s nickname. Register the app. In your project directory install firebase.

npm install firebase

3. Create Google Cloud Functions and Frontend in your React app

You will need cloud functions to set up authentication policies and store user data. You will also need to create registration and login pages as the frontend of your app. You might need to create some components in your project that will contain the frontend designs and animations. You can follow my example or create your own!

For Registration Page:

import React, { useState } from "react";
import { createUserWithEmailAndPassword } from "firebase/auth";
import { doc, setDoc } from "firebase/firestore";
import { auth, db } from "../firebase";
import { useNavigate } from 'react-router-dom';
import ShootingStars from "./ui/shootingstars";
import StarsBackground from "./ui/stars-background";
import { Label } from "./ui/label";
import { Input } from "./ui/input";

function Register() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [error, setError] = useState("");
const navigate = useNavigate();

const handleRegister = async (e) => {
e.preventDefault();
try {
// Create a new user with email and password
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
const user = userCredential.user;

// Save user profile to Firestore
await setDoc(doc(db, "users", user.uid), {
name: name,
email: email,
});

return (
<div className="auth-container">
<ShootingStars/>
<StarsBackground/>
<form className="one z-10 flex flex-col gap-5" onSubmit={handleRegister}>
<div className="input-container z-10 flex flex-col gap-4 space-y-2 md:space-y-0 md:space-x-2 mb-4">
<Label className="text-white">Name</Label>
<Input className="input"
type="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div className="input-container z-10 flex flex-col gap-4 space-y-2 md:space-y-0 md:space-x-2 mb-4">
<Label className="text-white">Email</Label>
<Input className="input"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div className="input-container z-10 flex flex-col gap-4 space-y-2 md:space-y-0 md:space-x-2 mb-4">
<Label className="text-white">Password</Label>
<Input className="input"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && <p className="error-message text-white mt-10">{error}</p>}
<button className="bg-gradient-to-br relative group/btn from-black dark:from-zinc-900 dark:to-zinc-900 to-neutral-600 block dark:bg-zinc-800 w-full text-white rounded-md h-10 font-medium shadow-[0px_1px_0px_0px_#ffffff40_inset,0px_-1px_0px_0px_#ffffff40_inset] dark:shadow-[0px_1px_0px_0px_var(--zinc-800)_inset,0px_-1px_0px_0px_var(--zinc-800)_inset]" type="submit">Register <BottomGradient /></button>
</form>
</div>
);
}

export default Register;

const BottomGradient = () => {
return (
<>
<span className="group-hover/btn:opacity-100 block transition duration-500 opacity-0 absolute h-px w-full -bottom-px inset-x-0 bg-gradient-to-r from-transparent via-cyan-500 to-transparent" />
<span className="group-hover/btn:opacity-100 blur-sm block transition duration-500 opacity-0 absolute h-px w-1/2 mx-auto -bottom-px inset-x-10 bg-gradient-to-r from-transparent via-indigo-500 to-transparent" />
</>
);
};

For Login Page:

import React, { useState } from "react";
import { signInWithEmailAndPassword } from "firebase/auth";
import { doc, getDoc } from "firebase/firestore";
import { auth, db } from "../firebase";
import { useNavigate } from "react-router-dom";
import ShootingStars from "./ui/shootingstars";
import StarsBackground from "./ui/stars-background";
import { Label } from "./ui/label";
import { Input } from "./ui/input";

function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [userData, setUserData] = useState(null);
const navigate = useNavigate();

const handleLogin = async (e) => {
e.preventDefault();
try {
// Sign in the user
const userCredential = await signInWithEmailAndPassword(auth, email, password);
const user = userCredential.user;

// Retrieve user profile from Firestore
const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);

if (docSnap.exists()) {
setUserData(docSnap.data());
console.log("User data:", docSnap.data());
console.log("Navigating to /game");
navigate('/game'); // Navigate to the game selection page
} else {
console.log("No such document!");
}
} catch (err) {
console.error("Error logging in:", err.message);
setError(err.message);
}
};

return (
<div className="auth-container">
<ShootingStars/>
<StarsBackground/>
<form className="one z-10 flex flex-col gap-5"onSubmit={handleLogin}>
<div className="input-container z-10 flex flex-col gap-4 space-y-2 md:space-y-0 md:space-x-2 mb-4">
<Label className="text-white">Email</Label>
<Input
className="input"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div className="input-container z-10 flex flex-col gap-4 space-y-2 md:space-y-0 md:space-x-2 mb-4">
<Label className="text-white">Password</Label>
<Input
className="input"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && <p className="error-message text-white mt-10">{error}</p>}
<button className="bg-gradient-to-br relative group/btn from-black dark:from-zinc-900 dark:to-zinc-900 to-neutral-600 block dark:bg-zinc-800 w-full text-white rounded-md h-10 font-medium shadow-[0px_1px_0px_0px_#ffffff40_inset,0px_-1px_0px_0px_#ffffff40_inset] dark:shadow-[0px_1px_0px_0px_var(--zinc-800)_inset,0px_-1px_0px_0px_var(--zinc-800)_inset]" type="submit">Login <BottomGradient /></button>
</form>
{userData && (
<div className="user-profile">
<h3>Welcome, {userData.name}!</h3>
<p>Email: {userData.email}</p>
{/* Add more user data here as needed */}
</div>
)}
</div>
);
}

export default Login;


const BottomGradient = () => {
return (
<>
<span className="group-hover/btn:opacity-100 block transition duration-500 opacity-0 absolute h-px w-full -bottom-px inset-x-0 bg-gradient-to-r from-transparent via-cyan-500 to-transparent" />
<span className="group-hover/btn:opacity-100 blur-sm block transition duration-500 opacity-0 absolute h-px w-1/2 mx-auto -bottom-px inset-x-10 bg-gradient-to-r from-transparent via-indigo-500 to-transparent" />
</>
);
};

Notice the use of firestore in the functions Login() and Register().

4. The main tea: Integrating GenAI

Install the Google Generative AI package in your project directory. Get an API key from https://ai.google.dev/aistudio by selecting the project you are choosing for your application.

npm install @google/generative-ai

Create a JavaScript file for generating AI responses from user inputs and integrate it to your frontend. You can do this directly in the file for AI responses or create a separate file for it and call the function. Don’t forget to store your API key securely.

import {  useState } from "react";
import { GoogleGenerativeAI } from "@google/generative-ai";
import { EvervaultCard} from "./ui/evervault-card";
import { BackgroundGradient } from "./ui/background-gradient";
import ShootingStars from "./ui/shootingstars";
import StarsBackground from "./ui/stars-background";

function GamedevAI() {
const [loading, setLoading] = useState(false);
const [apiData, setApiData] = useState([]);
const genAI = new GoogleGenerativeAI(
"YOUR API KEY"
);
const [messages, setMessages] = useState([
{ sender: "bot", text: "Welcome to the Mystery Game! Choose your subject and difficulty to start. You can select any subject that comes to your mind, yk!" },
]);
const [input, setInput] = useState("");
const [gameState, setGameState] = useState({ subject: null, difficulty: null });

// Handle input change
const handleInputChange = (e) => {
setInput(e.target.value);
};

// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim()) return;

// Add user message to chat history
const userMessage = { sender: "user", text: input };
const updatedMessages = [...messages, userMessage];
setMessages(updatedMessages);
setInput("");

setLoading(true);

// Handle game state and AI response
if (!gameState.subject || !gameState.difficulty) {
handleGameSetup(input);
} else {
await handleGameInteraction(input);
}

setLoading(false);
};

// Handle game setup (subject and difficulty selection)
const handleGameSetup = (userInput) => {
if (!gameState.subject) {
setGameState((prevState) => ({ ...prevState, subject: userInput }));
setMessages((prevMessages) => [
...prevMessages,
{ sender: "bot", text: "Great! Now choose a difficulty: Easy, Medium, or Hard." },
]);
} else if (!gameState.difficulty) {
setGameState((prevState) => ({ ...prevState, difficulty: userInput }));
setMessages((prevMessages) => [
...prevMessages,
{ sender: "bot", text: "Awesome! Let's start the mystery game. Type 'begin' to start your adventure." },
]);
}
};

// Handle game interaction with AI
const handleGameInteraction = async (userInput) => {
if (userInput.toLowerCase() === "begin") {
const initialPrompt = `
You are a detective solving a mystery related to ${gameState.subject} at a ${gameState.difficulty} level.
Start the story with an intriguing plot and ask the user how they want to proceed.
The mystery should contain puzzles related to ${gameState.subject}, which the user as to solve.
The user must use theoretical knowledge related to that subject and may apply some formulas.
Set the story in any part of the world.
Keep the responses short and precise so that the user doesn't get bored.
The user must find the game very interactive.
`;
const botResponse = await generateAIResponse(initialPrompt);
setMessages((prevMessages) => [...prevMessages, { sender: "bot", text: botResponse }]);
} else {
const userResponsePrompt = `
Continue the mystery based on the user's input: "${userInput}".
Provide one clue or one challenge at a time and ask questions to engage the user. Let the user know if the user is going in the right path.
`;
const botResponse = await generateAIResponse(userResponsePrompt);
setMessages((prevMessages) => [...prevMessages, { sender: "bot", text: botResponse }]);
}
};

// Generate AI response
const generateAIResponse = async (prompt) => {
try {
const model = await genAI.getGenerativeModel({ model: "gemini-pro" });
const result = await model.generateContent(prompt);
const response = await result.response;
const text = response.text();
setApiData(text);
setLoading(false);

if (result && result.response) {
const text = await result.response.text();
return text;
} else {
return "Sorry, I couldn't generate a response.";
}
} catch (error) {
console.error("Error generating content:", error);
return "An error occurred while generating the response.";
}
};

const formatResponse = (text) => {
// Split the text into paragraphs
const paragraphs = text.split("\n").filter((para) => para.trim() !== "");

return paragraphs.map((paragraph, index) => {
// Replace **text** with <strong>text</strong>
const boldFormatted = paragraph.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");

// Replace _text_ with <em>text</em>
const italicFormatted = boldFormatted.replace(/_(.*?)_/g, "<em>$1</em>");

// Check if the paragraph starts with a number (for options)
const isOption = /^\d+\.\s/.test(paragraph);
return (
<p
key={index}
className={`mb-2 ${isOption ? "font-semibold" : ""}`}
dangerouslySetInnerHTML={{ __html: italicFormatted }}
/>
);
});
};

return (
<div className="flex flex-col gap-5 block items-center justify-center min-h-screen bg-black">
<ShootingStars />
<StarsBackground />
<EvervaultCard text="Do you have the skills to play, Detective?"/>
<div className="z-10">
<div className="p-4 chatbox max-h-80 overflow-y-auto flex flex-col gap-4">
{messages.map((msg, index) => (
<BackgroundGradient className="rounded-[22px] max-w-sm sm:p-10 bg-white"
key={index}
>
{msg.sender === "bot" ? formatResponse(msg.text) : <span>{msg.text}</span>}
</BackgroundGradient>
))}
{loading && (
<BackgroundGradient className="rounded-[22px] max-w-sm sm:p-10 bg-white">
Generating response...
</BackgroundGradient>
)}
</div>
<form onSubmit={handleSubmit} className="p-4 flex">
<input
type="text"
value={input}
onChange={handleInputChange}
placeholder="Type your message..."
className=""
/>
<button
type="submit"
disabled={loading}
className="relative inline-flex h-12 overflow-hidden rounded-full p-[1px] focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50">
<span className="absolute inset-[-1000%] animate-[spin_2s_linear_infinite] bg-[conic-gradient(from_90deg_at_50%_50%,#E2CBFF_0%,#393BB2_50%,#E2CBFF_100%)]" />
<span className="inline-flex h-full w-full cursor-pointer items-center justify-center rounded-full bg-slate-950 px-3 py-1 text-sm font-medium text-white backdrop-blur-3xl">
Send</span>
</button>
</form>
</div>
</div>
);
}
export default GamedevAI;

5. Deploying Your Application:

Install the Google Cloud SDK. Use a single line command to deploy application in Google Cloud.

gcloud app deploy

Ensure Google Cloud SDK is properly installed and added to your file. Double-check your API key and ensure it has the correct permissions. Verify component imports and ensure that components are correctly used in your App.js file.

Result:

Your chatbot interaction can be displayed using a simple chat interface, showcasing a conversation between the user and the AI. This interface will typically include text bubbles for messages, distinguishing between user and bot messages using different colors or styles. Here’s what you’ll achieve:

  1. An Interactive Chatbot that interacts with users, understands their solutions, and generates responses based on user input.
  2. Your application will be live and accessible on the web. You will have successfully deployed it either on Google Cloud, making it available to users around the globe.
  3. You will have gained hands-on experience in building React components and integrating them with external APIs. You will learn how to deploy a web application on Google Cloud.

By the end of this guide, you’ll have a complete web application with AI-powered features, hosted and accessible online, and a set of new technical skills that can be applied to future projects.

What’s Next?

This is just the tip of the iceberg. What we can do with AI in game development can be upto one’s imagination, which is to say, ideas are boundless.

We can further implement user preferences to generate more personalised and context-aware responses from the chatbot. Introducing points, achievements, and leaderboards not only motivate users but also create a more engaging experience. Dynamic and animated visuals generated by AI can take the visual appeal of the game or chatbot interactions to the next level.

Call to action:

To learn more about Google Cloud services and to create impact for the work you do, get around to these steps right away:

--

--

WorldandAratrika
Google Cloud - Community

Just a computer science girly with a varied interest in everything <3