2014-10-17

System in action

This is a little preview of the current working state of my system Fate.

I'm starting with some source code put together from the last example. I use Sublime Text for writing since it allows for syntax highlights and code snippets and other nice things.



The source is saved inside a resource folder in a Unity project. I can open the visual editor to make sure that everything compiles, then I'm ready to build an executable.



Unity does its thing and then the game is ready to play.



When the game starts up the first thing that the system does is copy the source code from the project into a folder on the local drive. This copy contains the exact same source text files, and it is what will be read into the game in place of the original. In other words this makes the source of the game available to be modded if the player wants.



Next the system reads the files from this directory and compiles the source code into a game. If this process fails there will be an ugly error like this.



As you can see, these warning messages aren't always very specific, so I have a lot of work to do to make debugging reasonable. Generally though, the default game source *should* be bug free. One challenge here is that a lot of the source is compiled up front, but some things can obviously only be evaluated during the game, so run time issues may pop up with the same error.

If all goes well we are taken to the game's title screen. All of the values here (author, title, brief description) are exposed in the source code via content functions. This makes the system pretty flexible and generic, but right now it's a little too generic visually.



I can exit out of the game and modify the source code with this line:
content("sensitiveContent", "true")
This marks the game as containing restricted material. Now when we start the game we will get a disclaimer with a rating and content details that we set.



Back on the title page we can navigate to the about page that shows other data we can specify about the game. One cool thing that I'm not showing here is that the number of contributions by a contributor are counted, and under the credits contributors are sorted by most contributions to least.



Anyway onto the game itself. Once we press New Game from the main menu, we enter the first passage, Start.



There are a good amount of options available to adjust the text for readability.



A game could be entirely choice based, world based or a mixture of the two. When mixing the two (which is pretty much what I'm doing) you make choices until you reach end points where you are then free to explore the world. World exploration/interaction is done with a horizontal infinitely scrollable list of nouns and then actions on those nouns.



Once items are picked up they get held in a separate list just for inventory. This was an important addition I made during the rewrite since it helps to keep the main list of nouns less crowded while playing.



We can save our progress and the save goes into a saves directory beside the source folder. I currently don't have undo/redo exposed in the interface, but the history system is able to go from wherever you are all the way to back Start. All choices are included in game saves.



If we quit and reload, we automatically return to where we left off by loading the most recent save. Otherwise saves are accessible via a simple menu from within the game.



And finally once we reach an end point specified with control("end") our game is auto-saved and we can return to the home screen when ready. (ignore that bit about the world menu still being displayed >_>)



So there you have a quick run down of the system. I can't even really measure all the work that has gone into this, but I really think it was worth it. I still have some important things to do before I can release a playable demo for desktop platforms. Android is quite a headache due to resolution issues and iOS is a pie in the sky. My current focus is on writing Something, so I guess my next post will be about that.

2014-10-06

story map

Last time I talked about the syntax I'm using to write stuff. In this post I'll show some micro examples along with images of the story map representation.

Lets start with a basic example using the following source.

:: Start
Some text.
[[Passage A]]
[[Passage B]]
:: Passage A
Some text.
:: Passage B
Some text.
[[Passage C]]
[[Passage D]]
:: Passage C
Some text.
:: Passage D
Some text.
[[Passage E]]
:: Passage E
Some text.
[[Passage F]]
[[Passage G]]
[[Passage H]]
view raw 001 ex 1 hosted with ❤ by GitHub


I guess you could say I was kinda *inspired* by Twine again. I couldn't think of a better way to represent the basic information of going between passages, so, what more can I say.


Moving on, here is an example using a display() passage. It works a lot like <<display>> would, basically adding one passage in it's place when evaluated at run time.

:: Start
Some text.
[[Passage A]]
[[Passage B]]
:: Passage A
Some text.
display(Passage A filler)
:: Passage A filler
Some text.
[[Passage C]]
[[Passage D]]
:: Passage B
Some text.
[[Passage C]]
[[Passage D]]
:: Passage C
Some text.
display(Passage C filler)
:: Passage C filler
Some text.
[[Passage E]]
:: Passage D
Some text.
[[Passage E]]
:: Passage E
Some text.
[[Passage F]]
[[Passage G]]
[[Passage H]]
view raw 001 ex 2 hosted with ❤ by GitHub


I use display() a lot, so I decided to highlight those passages in the map for faster recognition.


In this next example I use some implicit links and display()s.

:: Start
Some text.
[[Passage A]]
[[Passage B]]
:: Passage A
Some text.
display(Passage A filler)
:: Passage A filler
Some text.
[[Passage C]]
[[Passage D]]
:: Passage B
Some text.
[[Passage C]]
[[Passage D]]
:: Passage C
Some text.
display(Passage C filler)
:: Passage C filler
Some text.
[[Passage E]]
:: Passage D
Some text.
[[Passage E]]
:: Passage E
Some text.
[[Passage F|query($choice = 1)]]
[[Passage G]]
:: Passage F
criteria($choice == 1 && $someOtherKey > 7)
Some text.
criteria($choice == 1)
Some text.
:: Passage G
Some text.
display($displayPassage = 5)
:: Passage G filler
criteria($displayPassage == 5 && $someOtherKey > 7)
Some text.
criteria($displayPassage == 5)
Some text.
view raw 001 ex 3 hosted with ❤ by GitHub


They also got unique colors to help them stand out. I don't use them all of the time, but when I need them they are super helpful to have.


Now onto the world portion of things. This source code sets up a basic world structure with five different nouns and a couple verbs. Notice that a Start passage is still required since it is always the entry point of a game.

:: Start
Some text.
:: Azcaplab's Ship [noun()]
:: Captain Azcaplab [noun(Azcaplab's Ship)]
The self proclaimed prince of the seven seas.
:: Isle of Remorse [noun()]
:: Coast of Regret [noun(Isle of Remorse)]
:: Cave of Foreboding [noun(Isle of Remorse)]
This cave is probably scary.
view raw 001 ex 4 hosted with ❤ by GitHub



The nouns act kind of like a layer beneath passages. Each parent noun draws a white line to it's children. The green lines connect the actions on nouns to the passage responses of those actions. These links get pretty crowded, so I'm still not entirely happy with the way this is setup. For now though, it works.


Now an example with more stuff going on.

:: Start
Some text.
control("player", Captain Azcaplab)
:: Azcaplab's Ship [noun()]
:: board [verb(Azcaplab's Ship)]
move(Captain Azcaplab -> Azcaplab's Ship)
All aboard matey. (sorry)
:: Captain Azcaplab [noun(Azcaplab's Ship)]
The self proclaimed prince of the seven seas.
:: Isle of Remorse [noun()]
:: Coast of Regret [noun(Isle of Remorse)]
:: dock the ship [verb(Coast of Regret)]
move(Captain Azcaplab -> Coast of Regret)
You pull up to the coast and hop out.
:: Cave of Foreboding [noun(Isle of Remorse)]
This cave is probably scary.
:: enter [verb(Cave of Foreboding)]
move(Captain Azcaplab -> Cave of Foreboding)
You enter the dank cave.
:: Treasure Chest [noun(Cave of Foreboding)]
This is the good stuff for sure.
:: take [verb(Treasure Chest)]
move(Treasure Chest -> Captain Azcaplab)
move(Great Demon -> Coast of Regret)
The treasure is yours!
:: Great Demon [noun()]
She doesn't like visitors.
view raw 001 ex 5 hosted with ❤ by GitHub


This time in Start I use control() to make the captain the player. With that set, the icon for the captain changes along with many of the other nouns. The treasure chest is taken by the captain at some point, so it gets represented as a pickup. All of the places where the captain moves get a container icon, since these locations could be rooms, vehicles or anything really. The Isle is still the default static icon because it never moves and never holds the player directly. Static objects are mostly useful for grouping nouns together, but don't need to serve any purpose in game. Finally the demon has the red dynamic icon since it does move around.

All of the little icons help add useful information that otherwise aren't represented in the source code.

Since this is all in the Scene view of the Unity editor I decided to add another perspective of the world to help understand what is going on.


From a top view we get a floor plan of the world at it's default state. It's much easier to see which object is inside of another this way. I've found that I don't need to make diagrams of this stuff anymore since it is easily represented here. The connecting lines are in the way in this image, but they can be toggled off.

And so that's the story map.

2014-10-02

syntax

The way I'd like to write games is simple. Write some text, then view it in a node map and repeat. The rewrite of Fate was mostly so that I could do just that.

The first thing I needed to do was formalize a syntax for writing that could handle the type of games I'd like to make. I started with tweecode as a base. Passages mark collections of text, and they look like they would with Twine.

:: Some Passage
Some text.
view raw 000 ex 1 hosted with ❤ by GitHub

Links between passages are done with familiar double brackets:

[[Another Passage]]
[[Any Passage|Passage B]]
view raw 000 ex 2 hosted with ❤ by GitHub

I didn't want to keep the Macro syntax: <<set $thing = 8>> , so I slimmed it down like this: set($thing = 8) but I kept the $ character prefix on variables as an act of habit.

One interesting feature I had to figure out how to represent are implicit links:

[[Another Passage|query($passage = 8)]]
view raw 000 ex 3 hosted with ❤ by GitHub

The query() part sets the value of 'passage' to 8 before executing a query to find the best passage to display. So to define passages that are queried:

:: Some Passage
criteria($passage == 8)
Some text.
view raw 000 ex 4 hosted with ❤ by GitHub

Basically the criteria() function binds the passage to it's arguments. To save a little writing I setup the ability to stack multiple passage responses under a single passage declaration.

:: Some Passage
criteria($passage == 8 && $onFire == 1)
Some text while the thing is on fire.
criteria($passage == 8)
Some text.
view raw 000 ex 5 hosted with ❤ by GitHub

Then there is the world system (although it's not really that amazing of a system). I opted for familiarity in the declaration of passages, nouns and verbs.

:: House [noun()]
This is a great house.
:: Room [noun(House)]
This a room in the house.
view raw 000 ex 6 hosted with ❤ by GitHub

The noun() call is done where tags would be in tweecode. Notice that the noun() function for 'Room' has 'House' as an argument. It basically sets up the default hierarchy of the two objects; the room is a child of the house. The body of these declarations are automatically mapped as descriptions that respond to an examine action.

Actions are declared in a similar way.

:: set on fire [verb(Room)]
You light the room on fire.
view raw 000 ex 7 hosted with ❤ by GitHub

Verbs that shouldn't always be available can use a require() function.

:: extinguish fire [verb(Room)]
require($roomIsOnFire == 1)
You try to extinguish the flames.
view raw 000 ex 8 hosted with ❤ by GitHub

require() was necessary in addition to criteria() since responses to verbs may need to be tied to criteria since they are essentially passages.

:: dance around fire [verb(Clown)]
require($player == Clown)
criteria($clownIsDrunk == true)
You dance carelessly around the fire.
criteria($clownIsDrunk == false)
You try your luck and dance around the fire.
view raw 000 ex 9 hosted with ❤ by GitHub

Moving objects around is done with a move() function

:: escape from house [noun(Clown)]
require($roomIsOnFire == 1)
move(Clown -> Outside)
The clown moves clumsily out of the burning house.
view raw 000 ex 10 hosted with ❤ by GitHub

I skipped a lot of functionality (print, display, if/else etc), but that is a quick look at the basics of how I'm writing Something in Fate.