Codementor Events

How to create a story game with Javascript

Published Oct 27, 2022

If you don't know Javascript or what that is, don't worry! You'll be able to learn along the way or even go without it at all. What we are going to explore is an open-source tool to build interactive stories - Twine(or see its website).

It allows us to make nonlinear stories. It can be presentations, fiction, or text-adventure games. In a nutshell, Twine creates an HTML file with all the content. So, it looks like a usual web page, and the tool is the constructor. You can upload such a "website" on the web or mobile platforms, i.e., anywhere that can parse HTML.

An example of a non-linear story (interactive fiction)

If you're particularly interested in games / interactive fiction, see an extensive collection of them. So what sort of games are possible? All of them since we can add Javascript. However, the easiest way is to do text-adventure games. Why? You don't need to program anything or much. You add stories and connect them in a meaningful way using links. I.e., if you choose "A", the story "AA" will be shown, if you choose "B", the story "BB" will be shown.

See also what you can create with the tool from their docs.

An example of an actual Twine page with a story

Creating such a page takes a few minutes on the Twine website(you can do that online). You can add Javascript, videos, music, images, customize styles via CSS and do the linking and playing around. It's a great way to start. Next, I will show you a different approach if you're used to coding in any developer's editor and want version control.

Try online first, especially if your story/game is small and only needs a little custom functionality.

Why you may need Javascript

You can go without it and feel fine. But if you have custom stuff, you'll use some macros and scripts. What are they? Imagine Twine as a core, but it has various engines that do things differently. It supports four such engines(story formats) to make the creation process more accessible. Each of them varies in complexity. In this article, I'll be using SugarCube.

This story format has many built-in things you may need. For example:

  1. Saving a game, resuming it from a save.
  2. Various events to react to. E.g., when a story is rendered, started rendering, etc.
  3. Macros, i.e., useful built-in blocks/functions. For example, buttons, custom links, conditional rendering, setting variables, DOM manipulations, etc.
  4. Audio handling.
  5. And many other valuable things.

Let's create a simple project where we want to use custom Javascript and CSS styles, but more importantly - we want to have version control! I don't use the tool's online or desktop version because I can only manage stories as files and have their versions by commit.

You'll need to install Tweego, a tool that can parse stories as files in any preferred text editor. Be aware of its limitations, though:

  1. When writing this article, the last update of Tweego was two years ago.
  2. Thus, you may not have all of the features from the supported story formats(e.g., Sugarcube).

Now you need to create a project folder:

$ mkdir twine-project
$ cd twine-project
$ git init

You can move the Tweego executable to this folder as well and add it to .gitignore

It's up to you how to organize files now! An example structure may look like the following:

.gitignore
README.md
bin/
src/
├─ config.tw
├─ styles/
│ ├─ menu.css
│ ├─ main.css
├─ modules/
│ ├─ menu/
│ │ ├─ menu.tw
│ │ ├─ menu.js

In the bin folder you have the Tweego executable to build the output to HTML(we'll get to that). All the story(game)-related code is under src folder. Tweego will put all Twine(.tw) files, CSS styles, Javascript scripts into one HTML. Therefore, it doesn't matter what project structure you have.

Twine format

Now, closer to the coding: what is config.tw? This is where your code will be in Twine format. Take a look at the specification. You may name this file whatever you want. It's named config for readability. There, we specify the settings for our game:

:: StoryTitle
My first game

:: StoryData
{
  "ifid": <a serial number of your game>,
  "format": "SugarCube",
  "format-version": "2.30.0",
  "start": <a name of the story that will be shown first>
}

:: StoryAuthor
<your name if you want>

<<script>>
// in case you'll need to have 3rd-party scripts
// remove this <<script>> section at all for now
importScripts(
  'https://cdn.jsdelivr.net/npm/chance'
)
<</script>>

You need to generate a serial number for your game, i.e., IFID. Read more about how to do that here. But for now, you can use 0000A000-A0E0-00C0-00B0-CF000E000D0E to skip this boring step.

format tells Tweego which story format to use. We'll use SugarCube. format-version is a version for this story format, currently supported is 2.30.0 only. However, there are newer versions(a limitation of Tweego).

start is a story that will be shown first. Let's create a file start.tw with this content:

:: StartOfMyGame

This is the first screen of my game, yay!

[[Start playing]]
[[Read about the author]]

The :: here indicates the ID of your passage(i.e., a page). It can be anything, e.g., :: start-of-my-game or :: something like this. Now that you have the ID, change your config.tw to have:

"start": "StartOfMyGame"

After the passage(page) ID, you do whatever you want. In our case, we wrote, "This is the first screen of my game, yay!", and it'll be rendered as regular text, that's it! The [[Start playing]] thing is a link to another passage(page).

To build that to HTML, run Tweego(it'll be watching for files changes):

$ ./bin/tweego -w src -o ./output/index.html

Here, we're telling it to watch the src folder and build an output HTML into the output folder as index.html. Run this command, and you'll see the HTML output in that folder. Don't forget to add output to .gitignore. Open output/index.html in a browser and you'll see something like this(with a more dark background color):

We create the links, but we also need to create such pages. So, we need to change the start.tw:

:: StartOfMyGame

This is the first screen of my game, yay!

[[Start playing]]
[[Read about the author]]

:: Start playing
<<back>>
It's another page called "Start playing".

:: Read about the author
<<back>>
I'm the author. This is my page.

We've added two more pages, so whenever you click on, for example, "Start playing", you'll be redirected to the "Start playing" passage:

We see a new link here - "Back"! <<back>> is a SugarCube macro that redirects a user to the previous passage(StartOfMyGame). It's a more convenient way of doing that than storing a navigation history each time.

We might create these two new passages in the other files or create all the game passages in one file. It doesn't matter because Tweego puts all of the files together into a single HTML file. You don't need to care about importing something!

Adding Javascript to Twine stories

Let's imagine we want to store some information about a player's choices. There are two approaches:

  1. We may use the <<set>> macro.
  2. We may use Javascript.

When using <<set>>:

:: StartOfMyGame

This is the first screen of my game, yay!

<<link "Start playing" "StartPlaying">>
  <<set $choice to "StartPlaying">>
<</link>>
<<link "Read about the author" "AboutTheAuthor">>
  <<set $choice to "AboutTheAuthor">>
<</link>>

:: StartPlaying
<<back>>
It's another page called "Start playing".
The choice is <<= $choice>>

:: AboutTheAuthor
<<back>>
I'm the author. This is my page.
The choice is <<= $choice>>

A few new things here:

  1. <<link>>macro does the same as [[]], but it adds more customizability. In our case, we kept the link text, but indicated a different passage ID(StartPlaying, e.g.). Also, we can do something when a link is pressed, e.g., a <<set>> instruction below.
  2. <<set>> macro stores a variable.
  3. <<= $choice>> is a macro to evaluate expressions. In our case, it's displaying $choice variable we set before.

We can achieve the same using Javascript(however, it seems unnecessary complicated in this example):

:: StartOfMyGame

This is the first screen of my game, yay!

<<link "Start playing" "StartPlaying">>
  <<script>>
    State.setVar('$choice', 'StartPlaying (Javascript)')
  <</script>>
<</link>>

:: StartPlaying
<<back>>
It's another page called "Start playing".
The choice is <<= $choice>>
  1. We still do the scripting inside <<link>> macro. But we use <<script>> macro now. Inside of it, we use a global object State's method setVar which does the same as <<set>> in the previous example.
  2. We still display the $choice variable not using Javascript, but we could find that HTML block using jQuery(which is built-in in SugarCube scripts), and then set the value of it to $choice, but it's unnecessary.

When you use Javascript, you have access to the story format's APIs, so it's more customizability. However, you may not encounter such complexity in your game.


That's it for now! There are more things to do in a game, of course. But you have the documentation and tools to discover and learn more independently.

Source

Discover and read more posts from Serhii C.
get started
post commentsBe the first to share your opinion
Buck Atkins
10 days ago

While on vacation, I didn’t know what to do to keep myself busy. I went through all the games on my PS and didn’t know what to do. But my friend recommended a site where I started playing the online game original minesweeper. I think you just need to try playing this or other games you like. Even when I went to work, I still play these games during breaks) While playing you can distract yourself, or relax a little.

Charles Stone
19 days ago

Yes thats the one. Firstly the enemy territory was supposed to be a add on/dlc for the franchise Return to Castle Wolfenstein but then they decided to be a mutiplayer game ( and free ).

More recently the game was added to steam however over the past 15 or more years the game has been modded and maintained by the community.

If you would like to see more of the gameplay search on reddit for enemy territory. They have good videos there and explanation how to download and play the game. ( It requires skill and only skill to master it )

hannah morison
8 months ago

anyone here help me out too make a simple code for my <a href=“https://tattoovillas.com/rotary-vs-coil-tattoo-machine/”>website</a>

Show more replies