Open In App

Text-based Adventure Game using MERN Stack

Last Updated : 27 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Text-based adventure games, where players navigate a story through choices and text, have a unique charm in gaming. With modern web development tools like MERN (MongoDB, Express.js, React.js, and Node.js), building these games is easier and more powerful than ever. Using MERN, developers can create immersive text-based adventures that captivate players with compelling narratives and engaging decision-making.

Output Preview: Let us have a look at how the final output will look like.

a

Final Preview

Prerequisites:

Backend (Node):

  • Set up a NodeJS server using Express.
  • Create a MongoDB database to store game data.
  • Implement routes for starting a new game, hitting, and standing.
  • Define logic to determine winners and calculate scores.

Functionalities of Backend:

  • Importing modules such as Express, Mongoose, cors, and body-parser.
  • Creating an Express app, setting the server port.
  • Middleware is used to Enabling CORS and JSON data parsing.
  • Connecting to a local MongoDB database.
  • Defining Mongoose schemas.
  • Starting the server and listening on the specified port (either from the environment variable or defaulting to 5000).

Frontend (React)/Functionalities:

  • Develop a responsive UI for the Blackjack game.
  • Create a decision based Story line.
  • Connect the frontend to the backend using Axios for API calls.
  • Implement functionalities for standing, and starting a new game.
  • Display game state, user paths and results/winner messages.

Steps to Create Text-based Adventure Game with MERN Stack:

Steps To Create The Backend:

Step 1: Create a new NodeJS project.

npm init -y

Step 2: Install necessary packages such as express, mongoose, cors, and body-parser.

npm install express mongoose cors axios

Step 3: Set up a MongoDB database using MongoDB Compass. Define Mongoose schemas as require, Game Logic and Routes In server.js:

Project Structure(Backend):

Screenshot-2024-03-16-215303

File Structure

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"axios": "^1.6.8",
"cors": "^2.8.5",
"express": "^4.18.3",
"mongoose": "^8.2.2"
}

Example: Below is the code for backend:

Javascript
// server.js

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const routes = require('./routes');

const app = express();

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/text_adventure_game', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

// Middleware
app.use(bodyParser.json());

// Routes
app.use('/api', routes);

// Start the server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Javascript
// routes.js

const express = require('express');
const router = express.Router();
const GameController = require('./controllers/GameController');

// Routes
router.get('/player/:id', GameController.getPlayer);
router.post('/player', GameController.createPlayer);
router.put('/player/:id', GameController.updatePlayer);

module.exports = router;
Javascript
// controllers/GameController.js

const Player = require('../models/Player');

module.exports = {
    async getPlayer(req, res) {
        try {
            const player =
                await Player.findById(req.params.id);
            res.json(player);
        } catch (err) {
            res.status(400).json(
                {
                    error: err.message
                });
        }
    },

    async createPlayer(req, res) {
        try {
            const player = await Player.create(req.body);
            res.json(player);
        } catch (err) {
            res.status(400).json(
                {
                    error: err.message
                });
        }
    },

    async updatePlayer(req, res) {
        try {
            const player =
                await Player.findByIdAndUpdate(
                    req.params.id,
                    req.body,
                    { new: true }
                );
            res.json(player);
        } catch (err) {
            res.status(400).json(
                {
                    error: err.message
                });
        }
    }
};
Javascript
// models/Player.js

const mongoose = require('mongoose');

const playerSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    location: {
        type: String,
        default: 'Start'
    },
    inventory: [String]
});

const Player = mongoose.model('Player', playerSchema);

module.exports = Player;

Steps to Run the backend:

Step 1: Navigate to blackjack-server root directory:

cd text-adventure-server

Step 2: Run the server using following command:

node server.js

Steps To Create The Frontend:

Step 1: Set up a new React app using create-react-app.

npx create-react-app text-adventure-game

Step 2: Go to the desired directory using the following command.

cd text-adventure-game

Project Structure(Frontend):

f

file Structure

The updated dependencies in package.json file of frontend will look like:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},

Example: Below is the code for frontend:

CSS
/* text-adventure-game\src\App.css */

.App {
  text-align: center;
}

