Z-sorting has been, in my experiments, been based on the Y value of a given Sprite object. It’s the sorting mechanisms have been specifically explored rather than the logic that they were arranged.
In this post I want to describe an alternate method to sort using collisions.
This method was developed to solve the issue of complex graphical elements. By ‘complex’, this refers to images that are possibly concave, or elongated. In previous z-sorting implementations, the Sprites being sorted needed to remain within a given Tile area in order for the Y position to be specifically determined for that graphic element to be properly sorted. So what I aimed to do is to overcome/get rid of the limitation that forced discrete graphical elements to remain within one Tile.
Consider the image below.
Note the elongated ‘wall’ and the L-shaped wall, both of which implicitly extend beyond one Tile area. The goal here is this: given these irregularly-sized/shaped elements, find a way to determine whether the character Sprite should be sorted behind/above the ‘walls’.
First I considered finding intersections of 2d vectors in which to construct a depth stack, but then thought it was too computationally cumbersome as an initial approach. Furthermore, this required multiple definitions of 2d vectors if, for example, the shape of the ‘wall’ was L-shaped. Note that the bottom edges were the 2d vectors that were being checked against the character’s position and vector which was, in turn, based on the isometric tile ratio, which determined the vector direction to check with. Again, very cumbersome.
So I came upon an idea of using collisions for a check. The logic is simple. If the character’s base position touches (collides into) a wall, it will be set to be behind the wall. This is the starting point and main principle of the method.
The image below describes it in C2’s event sheet.
The main Sprite being sorted is pc, and mover is the collision line. pc is pinned to mover.
The offsets shown in the image above are somewhat arbitrary. What’s important is the principle of the collisions, which are explained below.
The first issue is that the base of the character (a.k.a ‘mover’) must actually be a volume or area, not a single point because the Sprite needs to check for a hit on the full base width of the Sprite.
The red line is the ‘mover’, and is tailor fitted to the width extent of the Sprite that is being sorted. In fact it should encompass the widest width of the Sprite animation. The ‘mover’ is collision-aware, of course.
Second, we have to consider the Y position of the mover in relation to the Sprite being sorted. Ideally, the position should be the bottom vertex of the virtual Tile diamond. When the ‘mover’ is too high, the collision will occur ahead of time and will seem too premature. This offset doesn’t need to be precise as it is based on the specifics of the graphics, and the parameters of the game’s aesthetics.
Third, the ‘wall’ Sprites must have accurate collision polygons; as accurate as you need them to have.
Fourth, since we are dealing with 2d layers, we need to design our ‘sortable’ graphical elements so that they can split up so that can be sorted at all.
So if we were designing a four-sided room, you can encompass it with 2 elements. The first element is the bottom L-shaped wall, and the second element is the upper L-shaped wall. In this way, it possible for the character to appear either in front, or behind, either of these two elements.
The C2 manual references the project file folder which contains other files other than its default.
Even though files and subfolders can be created in this folder, it doesn’t automatically become part of the caproj unless it is actually specified/registered inside the caproj. Inside C2, it is possible to ‘auto-import’ files, but this only works at the root level of /Files; directories aren’t traversed, making this mechanism suitable for single files, like configuration files.
However, when using the Files folder in other ways, such as replacing animation using Rex’s Animation Loader, it would be a monumental task to get all these files in. So I’ve written a Python function that traverses any given folder and writes out a block that can be copied and pasted into the caproj, which is near the end of the caproj. Perhaps in future, I will make the procedure more seamless; right now, the manual copy-paste is for security reasons.
The code below is very unpolished, but gets the idea across.
''' Create a folder structure in caproj/xml format with a given directory
The intention is to create a sprite animation folder in the /Files
project folder, and have that referenced as imported files in the caproj.
The output of this function is to be copied and pasted into the caproj.
gb = glob_buffer()
ext = '.png'
filesdir = 'X:/GAME_PROJECTS/c2/Files/'
unitdir = 'hero_w'
rootdir_name = '%s%s' % (filesdir, unitdir)
rootfolder = CaprojFileFolder(rootdir_name, ext, filesdir)
gb.buffer += '\n%s' %rootfolder.xml
fn = 'c:/outputcaproj.txt'
f = open(fn,'w')
for b in gb.buffer:
for c in folder.content:
if isinstance(c,CaprojFileFolder) == True:
gb.buffer += '\n%s' %c.xml
gb.buffer += '\n\t%s' %c
gb.buffer += '\n</file-folder>'
''' The folder class contains info about the folder, eg content, name '''
def __init__(self, path, ext, rootdir):
# code below considers trailing / separator like c:/test/folder/
# where the last -1 index will contain ''. code below doesn't allow that ''
self.rootdir = rootdir # root for relative path
self.ext = ext # allowed file extension
self.name = [x for x in path.split('/') if x != ''][-1]
self.xml = '<file-folder name="%s">' % self.name
self.path = path
self.content = 
mf = matchfiles_full(self.path,'*')
for m in mf:
if os.path.isfile(m) == False: # folder
newfolder = self.__class__(m,self.ext, self.rootdir)
fl = [x for x in m.split('/') if x != ''][-1]
# check if extension is allowed
relpath = self.path[len(self.rootdir):]
if fl.endswith(self.ext) == True:
fls = '<file name="%s/%s" />' % (relpath,fl)
The class CaprojFolder represents a folder and the contents of the folder (stored in the content list). An element in content may either be another CaprojFolder object, or may be a path to a file. The caproj code snippet is written in a text file in the C: drive (!).
Once this snippet is pasted over to the caproj new folders and subfolders would be created in the /Files folder the matches the one found in the file system. The only major difference is the name of the actual files, which I explain below.
Referencing the files
Though the script mimicks the file system folder structure, C2 does not use these folders as a path to the file. In other words, C2’s folders structure is purely for visual organisation within the C2 editor. The files themselves are treated as though they were in the root directory. Therefore, I opted to name the files to represent their full relative path.
For example, say the script references a file: hero_w/run/000.png
This file is put under /Files/hero_w/run. But it is also named, literally: hero_w/run/000.png, and not just 000.png as you would normally expect. If I had named the file 000.png, there would be no way to distinguish this 000.png with other files in other C2 File subfolders. So a unique name was necessary.
On 2017 08 09, I rendered this for the Slidewalk lookdev.
Last night (early morning) I finished the Sub-Rail lookdev:
Looking back at my basement lookdev:
It’s quite apparent that the style has changed a bit. The environments are a bit different in scale and focus and this is probably one reason why the style has moved places.
Analysing the three renders
The basement scene is the most ‘cartoony’:
Mainly shaded by AO. The AO is strictly controlled on a per element basis. Some don’t have AO at all.
There is an ambient that gives a base cool colour.
Then there are individual space lights.
Parametric shading (eg pipes) is tightly controlled
Textures are made contrasty to enhance the ‘cartoony’ look. Textures are also posterised, and colours are remapped for the ‘cartoony’ look.
The Slidewalk scene is mingling of more GI lighting with forced shading:
AO-like shading is achieved partly by GI solution.
There are also AO shaders where the darkness is emphasised.
Spacer lights are still there.
Apparent soft shadows both from volume spacer lights and AO.
There is still parametric shading but you can see the influence of spacer lights on other surfaces beyond the floor it was originally (ie in the basement scene) intended.
Textures were not posterised or made contrasty. Much of the ‘cartoony’ look is gone.
The Sub-Rail scene is dark and moody, which dictated the techniques I ended up using:
AO for dark areas
Lots of volume spacer lights achieving subtle shadows
Use of spacer lights to reveal texture (eg vertical tunnel lights against bricks)
Some textures were posterised to increase contrast (eg entrance wall)
Specifically chosen textures for floor to reduce textural complexity.
In my Look and feel post, I pointed at things I should avoid, mainly pointing out at the realism of the renderings. But it seems that it’s hard to avoid given the nature of the tools I’m using.
First, the mapping issue. Laying down a map section as I have done in 3d is much easier to visualise than if I went with the 2d route. I think, given my existing skillsets, it would have taken much longer as well. I think it would have produced a different look, probably closer to the pegs shown in the Look and feel post. But having laid it out in 3d, I had started using 3d lights, GI, AO, etc. And this consideratio is ultimately what causes the look to be more realistic than I originally intended.
In the basement scene, the area is small enough that it’s manageable for me to isolate shading against certain lights, and force parametric shading on certain objects. This workflow is closest to a ‘2d’ approach (with the use of Tiled in mind).
In the Slidewalk scene, the area was too big. There were so many elements that needed considering that it would have taken me loads of time to adjust every section of it.
In the Sub-Rail scene, the scale was reduced, and the scene’s mood was darker, hence it was easier to manage even some parametric shading. Nevertheless, I could see I was moving on to the realistic territory here.
In lookdev, I’m trying to sketch out a look. But if I’m starting to sketch (ie quick renderings), I’ll end up using the most efficient tools. And if those tools are geared to producing realistic renderings, then I have to take another step to ‘dumb it down’. For example, it takes more effort to tweak a posterised texture than simply to use the texture itself. In the area of shading and lighting, that problem balloons to the point where it no longer feels like sketching, especially when many elements are being placed in the scene, and/or if the scene itself is large.
The question goes back to the 4 bullet points that was supposed to guide me in the lookdev:
Bright neon lights
Dirty and clean contrasts
Feeling of unease in the environment
With these in mind, what re-considerations can I make?
Certainly, the Slidewalk scene was not as dark as the other scenes. But I didn’t really need everything to be dark. However, the expansiveness of the scene and the flat and dull blueness was overpowering. I think there is no contrast the makes anything pop because everything is lit evenly. There’s no concrete idea of what the sky looks like (whatever is there looking up) because it’s just grey-blue. Perhaps shadowing of tall buildings out of sight? Perhaps shadow movements of overhead cranes or floating cars (if floating cars exist). Also, it must be emphasised that the expansive space helped create the problem of how to light it interestingly. The space caused me to abuse ambient lighting. One lession that I think I’m seeing in the Slidewalk scene is that even if the physical space is big, it can be (should be?) partitioned off using shadows or its inverse, lights. Whatever the case may be, the use of contrast can be used to define major sections within a bigger area.
In the basement scene, the darkness was not literal darkness, but that everything was subdued, and there were only a few sources of light. The littleness of the scene made the dark aspect easy to control, because the partitioning of lightness and darkness was done in small parts.
In the Sub-Rail scene, I think this was the most judicious use of lights given its relative size. I think this scene hits the dark aspect of the game: overall subdued darkness punctuated by slithers of light coming at different angles. In addition, there are volume space lights that partition areas within areas into light and shadow, eg ‘Sub-Rail’ text on car is shown clearly while other parts of the car are in shadow.
I would say that the basement and Sub-Rail scene lent themselves well to the dark aspect, while the the Slidewalk scene was naturally more problematic. And what I’ve learned is that if I’m doing a large space, then I must compartamentalise that space more.
Bright neon lights
In the same manner as above, the more contrast the scene has, the more bright neon lights can be successful. The neon lights give off a sense of civilisation when everything else is hidden in unknown. In the Slidewalk scene, everything seems exposed, so that nothing is really that hidden. This makes it weak even in this respect.
Dirty & clean contrasts
We can think of dirty and clean contrasts in one scene or the comparison of moving from one scene to another. The Sub-Rail scene is a dirty scene, mainly. The dirtiness relates to the brick, the trash, the chairs, and cautionary markings. The cleanliness relates to the lights (perhaps there should be broken lights?) and screens, the ticket machine.
Even in this respect, I think the Slidewalk scene fails, even though there are trash on the floor and the bricks are used as textures. I think there is too much brick everywhere that the dirty/clean contrast can shine. The floor, possibly, contributes to these non-contrastiness. However, the Slidewalk flooring itself, is stark and pops out, which is probably the best thing in the scene in terms of dirty & clean contrast.
As mentioned, feeling unease might be more to do with gameplay, or even sound, but I think it’s worth a try to challenge myself into thinking about this more clearly in respect to lookdev.
I think the use of propaganda is a way, but that requires specific propaganda messages to come through, which means a specific design. Now, the question would be whether ‘feeling unease’ can come through in terms of lighting and shading and use of space.
Possible devices to use to cause unease:
a flickering bulb
pulsating lights (SCAMs?)
unseen depths or corridors (Slidewalk scene, big drop)
Though I’ve already done a bit of art for the game, I’ve honestly been fluffing about this one for quite some time. I didn’t have a definitive look and feel so i’m setting one up now.
I had been discussing with B about how to really get into terms with this. I think my main problem, which includes the art I’ve already made, is that the inspiration came from simply an attraction to a particular artwork (in this case Gray Shuko), but had nothing to do with the theme or the feel of the game itself as I want to depict it.
This is 2400 AD:
Playing it when I was but a babe, I can summarise the overall feeling in several points.
Bright neon lights
Dirty and clean contrasts
Feeling of unease in the environment
Note that while certain feelings are not readily expressible from a lookdev perspective, it helped to focus on the overall emotion/feeling I was having when I played the game, because that is the sort of thing that motivated me to develop CITIZEN. Perhaps what seems unrelated might actually be useful when the time comes. Who knows?
As B pointed out, the darkness comes from the black background, and the ‘neon’ probably comes from the magenta-coloured gui frames.
The ‘clean’ is obvious, because the graphics are simple. But I think I there was a ‘dirtiness’ in line with what was going on as a game, so that there are those contrasting elements, especially when going from one area to another (eg entering a room from exterior).
The feeling of unease is harder to track down, and I think that’s more of a gameplay issue, at least from how it looks like right now. I think however, that it will also inform other design aspects, like how the robots will look like, and perhaps even how sound, or the writing is done.
With those 4 points guiding me I scoured for anything in the web for indicators that may give me an idea how to hammer home those things. The thing was, I didn’t necessarily look for the most beautiful images, because I knew that beautiful images were stuffing up my efforts to have an actual theme to the game.
Good examples and why
In respect to the Good Examples shown above, the most use of lighting was the picture of The Last Night which features area lighting, where one portion of the screen will affect a surface’s diffuse colour. This is fine as long as no shadows are implicitly cast (ie by use of explicit harsh lighting).
Note that even when neons were used in the abovementioned image, the overall effect was still diffused; as it is pixel art, there’s an acceptance of these compromises. The artwork that I have to develop should also have the same acceptability using those compromises.
The image shown on the right is probably the kind of character proportions/shape that I want. This means it is realistically proportioned, but not particularly realistically shaped or accurate.
As described in Rex’s own site about GridMove.Direction, this expression is only valid if the movement is to a neighbouring tile. In the Slidewalk system, however, although the construction of the Slidewalk path may be straight, the target tiles may be separated from each other for efficiency sake (ie less node paths to draw in Tiled).
It is inefficient to ‘connect-the-dots’ in C2 by tagging the tiles that the path goes over, so I’m instructing GridMove to move to the Slidewalk target tiles that are not neighbours, This makes GridMove.Direction invalid.
What I did, instead, was to do my own GridMove direction by comparing GridMove’s SourceLX/Y and DestinationLX/Y expression and generate a direction using conditions.
At this point, however, the conditions assume that the nodes are positioned in a way that it follows the lines for 8 directions, as it checks how the target tile’s LX and LY are different for the source’s corresponding LX and LY.
Commands are processed in the order they appear in the intend_list. However, there are 2 types of commands: state, and action. State commands change the sprite variables, and Action commands both change sprite variables as necessary, and also change animation (that is, generate a new lookup).
Directional commands (d:#) are the only State commands. They change the seq_dir variable but they do not issue a PlayerSetAnimation implicitly. It allows for other situations to do that.
NPC animation sheets
NPC animation sheets were created alongside Player animation sheets and they are essentially copies with the main exception that all animation functions as described in the original document have an additional argument passed on to them, which is the UID of the sprite being changed.
In animating Player, this was easy because all references to Player was the same. With NPCs, it’s made so that it can accommodate changing animation across multiple sprites.
NPC animation maps
In the same way, NPC animation maps have been created on a per-NPC basis. Each NPC will have its own animation map named according to the name of the NPC.
NPCs in Tiled
Tied with the animation sheet system, the Facing parameter in Tiled, like the one for the Player, orients the NPC at load time (ie modifies the direction).
Details of how NPCs and other entities in Tiled are defined will be covered in later topic.
The Slidewalk system, taken from 2400AD is implemented by using several components in Tiled, and hooking them up in C2.
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 swpathObject 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.
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.
Knowledgebase for creating a Construct 2 isometric game.