All posts by faulknermano

Slidewalk TMX and C2 system

The Slidewalk system, taken from 2400AD is implemented by using several components in Tiled, and hooking them up in C2.

Tiled/TMX Component

The Slidewalk is comprised of several objects which are the children of an ObjectGroup; the ObjectGroup, in effect, is a single Slidewalk entity.

Components of a Slidewalk

What identifies an existence of Slidewalk is not the ObjectGroup, but the individual Objects which must be named starting with the prefix sw. Each Object component of the Slidewalk contains the name of the Slidewalk it belongs to. The name of the Slidewalk is the name of the ObjectGroup.

A Slidewalk must always have a path. A path is a Tiled polyline. It must be named swpath. The path will later define the waypoints of the Slidewalk. The swpath Object must also contain an attribute called speed. This is the speed value of this Slidewalk.

A Slidewalk must have at least one entry-only point. This point is defined by using an Object (parented under the Slidewalk in question). It must be named swin.

A Slidewalk must have at least one end-only point. This point must be named swend.

A Slidewalk may optionally have an exit point. This exit point may also be a potential entry point. Every point, whether it is an entry, exit, or end point, must begin with sw. Then use the keyword in to indicate this is an entry point, and the keyword out to indicate it is an exit point; these keywords may be used in the same point, such as swinout.

The exit and the end points must also contain a custom property called exitdir. This is a GridMove direction that specifies which direction the player will move towards when deciding the exit, in order to get off the movement effect of the Slidewalk.

C2 Implementation

In C2 the first step is to get the swpathObject to mark the waypoints of the MTiles. In the same procedure, they are marked with the name of the Slidewalk they belong to. The waypoint sequence index is internally based on how Tiled writes it, which is sequential anyway, so the C2 loop iterator does this conveniently.

As the MTiles are being tagged as waypoints, they are being added into the InstGroup with a group named after the Slidewalk’s name, which uniquely identifies these series of MTiles for SLG and GridMove. They are added in the order they are looped, so the sequence is still preserved at this point.

MTiles are then further marked with their entry/exit/end values, as well as the custom properties as inputted in Tiled.

When the player moves on top of the Slidewalk, the On GridMove reach target trigger will check whether the player is on an entry point. If it is, a function called CreatePathFromIG is called whereby it takes the Slidewalk InstGroup waypoints and transfers those waypoints to the player’s own InstGroup moving path. CreatePathFromIG does something more, though: it considers the possibility of multiple entry points; it finds the MTile that the player has entered from, and starts retrieving MTile waypoints from that point until the end.

 

 

Limiting the vision

As I asked in my previous post, is limiting the vision of a game due to technical limitations good or bad? Should we look to other tools that would live up to the expectation of accomplishing that vision, or should we just stick with what we have and make the best out of it?

But I think my question touch a more philosophical note in me, and I’d like to write it that way.

Vision?

What do we mean by vision? Let me put it honestly in the argumentative spirit it came from: when someone says they don’t want to ‘limit the vision’, and when they’re relying someone else to actually realise the vision, it means that they want a free ticket to every ride in town, at any time, for any reason at all. What they mean is feature creep. They might want to add stuff in; particles, explosions, heavier models, bigger textures, other bells and whistles. They might want to add a new kind of gameplay because they think it’s more interesting. Or maybe they want replace the whole engine.

They might say they’re expanding the vision of the product. They put it that way because it sounds better than ‘I changed my mind, because I was never really sure to begin with; let’s do it this way instead.’ Better not to look as dumb as they really are, so they make it out they had this in mind all along.

When you work alone, your decisions will echo in your bones, because if you decide to change game engines, you’re passing sentence onto yourself and doing the time yourself. There’s not much room for ego because there’s no one else around to pass the buck to.

But when more people are involved, let say in a thing called the Creative Company, ‘expanding/modifying/altering visions’ becomes a problem because it’s easy. It’s easy for people who are actually detached from the actual creating process to think of ideas. When there are no consequences to your thoughts, you obviously will just think of any shit that comes into your head, right?