.App-header {
  background-color: #44516c;
  min-height: 30vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

button{
  background-color: black;
  color:rgb(200, 135, 70)
}

footer{
  color: green;
}
Javascript
// text-adventure-game\src\AdventureGame.js

import React, { useState } from 'react';

function AdventureGame() {
  const [currentScene, setCurrentScene] = useState('start');
  const [playerName, setPlayerName] = useState('');
  const [score, setScore] = useState(0);
  const [inventory, setInventory] = useState([]);
  const [health, setHealth] = useState(100);

  const handleInput = (choice) => {
    switch (currentScene) {
      case 'start':
        if (choice === '1') {
          setCurrentScene('scene1');
        } else if (choice === '2') {
          setCurrentScene('scene2');
        } else {
          alert('Invalid choice!');
        }
        break;
      case 'scene1':
        if (choice === '1') {
          setCurrentScene('scene3');
          setScore(score + 1);
        } else {
          setCurrentScene('end');
          setHealth(health - 20);
        }
        break;
      case 'scene2':
        if (choice === '1') {
          setCurrentScene('scene4');
          setInventory([...inventory, 'map']);
        } else {
          setCurrentScene('scene5');
        }
        break;
      case 'scene3':
        if (choice === '1') {
          setCurrentScene('end');
          setHealth(health - 30);
        } else {
          setCurrentScene('end');
          setHealth(health - 10);
        }
        break;
      case 'scene4':
        if (choice === '1') {
          setCurrentScene('scene6');
          setScore(score + 2);
        } else {
          setCurrentScene('scene7');
        }
        break;
      default:
        setCurrentScene('start');
    }
  };

  const renderScene = () => {
    switch (currentScene) {
      case 'start':
        return (
          <div>
            <p>Welcome to the Adventure Game!</p>
            <p>What's your name?</p>
            <input
              type="text"
              value={playerName}
              onChange={(e) => setPlayerName(e.target.value)}
            />
            <p>Choose your path:</p>
            <button onClick={() => handleInput('1')}>Path 1</button>
            <button onClick={() => handleInput('2')}>Path 2</button>
          </div>
        );
      case 'scene1':
        return (
          <div>
            <p>
                Scene 1: You wake up in a dense forest. 
                What do you do?
            </p>
            <button onClick={() => handleInput('1')}>
                Explore deeper into the forest
            </button>
            <button onClick={() => handleInput('2')}>
                Look for a way out
            </button>
          </div>
        );
      case 'scene2':
        return (
          <div>
            <p>Scene 2: You find an abandoned cabin. What do you do?</p>
            <button onClick={() => handleInput('1')}>
                Search the cabin for supplies
            </button>
            <button onClick={() => handleInput('2')}>
                Ignore the cabin and keep moving
            </button>
          </div>
        );
      case 'scene3':
        return (
          <div>
            <p>Scene 3: You encounter a group of hostile creatures.</p>
            <button onClick={() => handleInput('1')}>
                Fight them off
            </button>
            <button onClick={() => handleInput('2')}>
                Try to flee
            </button>
          </div>
        );
      case 'scene4':
        return (
          <div>
            <p>Scene 4: You discover a hidden map inside the cabin.</p>
            <button onClick={() => handleInput('1')}>
                Take the map and continue your journey
            </button>
            <button onClick={() => handleInput('2')}>
                Leave the map and move on
            </button>
          </div>
        );
      case 'scene5':
        return (
          <div>
            <p>
                Scene 5: A sudden storm forces you 
                to seek shelter in the cabin.
            </p>
            <button onClick={() => handleInput('1')}>
                Wait out the storm inside the cabin
            </button>
            <button onClick={() => handleInput('2')}>
                Risk continuing your journey despite the storm
            </button>
          </div>
        );
      case 'scene6':
        return (
          <div>
            <p>
                Scene 6: You stumble upon a hidden cave 
                filled with treasures!
            </p>
            <button onClick={() => handleInput('1')}>
                Explore the cave further
            </button>
            <button onClick={() => handleInput('2')}>
                Leave the cave with the treasures you've found
            </button>
          </div>
        );
      case 'scene7':
        return (
          <div>
            <p>
                Scene 7: You encounter a friendly 
                traveler who offers to join you on your adventure.
            </p>
            <button onClick={() => handleInput('1')}>
                Accept the traveler's offer
            </button>
            <button onClick={() => handleInput('2')}>
                Continue your journey alone
            </button>
          </div>
        );
      case 'end':
        return (
          <div>
            <p>Game over, {playerName}!</p>
            <p>Your health: {health}</p>
            <p>Your score: {score}</p>
            <p>Inventory: {inventory.join(', ')}</p>
            <button onClick={() => setCurrentScene('start')}>
                Play again
            </button>
          </div>
        );
      default:
        return <p>Invalid scene!</p>;
    }
  };

  return <div>{renderScene()}</div>;
}

export default AdventureGame;
Javascript
// text-adventure-game\src\App.js

import React from 'react';
import AdventureGame from './AdventureGame';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>Text-based Adventure Game</h1>
      </header>
      <main>
        <AdventureGame />
      </main>
      <footer>
        <p>Created by @GeeK</p>
      </footer>
    </div>
  );
}

export default App;

Steps to Run the frontend:

Step 1. Navigate to text-adventure-game root directory:

cd text-adventure-game

Step 2. Run the following command:

npm start

Output:

test-(1)

Final Playing

Conclusion:

Text-based adventure games allow players to immerse themselves in imaginative worlds through compelling stories, exploration, and strategic decision-making. Developers can craft captivating adventures using the MERN stack, which enables them to create interactive experiences that transport players to realms of adventure and mystery. Embarking on quests, solving puzzles, and interacting with characters, players shape their own unique journeys in text-based adventures, where the possibilities are boundless and imagination reigns supreme.




Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads