Tag Archives: janus

Prototype sampler # 1

So, finally, I’ve completed my last major developmental milestone and I think that deserves a post. ūüôā

The above video simply shows the playthrough of¬†some of the game mechanics. The quest has not been fully written yet, and though all of the game mechanics are working, they’re not readily apparent without some introductions. The video is mainly to see how adventure and combat are blending as one piece.


I’ve slaved away on numerous aspects to accomplish all the major milestones I had set out to do. There were a few additions to these, but they were minor changes, and all in part of the iterative process of figuring out the closest gameplay mechanics I wanted to implement in Unity.

Though my work isn’t done yet — there are still UI issues I need to sort out — and there are still some niggling bugs present in the prototype, it is largely playable. By ‘playable’ that means you can run around, talk to people, and shoot Robots, and get shot back. You can plant a bomb, blow it up, and you can blow yourself up in the process as well. You can ‘pox’ a powerlet to get energy, you can buy and use meds to heal yourself. Frankly, a few months ago I didn’t think I could end up saying all this in one paragraph.

Most of the joy, and fear, of this prototype has been the implementation of a bespoke AI graph framework. It’s a joy because it actually works; it’s a fear because it sometimes feels too deep for me to always grasp its innards when some things don’t go right.

I’ve gone through mounds of halved/quarter-A4 to-do sheets with heaps of orange highlighter marks signifying all the big and small tasks or goals I needed done. There are so many disparate systems working that that if I didn’t have a calendar tracking my progress, I wouldn’t be able to grasp what I myself had accomplished.

For example, here’s a quick run-down of the aspects.

  • Asset creation.
    • I’ve heavily used Janus to break out animated sequences.¬† Using FORFILEs, a Janus looping construct that iterates through the lines of a file, creating an animated character, such as the Player character, was simple as I needed only to set up one angle and let Janus break out all the other 15 directions. Variable frame ranges for a particular animation were also taken care of using the same principle.
    • Janus was an important cog in the making of the prototype because of the amount of iterations for the scenes. An element would sometimes become designated as an interactable element, which had to be split from the main scene and rendered separately.
    • NPC/Robot portraits had a separate animation and render, and specifically had to go through post-processing.
  • Tiled¬†was used in making the maps, and Rex’s TMX Importer was used to carry that information in C2. I had to do some modifications to the TMX Importer to enable the retrieval of the Tiles and Objects image source. Tiled enabled me to experiment and implement concepts by introducing certain datatypes for the engine’s use, which informs me of how I may implement the maps in Unity.
    • This had to be balanced with Game Data Documents which are comprised of text-based files of varying structures. These Data Documents are the immutable attributes used by the systems. In the beginning, the data would come from different sources; one would be defined in the TMX, while others defined in a CSV table. As I progressed, I refined the categorisation of data.
  • The in-game Inventory system was one hell of an undertaking, The Inventory system is connected to the Trade system, which is further split into two variants: the Container system, and the Merchant system; the former simulates the ability to store items in ‘containers’, and the latter simulates buy/sell transactions with NPCs. Merchant data, like price, buy/sell limitations, and price adjusters are tied to tables and the NPC entity as defined in the TMX.
  • While the code related to the movement was entirely specific to C2, I had to nevertheless overcome these issues to get a working prototype. Pathfinding needed some optimisations, behaviours related to physicality of entities needed to be coded in relation to the established movement behaviours. This aspect will largely be replaced by Unity’s navmesh, in addition to a target grid overlay that I may custom-build myself.
  • The Action Strip (a.k.a. Astrip) system — the method for interacting with elements in the game — was developed to be authored using text files (like most systems in the game).It serves as the hub for all ‘adventure’ interactions. It was also designed to be generic so that the display of interaction results can be be tweaked directly from the text file. For example, a ‘look’ action,¬† at an object may initiate a display of a description, or the narrative box, or initiate a dialogue, or anything else that has been allowed in the engine.
  • The Convo system was another early development. Some additional Python code was necessary to convert the authored .graphml files (using yEd) into a Markdown format (for readability in a text editor). However, the development of the AI graph framework proved that the Convo system was inferior, though both used node graphs. Although the Convo system has not yet been upgraded to use the same (or similar) framework of AI, this would eventually be done when the port to Unity is made.
    • The Convo system could be called by the Astrip system.
    • The Convo system also allows implicit trade of items. For example, if through speaking with an NPC, it gives you an item to be used. The Convo system communicates to the Inventory system and places the item in the Players inventory.
  • The AI system used the TGF format to represent a nodal graph. Then an in-game parser and callback/event handler framework handled the execution of the AI graph on a per-Robot basis.
    • The AI system is connected to other systems, such as the Inventory, the Trade, Convo (dialogue system) and of course, NPCs/Robots themselves.
    • Using the AI system, a Robot can accost you to do a contraband check, which was one of the first implementations of the AI (even before combat).
    • The AI can contextualise its own dialogue with the Player, changing it from a contraband check to an arrest, for example.
  • Lookups for gameplay values, such as hit-chance, effect of skills on gameplay, were done using a non-linear interpolation that was accomplished by using Open Office Calc’s cell formulas. This allowed me to tweak lookup values utilising functions as opposed to doing it individually, per cell! This application was conveniently placed to export to CSV directly, so no other intermediate process was needed to get it to C2.
  • The Combat system is closely tied with the AI and is comprised of many factors, a few of which include:
    • Alert level behaviour of Robots; certain actions at a certain alert level means differently for Robots. For example, running or crouching is OK when Alert Level is 0. But when the Alert Level is 1, running or crouching is interpreted as¬†suspicious and Player will be fired upon.
    • Behaviour of Robots differ from one another. Some guard, some patrol, some check for contraband.
    • Offensive component
      • Player accuracy skill
      • Weapon attributes such as range, max_range, weapon dropoff (weapon damage and chance to hit is affected)
      • Rate of fire
      • Dual-wielding of weapons
      • Crouching increases accuracy
      • Bomb placement and detonation
      • Shock effect; certain weapon may stun a Robot for a period of time.
      • GMAC system, which is a modifier on top of a typical random number generator.
    • Defensive component
      • Use of cover for defence
      • Crouching reduces profile, increases Player defence against being hit
      • Running increases Player defence against hit but only if running perpendicular to Robot.
    • Stealth component
      • Crouching behind low obstacles for stealth
      • Noise level when running; Robot hears you!
      • Glitters is Electronic Counter-Measures and makes the Player invisible for a short period of time.
    • Hacking¬†powerlets to get more energy, and the associated success rates, and the penalties for failure
    • And others that are too lengthy to include, but you get the idea…

Normally, a prototype is small, whose gameplay represents the root of what the game is about. Sometimes, a prototype is created to determine if a gameplay works or not, or if people like it enough.

But I built the prototype as a technical reconnoitre¬† of what I’m going to come up against. You can say I was also trying to form a beachhead at the same time. I don’t know if people would like it, but I can’t be dissuaded either way; I’ve gone this far solely on the excitement of taking a childhood game to my present.

But a prototype is also made to present the gameplay as clear as possible, that if the prototype is¬†fun to play, then the real thing would be as fun, if not more fun to play. The problem I have with Citizen is that it is an adventure as much as it is a shoot-em-up game. The fun in 2400 AD, Fallout, or Shadowrun, for example, is the fact that it is an adventure. But I find it difficult to express the full adventure by doing a half-adventure. I think that’s due to my lack of experience writing for games. At the same time, I think that I’ve been focused so much on the technical aspects that I’ve not really dug as deep as I should into the potential of the narrative. I’ve been working on the framework in which I hope to base an adventure story (of which I have a first draft already), and I think that this prototype, as it stands, should be just seen as the prototype for the framework.

More to come.

 

Advertisements

Animation ‘sheets’

In the test RND project, there were a fair amount of sprites used for the player character which featured variations that depicted a separate facing direction from the move direction.

In CITIZEN there are additions such as drawn weapon of a certain size, pose changes (standing, crouched), and speed. All of these combine into a huge animation sprite ‘sheet’.

I use ‘sheet’ to mean a series of sprites meant to be organised together. Much of the work has been how to organise the sheets in such a way as to easily reference them.

Organisation

The animation hierarchy looks something like this.

  • For every direction (8 directions)
    • (For every direction) A weapon state: unarmed, armed small, armed medium
      • (For every weapon state) A pose: stand, crouch.
        • (For every pose) An intended movement: idle, walk, run, weapon in, weapon out, shoot.

Due to the resulting number of images, I had decided against implementing a separate facing and moving direction, since this would somewhat triple the amount of frames.

In C2, the interface to manipulate sprites is not production-friendly, so the lesser number of Animation folders used the better. Therefore, I decided to arrange the animation by the last element in the hierarchy, which is the intended movement. This grouping was also closest to how it was being rendered in 3d, so the transfer of the sprites to C2 was simpler.

Animations grouped by ‘intended action’.

For the record, ‘wf‘ is ‘walk forward’, ‘rf‘ is ‘run forward’, ‘cf‘ is ‘crouch forward’, ‘wepio‘ is a combination of ‘weapon in’ and ‘weapon out’, and the rest are self-explanatory. ‘wepio‘ has been put together in one group because the frames used in ‘weapon in’ is the reverse of ‘weapon out’, and also the limited frame meant that another animation folder was unnecessary.

Inside any of these folders are the large number of sprites which depict the intended action (eg wf) in stand and crouch, and for each of that, in unarmed, armed small, and armed medium variations, and for each of those variations, all 8 directions.

It is organised as the hierarchy above indicates. Eg in the folder ‘wf’, the first number cycle of frames depict Direction 0, Unarmed, Stand, Walk forward. The second depicts Direction 1, Unarmed, Stand, Walk foward, etc.

Animation map

In order to to retrieve the sprite frame or sequence to play, an animation map had to be created in order for the C2 events to locate the frame and folder.

The animation map consisted of simple directives. Here’s an example:

# IDLE --------------------------------------
u upi anim:idle
u upi is_seq:0-0@5,0-3@3,4-5@3,6-7@2,7-5@2,5-0@2
u upi num_frames:8
u upi prev_frames:0

as upi anim:idle
as upi is_seq:0-0@5,0-3@2,3-6@2,3-0@2
as upi num_frames:7
as upi prev_frames:8

am upi anim:idle
am upi is_seq:0-0@5,0-3@.15,3-0@2,4-7@2,7-4@10
am upi num_frames:8
am upi prev_frames:15

Left of the colon ‘:’ is the directive, and at the right is the value.

The directive is in this template: <wep> <pose&action><key>.

<wep> can be ‘u‘, ‘as‘, ‘am‘.

<pose&action> is a combination of the pose and intended action. ‘pose‘ can either be ‘up‘ or ‘dn‘. ‘action’ can be ‘i‘ (idle), ‘wf‘ (walk forward), ‘rf‘ (run forward), ‘cf‘ (crouch forward), ‘s‘ (shoot), ‘wepi‘ (weapon in), and ‘wepo‘ (weapon out).

Then back in C2, a lookup string is constructed based on the state variables, and this is used to find the directive name, eg ‘u dni’, which means unarmed, crouched, idle.

The ‘key’ is the attribute of a particular animation. ‘anim‘ refers to the C2 animation folder that this animation will use. ‘is_seq‘ is a string¬†that tells the system to play a sequence in a particular order, including a wait time. The syntax for is_seq is as follows.

<startframe>-<endframe>@<wait_time>

Note that the startframe and endframe tokens are relative to the sequence being looked up.

The ‘num_frames‘ key specifies the number of animation frames this sequence is supposed to have. This is used to find the proper offsets.

The ‘prev_frames‘ key specifies the number of total offset frames from the beginning of this animation folder. Note in the example above, notice how the next ‘prev_frames‘ value is the sum of the ‘num_frames‘ and ‘prev_frames’ of the previous entry. It is definitely easier to create the animation map in the order in which they are currently arranged in the 3d renders, so that the specification of offsets in the file is just a matter of adding on top of the previous.

(There are also two other keys that bear mentioning. One is ‘next_anim‘ and ‘endsignal‘. ‘next_anim‘ is used so that a specific animation be explicitly told to move to another animation after the sequence is finished. Currently, this is used to automatically move to the ‘idle’ animation after drawing or hiding the weapon. ‘endsignal’ is a text that is specifically used to trigger some action/event in C2. For example, this is used to signal that a weapon has completed being drawn or holstered. So in the YML file, the endsignal of wepin, is ‘wepin’, and in C2, an “AnimationEndSignal” function is used to look for that specific signal string. When it encounters it, it changes the weapon status variable.)

In relation to sorting renders out predictably, 3d renders are appended with a prefix that enables them to be alphabetically sorted in a predictable way. So, for example, if the ‘wf’ (walk forward) animation folder, the ‘u’ sequences go first, followed by ‘as’, then ‘am’. That sorting is enforced by prefixing 3d renders of ‘u’ by ‘a_’, ‘as’ by ‘b_’, and ‘am’ by ‘c_’. In addition to this, the direction angle (eg 0, 45, 90, 180, etc) are written with 3-digit padding (eg 000, 045, 090, 180). With these two modifications to the 3d render filename, this will¬†always yield an correct sort. These naming conventions are incorporated into Janus configurations so there’s no need to do them manually.

The point is: renders need to be sorted right from the time they are rendered, so that when cropped and copied over, C2SpriteMan can rename them in the proper order they will appear finally in the animation sheet.

Primer on animation workflow

Now that the animation aspects being developed in C2 is becoming closer to how renders are named, the following information bears putting down.

First there are several applications involved in making character animation in C2 in CITIZEN: Maya (using Sandline for cache export), LightWave3D, Imagemagick, and C2SpriteMan. The following information is the workflow of data and formats leading up to the final graphics put into C2.

Maya animation

The animation workflow starts assuming that all characters have been modelled; texturing is done separately from animation. In Maya, the characters are rigged.

The organisation of the Maya scenes are such that only one scene file is used for all animation.

Because the Maya workflow uses Sandline, which uses namespaces in order to organise the cache files in the file system, multiple references of the character are brought in. (While I admit that this is an overhead, it is negligible because of the minimalist assets used in the game). These multiple references refer to the animation that the rig is intended to be. For example, the walk animation will have the PWALK (Player Walk) namespace, and the run animation is called PRUN. These referenced rigs will be animated as such, and then they are exported via Sandline, the cache folders they will reside in will be according to their namespace: /PWALK, and /PRUN. This makes it easier to identify the animation in the file system.

Furthermore, the animation timeline in Maya starts at frame 1. Each referenced rig, each with their own assigned animation, starts at frame 1 and animate for as long as necessary, as long it is no more than 100 frames long, which is the defined limit for this workflow.

Orientation of the character is +Z. Animation is only for one direction, too, since the multiple directions are rendered automatically using Janus in LW.

Once the cache files are exported it goes into LW to be set up.

LightWave rendering

There are too many steps that describe this workflow, but the main idea, is that the relationship between the animation done in Maya is related back to LW in several methods.

Animation ranges

It must also be noted that while in Maya, all animations started in frame 1, in LW, the animation is blocked out in discrete 100-frame chunks. For example, the walk animation (eg /PWALK) is assigned frames 1-100, the run animation (/PRUN) frames 101-200, the crouch (/PCROUCH) animation frames 201-300; the idle animation (PIDLE) is assigned frames 901-1000, etc.

A nodal network in LW is created that assigns each cache file to the appropriate geometry. It also offsets each cache (which starts at frame 1) to their corresponding mapped chunk as described above.

Janus

Once the cache files are assigned, the geometries are parented to nulls that enable it to rotate to 8 directions. Then Janus comes in where it is responsible for breaking out the master scene file into the varied animations.

There are two ‘map’ files configuring Janus to do this.

Timeframe definition

Actually called ‘CDEMO_timeframe_definition.txt’, this text file maps out the the entire duration for all animations.

For example, it defines where in the timeline you can find the ‘unarmed walk forward’. Basically, Janus uses this file to set the start and end frames (and frame step parameter) depending on the intended breakout.

The intended breakout, on the other hand, is defined in Janus’s FORFILEs.

FORFILE

Without getting into too specific with Janus, FORFILEs is a Janus capability to iterate through a text file and populate aspects of the breakout by the contents of the file.

In this usage, the FORFILE lists all the breakouts that need to happen. For example, the FORFILE lists that for every 8-direction angle, a ‘unarmed walk forward’ animation is exported. In Janus, it reads the tokens and then through its configuration it reads the timeframe definition (described above) and looks up the appropriate time frames to render.

It’s also in this FORFILE that all the specific naming conventions are applied to the final image output.