So, am I saying, that easy==bad? Sure. Because it’s usually borne in the bed of laziness. easy==lazy. That’s precisely why it’s bad. That’s precisely why it’s not an idea you can depend on to be sound, no matter how clever it sounds now. Some people in the past introduced the argument that it’s helpful for ideas to be free from the burden of the hard toil necessary to achieve it because, said this person, if anything is seen too hard to do, people will likely lose heart and abandon an idea that would have otherwise been brilliant. Fair point, and it explains a lot of great things in the world, like slavery and cotton fields, to name one of many. Despite its seemingly sound reasoning I’ve only heard it from the mouths of those who don’t know or don’t want to dirty their hands to work the fields. I think my reasonable answer to that argument is that if people forego a good idea because it’s too hard, the only thing lost is the elite’s vainglory, and we can definitely lose a bit of that. And, perhaps the idea is simply not good enough to sacrifice what has to be sacrificed. Perhaps the idea is only good for those who have nothing to lose by having it.

Technocreativity

I think that creativity is expressed primarily by the limitations we impose on ourselves. I don’t believe in the propaganda that boasts that you will can express your creativity because new doors/features/tools/technology have now been opened to you. For example, as recent as a few months ago, a Disney-esque Samsung ad declares at the end: “We make what can’t be made so you can do what can’t be done.” Clever and nonsensical advertising, sure enough, but the point they’re making is even more nonsensical. The consumer actually does nothing. All they’re doing is — what’s the popular verb for this nowadays? — consuming a product. Or they’re consuming an experience. You’re made to believe you’re empowered.

So, in the same vein, is technology empowering us to be more creative? No. We are just more reliant on technology to express our creativity. It shapes or forms how our expressions look like, but it doesn’t necessarily improve them; it doesn’t make us more creative. We might have a greater palette to choose from, but excepting those who are obsessed by stats, why should we give a fuck how much greater our palette is? But that is the problem, isn’t it? Aren’t we all us obsessed by stats? Don’t many of us largely concern ourselves with specifications: which has more VRAM, more Ghz, less latency, more bandwidth, read/write speeds, terrabytes, etc. We are sold and are persuaded that we are better off creatively because we have better hardware or software. And I wonder, how should we rank ourselves against the people’s creativity generations before? Should we, given how we’ve technologically advanced, fancy ourselves superior?

And if new technology doesn’t make us more creative, why should we update our progs at all? Because new technology is our medium. We have no choice because we are born to it and to the dizzying rate of how it changes. But it’s no Muse of Creativity. It will not heal anyone of laziness. In fact, it is far easier to be lazy because there are too many conveniences at our disposal. And even the sincere among us are more readily confused because technological possibilities are too narrow a framework to build creative minds.

If technological updates are too fast, it is no sin to stay where you are. If you can keep up, move on to the new.

The only thing the differentiates a good and a bad decision is the laziness not to know the difference.

 

Why C2?

For the past many weeks, I’ve been focusing a lot on the development of the animation sheets. But because of this, I hadn’t touched the former aspects of the game for some time, and when I got back to it, there were some issues that were brought to the fore, such as the Beltway being broken. To be honest, I was utterly surprised at this, since I had no recollection, or notes, that say that it had been broken when I left it to develop the other parts. There were also other things that I noticed that needed changing as I tested the implementation of the animation sheets along with overall player movement.

I found myself a bit overwhelmed and a bit tired, as I sometimes do, from having to debug C2 events. For all the user-friendliness it has, it can still be be quite opaque especially if you’re trying anything abstractly complex. It’s not totally the fault of C2, but because of its lack of modularisation, or object-oriented framework within the event system, picking apart why something doesn’t work requires a bit of jumping around, trying to sort out which are workarounds to some weird behaviour, and which ones are meant to do something actually functional.

Anyway, for several minutes I stared at the monitor, and I was seriously debating why I’m developing this game in C2, and not in Unity, where I have some experience in, and the fact that I’m actually good at coding (at least good enough not to doubt my ability to see the project through). Not only do I code, but I’ve been in the CG industry as a 3D artist and TD for 15 years now; on that alone, Unity is more of a familiar programme than C2. But I had a discussion with my wife who takes these technical diatribes with calmness and puts out good arguments, and the result is my rethinking of my situation.

So the question is: why C2?

It began with the fact that C2 made things simple. But what I’m doing with CITIZEN is not so simple. And that reason seemed to be not good enough.

C2 is fun because of the event sheets. But though the event sheets are effective, they are effective as procedures. They are less fun, and less effective when you want some object-orientation or inheritance, which is so useful when when I started delving into certain gameplay concepts that I wanted to implement for the game.

But I think one of the strongest reasons is one of a balance between a simplistic framework, and the relatively blackbox type of plugin filtered into the C2 editor which results in less bugs during development. This is both a pro and con. Taking Rex’s GridMove/Board/SLG/InstGroup system. These are separate systems working together as one. But it has some learning curve to understand how it all of them work. In fact, some components actually need the other, so they’re really necessary modules in order to come up with something like an isometric grid framework. But once this framework is up, the input and output (eg On reach target, On move accept) is unambiguous, and can interact naturally with any other event/behaviour in C2. Why unambiguous? Well, first, the C2 editor registers the event, so it’s part of the choices. Unity, on the other hand, can be quite confusing in this respect; what event handler is being called?; a search through the documentation is necessary.

In Unity, it is possible to get a well-written framework, but you’re going to have to try it out first before you know what you’re going to get. Sure, that’s the same with C2 plugins, but the C2 plugin framework shields you from some the randomness of 3rd-party scripting. C2 plugins follow C2’s rules in order to play nicely with C2. You’ll spot a lemon faster.

But I think the most important aspect to plugin frameworks is simply that they can be added in without messing things up. I don’t have syntax errors in C2 compared to Unity, so if something doesn’t work, syntax doesn’t necessarily some into the picture. Even data types are managed.

I think the best example I can think of is trying to implement Unity’s 3rd-person controller setup to your own game. There’s so much going on in that package that if you try to fix it to behave how you want it, you’re going to mess it up so much that you might as well write your own to begin with. Unity is so flexible, but unless something is so well written, with open-ended framework development in mind (like, for example, PlayMaker), the Unity environment is pretty much like a sandbox.

C2, on the other hand, is more like a playground. You can’t move the playground that’s there, but you can add other stuff into it, transforming it into something new.

At the end of the day, this is just a hobby so it’s important to have fun. Fun is a major motivator. It’s obviously not fun when I have to deal with the awkwardness of C2, or when I have to refactor my events. It’s not fun when I accidentally change the image-point on one of my images and having no undo option for that. But nevertheless, it’s fun to figure out how implement features on an engine that offers a good starting framework.

Without a doubt (in my opinion, at any rate), it is primitive tool when you consider other production engines out there, open-source or otherwise. I think it is easy to outgrow the tools due to the ever-expanding ideas for a game. That’s partly why I had to limit the ideas that I was coming up with. Is that a bad thing? No, but I think I should like a separate post about that. 🙂

I think, however, in the future, after I complete CITIZEN in C2, I will more seriously consider using Unity for other projects that may require a more expansive toolset, especially one that may yield a better game by going 3D.

MTile/GTile edge definition syntax

Though this syntax was developed many months ago, I had forgotten to document this.

Overview

This edge definition refers to the edges that exists in any GTile tile/sprite. This obviously means that edge definition is a Tiled property of the GTile, which is then passed on in C2 to generate MTile edges at runtime.

Note that a GTile refers to a graphics tile which is a bigger-sized tile. An MTile refers to a movement tile which is smaller and forms the grid of possible tiles to move to. Every GTile is subdivided into equal square MTiles.

Edge definition

Below is an image representing one GTile, and the MTile IDs within it:

For every GTile, there can exist any number of edges. Using this image as an example:

That sprite is mapped out as:

Here, we introduce the edge syntax used to define those edges.

<mtile_id>:<gridmove_direction>[,<mtile_id>:<gridmove_direction>...]

Where mtile_id is the id of the MTile that needs an edge definition (because it is neighbouring a GTile edge), and gridmove_direction is the direction where the edge lies on that MTile specified in the GridMove format.

In the sprite above, the edge definition is:

1:0,5:0,8:3,9:3,10:3,11:3

That is, the MTile ID 1 and 5 have a edges at Direction 0 (east), IDs 8, 9, 10, and 11 have edge at Direction 3 (north). Note that it’s possible to have defined other MTiles, such as:

6:1,7:1

…to replace 10:3, 11:3. It doesn’t matter, as long as the same edge is not specified twice.

Tiled

Note that the edge definition is inputted in Tiled in its tilesheet editor. Currently, there are two Tile properties associated with edge: edge and edge4. edge was the original use, which subdivided each GTile into 2×2 MTiles. edge4 subdivided it further (4×4) giving it a maximum of 16 MTiles per GTile (this depicted in the image above).

In the C2 project, only one type is used and can be interchanged or modified depending on final design choices.

Animation sheet commands – addendum # 1

Some animations are not meant to be played in their totality before allowing the player to change states. For example, when the player is drawing the weapon out, the player is not allowed to move or draw the weapon back in before the weapon has been fully drawn.

No Interrupt

To do this, a no_interrupt variable has been introduced in the animation map which allows a specific animation to have this behaviour; this is transferred to C2 during runtime, of course.

Thus, all player commands are issued through several kinds of ‘intend action’ functions. Currently, they are:

  • PlayerUserCommand – handles the SWAP actions; queries if an animation is ok to interrupt or not
  • PlayerUserCommandDirection – handles directional actions (ie ‘d:#’); it also sees if an animation can be interrupted, and sets an integer intend_dir variable for use with other things in the game.

Animation sheet commands

With the ongoing development in Animation Sheets, I’ve also formed a command syntax to allow flexibility in how actions are sequentially executed, while also being aware of sequence animation.

The flow in C2 is a bit convoluted so I want to document this for future reference.

intend_list

This is the variable that contains a sequence of ‘intended actions’ An example intend_list looks like this:

a:wepi,a:idle,w:as,a:wepo

Where:

<action_prefix>:<action>,<action_prefix>:<action>,...

An action_prefix can be ‘a’, ‘p’, ‘w’, ‘s’. This corresponds with ‘action’, ‘pose’, ‘weapon’, and ‘speed’, respectively. I’ll refer to this as SWAP for simplicity.

Actions can be move, wepi, wepo, shoot, idle.

Pose can be up, dn.

Weapon can be u, as, am, al (this is not yet implemented as of writing)

Speed can be walk, run.

C2 logic flow

Like I said, the flow jumps around.

The first, we start with a given animation. An animation is defined by SWAP, and SWAP are actually something like states. The SWAP is referred to in order to make the right decision which animation is to be played.

So even at the beginning, the SWAP is initialised manually and then PlayerSetAnimation, which is the function that does much of the decision-making is run, and in constantly run.

How it works

At the beginning of PlayerSetAnimation, the first item ([0]) in intend_list is read and then popped. (Since it is a string, it is implicitly tokenised; when I say ‘popped’ I mean that the first token is popped, since it is not really an array, though it is treated as such.)

The popped data is called command. The command is queried if it is an Action. If is not an Action, then the state variables of the SWP is set. For example, if the command is s:run, then the intend_speed variable, which is the internal C2 state variable for controlling actual speed, is set to "run".

One important note is that the intend_list commands continue to be popped until an Action is encountered (so that SWP are processed as state variable changes only).

However, if it is an Action, then it starts querying the SWP variables, then forming an appropriate lookup to the animation map list.

If it is Speed or Pose change then after setting the state variables, it will determine a lookup but uses the nominal animation with changed Pose or Speed variables.

The complicated bit

The complication lay in the combination of the implicit ‘next animation’ of one animation. Some important things to remember:

  • An animation, whether it has a sequence or not, will default to looping itself.
  • In the animation map, a ‘next animation’ can be specified. This makes one animation inherently connected to another.

I’ll demonstrate different intend_list examples and how they are processed, and what goes on beyond the PlayerSetAnimation function.

Example 1

Given an intend_list:

a:wepi,w:am,a:wepo

This is used when switching from a small weapon to a medium weapon, and only applies when a small weapon is equipped.

PlayerSetAnimation

When a:wepi is popped, ‘wepi’ is the intend_anim, which is the keyword. SWP variables are queried (although in this case only the pose and weapon are relevant). Then the animation lookup is set, in this case ‘am upwo’ (medium weapon, standing weapon out).

Remember that the intend_list is popped, so it now looks like this.

w:am,a:wepo

Then the lookup is used to get the seq_start/seq_end, and other variables needed to play the animation. The next_anim and is_seq variables are also populated.

PlayerPlayAnimation

Then PlayerPlayAnimation is called. What this function does is simply play the animation, and starts the playhead at the appropriate place. It doesn’t check anything; it just makes the animation run.

PlayerCheckAnimation

Then there is a On frame change C2 trigger, which calls the PlayerCheckAnimation function. This function checks to see if the sequence as ended. If the sequence’s parameters have been satisfied, it calls PlayerAdvanceSequence.

PlayerAdvanceSequence

PlayerAdvanceSequence is reponsible for determining what to do next. If there is a sequence of animation to be played (ie segmentations of the animation to be played discretely), then this function increments the sequence index. This is important, because as long as there is a sequence to be played, the system will continue to play the sequence.

If however, the sequence has been played out PlayerAdvanceSequence decides several things. First, does the current animation have a ‘next_anim’ name? In this example, the ‘wepi’ animation has ‘idle’ for its ‘next_anim’.

So if there is a ‘next_anim’ name, the next thing is to determine if there are any other Actions in the recently popped intend_list. In our case, yes. Remember our intend_list:

w:am,a:wepo

‘a:wepo’ is still present. So here, nothing special happens. The above intend_list is retained, and PlayerSetAnimation is called again.

Back to PlayerSetAnimation

When it is called again it sees ‘w:am’, and pops that. But we already know that PlayerSetAnimation will always keep on popping non-Action commands (setting variables as commanded by SWP). So after popping ‘w:am’, it sees the final command ‘a:wepo’ and pops that.

When the last is popped, intend_list is blank. But the whole process is repeated: the lookup is made, and then run. At some point, we end up in PlayerAdvanceSequence again, and then from here we ask the same questions. Does it have a ‘next_anim’ variable? Yes: ‘idle’.

Does it have any other Action in intend_list? No, it’s blank. When that happens, the default ‘next_anim’ is prepended to the intend_list (although because it’s blank, it doesn’t matter). Then the intend_list looks like:

a:idle

Then PlayerSetAnimation is called with that intend_list, in which is goes back to idle.

Example 2

Let’s try another intend_list going through the same process:

a:wepi,w:u
PlayerSetAnimation

‘a:wepi’ is processed and popped first in PlayerSetAnimation. Because it’s an Action, a lookup is immediately processed for it. Then the intend_list looks like:

w:u

After the lookup is processed, it is played and then checked like Example 1.

PlayerAdvanceSequence

Eventually we arrive at PlayerAdvanceSequence again when the ‘weapon in’ animation sequence is finished. The ‘weapon in’ animation has ‘idle’ for its ‘next_anim’.

PlayerAdvanceSequence queries if there are still any Action commands in the intend_list. There are none (ie ‘w:u’). So what happens is that the ‘next_anim’ is appended to the intend_list so that the PlayerSetAnimation knows it is the right time to use the ‘next_anim’. The intend_list now looks like this:

w:u,a:idle

So when PlayerSetAnimation is called with that intend_list, it processes and pops ‘w:u’,  which switches the weapon to ‘unarmed’, and then processes/pops the Action ‘idle’. This completes the command sequence for this example.

Example 3

Given the this intend_list:

p:dn,s:run

These are just Pose and Speed changes, so they are considered separately.

PlayerSetAnimation

Pose and Speed commands change the intend_pose, and intend_speed variables. But a lookup must also be made for them in the same way Actions are. But the main difference is that in Pose and Speed, they are popped and processed immediately at the beginning with the setting of the variables. In our example, before a lookup is created, the intend_list would be empty, though the variables have been set (ie intend_pose=”dn”, intend_speed=”run”)

The lookup is made in the same place where Actions are. The main difference is that Speed and Pose uses the state variables to create the lookup. And just as importantly, it relies on the current PLAYER.anim variable to know what the current animation is.

PLAYER.anim

PLAYER.anim is an instance variable in the sprite which contains the ‘base’ animation which specifies only the Pose and the Action. Some examples:

  • If player is idle, unarmed, is standing, PLAYER.anim=”up idle”.
  • If player is idle, unarmed, is crouched, PLAYER.anim=”dn idle”.
  • If player is idle, small weapon drawn, crouched, PLAYER.anim=”dn idle”
  • If player is walking, standing up, weapon drawn, PLAYER.anim=”up move w”
  • If player is walking, crouching, unarmed, PLAYER.anim=”dn move w”
  • If player is intending to run, crouching, unarmed, PLAYER.anim=”dn move w”

Note that PLAYER.anim doesn’t express whether a weapon is drawn or not.

Using PLAYER.anim, we can get the nominal state of movement. Then is a Pose change is required, then the Pose aspect of the PLAYER.anim is modified and PlayerPlayAnimation is called.

The rest goes through the same process again.

PLAYER.do_not_check

A variable called do_not_check is used for bypassing actual checking of frames during OnFrameChange. The reason behind this is because there is not much frame control when I switch animation folders. C2 only allows two options: start at the beginning of the animation folder, or the current frame. But because I’m using it as a map rather than as sequence, I’m jumping from one folder to another, so the frame numbers become arbitrary, based on the animation map. So either frame method is useless in this case. What was actually happening was that the frame being queried as the wrong value; when the the animation folder was switched to, and the play_start and play_end variables were populated (through the values in the map), the OnFrameChange event was triggered. The current AnimationFrame, at this point, is likely to be wrong, since I can only begin at the start, or at the current frame integer (which won’t correspond to a new animation folder). Therefore, I needed a way to prevent the check before I could properly set the frame at the next tick.

To do this, before calling an animation folder, I first put do_not_check=1, then call the animation folder (ie ‘Set animation (start at the beginning’). This has the instant effect of triggering OnFrameChange. But a condition checking the do_not_check variable will bypass it.

Conclusion

The main ideas are

  • intend_list is the the entry point for all animation changes
  • intend_list is popped and data is put into PLAYER.intend_anim
  • intend_anim is used to process what command this is
  • If a command is an Action, is processed normally
    • An animation lookup is constructed from the command itself
    • In addition, the other SWP variables are used
  • When a sequence has played out a check is made if there are still an Action command in the intend_list, to know whether the ‘next_anim’ animation needs to be played out.
    • If there are still Action commands, then the intend_list is fed back in PlayerSetAnimation.
    • If there are no other Action commands, the current animation’s ‘next_anim’ name is appended to intend_list and then is fed back to PlayerSetAnimation.
  • If a Pose or Speed change is made, the intend_pose and intend_speed variables are changed. Then the command is made a lookup using the nominal animation that is currently playing. The nominal animation is found out by referencing the PLAYER.anim variable.

 

Animation and intentions

Intentions

Animation being in sheets, can then be driven by separate variables that describe a few properties of an animation. These properties (also called intentions) are:

  • Action
    • idle
    • move
    • wepout
    • wepin
    • shoot
  • Pose
    • up
    • down
  • Speed
    • walk
    • run
  • Wep
    • unarmed
    • armed small
    • armed medium
    • armed large

Each of these properties are termed ‘intention’, which means that player intends, for example, to have a ‘walk’ speed, an ‘up’ pose, and ‘unarmed’. So when the ‘move’ action is called, it will find the appropriate animation to match ‘walk, up, unarmed’. If a ‘wepout’ action is called, that would not yield an animation, because the ‘intended’ weapon is ‘unarmed’. If the ‘wep’ was changed to ‘armed small’, then the animation will yield the player in standing pose, drawing a small weapon. The speed intention is not relevant here, of course; it’s only relevant when a specific action is needed to query that intention.

Multiple intentions

Multiple intentions are assumed in the system. This enables a number of actions and intentions to be strung up together to make a sequence of movements one after the other. Multiple intention are basically stringing up a series of intentions by commas; eg a:idle, a:move,p:dn,s:walk,w:u. Note the ‘a’, ‘p’, ‘s’, and ‘w’ prefixes which denote ‘action’, ‘pose’, ‘speed’, and ‘wep’, respectively. This specifically switches those intentions .

Note that even in animation maps, there is a key called ‘next_anim‘ which automatically moves from one animation to another. However, multiple intentions are prioritised first, so that if the intentions are written like a:wepin,a:wepout, then animation plays ‘wepin’ first, then ‘wepout’, but it will ignore the ‘next_anim‘ value of ‘wepin’. Since ‘wepout’ is the last intention, it will move to the animation of ‘wepout’s’ ‘next_anim‘, if any.