• Register
Post news Report RSS Leveldesign in Skullstone

In this article, we describe some interesting solutions we've used to create levels in Skullstone.

Posted by on

Skullstone leveldesign main


Level design is one of the final steps of developing a game. It consists in creating maps/terrains and filling them with objects, NPCs, obstacles, enemies etc. Most of such elements is just decorative or are placed to be an easy challenge. They are merely graphical objects equipped with colliders or defined by a specific volume of a map.
However, the remaining ones require a bit more designer's attention (and effort). The opponents must be AI-driven, the NPCs have to have their dialogue lines configured and interactive objects must be properly scripted. We do not hard-code such things. Tinkering with them should be easier than writing the game code and possible for those who do not have access to the code.
Simple dialogue systems allow for organizing texts in tree-like structures with capability to add a predefined function in actual game code to launch. For example, it can unlock a certain feature, give the player a reward (item), grant experience or activate a subquest.

The AI usually concerns more or less complicated algorithms that analyze the situation on the map and decide what action to pursue next. It is often the case, when it is configurable with merely a few parameters such as aggresion level, assignment to a particular group or... ability to cheat (yep, AI often takes an easy route and do not follow the human player's rules). More advanced systems give the option to precisely adapt a particular behavior to a given situation and selecting the parameters of the game environment that influence the bot behavior. A perfect example of such an advanced solution is our Polish system called Grail.
The whole process of making a map and placing objects can be tackled in many ways. If we use Unity engine, then the simplest one is to use the built-in scene editor. It enables to place objects in the world space made from prefabs prepared earlier. Oftentimes, however, the programmers implement their own editor which can immensely boost designers work. This was our approach in Skullstone.

The underground scenes were possible to be created very fast thanks to their specific nature. The map is a grid in which we mark which cells are corridors. The walls, the floor and the roof are generated automatically. In addition, each cell may contain an object (whether it is interactive or not) on the floor and on each of its edges. We click on the cell to select one of the predefined elements from a list, e.g. lever, button, door, spider's web... The structure (representation) of such a map is simple as well. It contains identifiers of object types and their placement - which is - x,y and orientation. What you see on the screen is dynamically created based on such a representation.


During a game, the player may place an object on the floor and we took advantage of this fact when making levels for the game. There was no need to copy an already existing mechanics in the game, so one of the aspects of creating a level is flying over the map in the 'god mode' and placing objects which later can be found by the player. Placing enemies is even simpler - we only have to choose the enemy type from a list and click the respective button in the editor. The mob appears in the cell directly in front of the player character. We can select the mob on the list of all mobs in the current level and set its additional properties such as the group it belongs to or initial behavior (sleep, guard).

Okay, but what should I do to make everything feel "alive" - how to implement the game logic? How to connect the lock and the door? How to make the lever close the trapdoor? The most obvious answer seems to be - write the script for each such behavior. In Unity, we would create and add a dedicated component that will detect whether the switch is toggled on and perform the action on the scene object that is operated by it. Languages such as Lua or Python allow scripting object in a similar fashion. They require their respective interpreters to be loaded by the game engine and properly configured to work with it.

We have done it in much simpler way in Skullstone. Each object has two boolean states: open/closed, on/off, alive/dead etc. We deal with them as we dealt with a two-state machine. The most expected thing is then to transfer the state of an object to another one. As we mentioned earlier, each object is represented as class+x+y+direction, which allows addressing them easily. In order to control a trapdoor by a lever we only need to transfer a state from one object to the other. With this purpose in mind, we have designed our own language to represent this. The basic instruction consists of the adress of the object the action operates on, the type of action (state transfer, lock, timer and so on) as well as parameters. In the case of state transfer, one of the parameters is the adress of the destination object and the value that denotes whether it should be negated or not.


At one point, it became apparent that a script can also be used to set a particular property of an object and not only for operating on states. In this way, we attach texts to the signs on the wall and we configure altars (you will see what they are in the game).
What about if we wanted to define a more sophisticated behavior? What if we neeed to store some data and run a sequence of events? Memory cells are needed and we have them. Each of the two-state objects can be such a memory unit that can accept 0 or 1 value. We hide them in some inaccessible place so the player does not need to see them. Those strikethrough cells are such "memory objects". They have no special logic to them nor interaction, they cannot even be seen! They simply remember the state and can be addressed. A suitable sequence of state-transfer instructions that conditionally lock the state (change of the state is locked if a particular state occurs in another object) has given us the door, which we can open and close only a few times before they are lock for good. Nice trap, isn't it? Especially, if there is a pack of bloodthirsty creatures waiting behind the door.


Some time ago we recorded a video in which we presented our solutions. I think it will be a nice addition to the above article.


I really hope that you will like our unconventional solution and maybe it will inspire someone to create something interesting too. We are eager to read about it.

Frozenshade

Post comment Comments
rit2040
rit2040 - - 61 comments

Hey! Thanks for sharing this article on indieDB.

The functionality of the map editor looks pretty amazing in terms of ease of use and fast iteration.

One thing I'm curious about the editor, is how does it know what object to refer to when connecting a key to a door?

Since you mentioned in the article, when a key transfer its current state to a door, it only needs to know the address of the destination object - which seems to imply every address only associate with one object? If this is the case, I imagine there must be an object manager that can map the address to an object in the scene which has a one-to-one relationship to the key?

Could you confirm this is the case (and perhaps elaborate on that aspect of the map editor?)? If not, I'm curious to know how the map editor handle object referencing between keys and doors.

Thank you again for the wonderful article, your team is very lucky to have a strong programmer!

looking forward to your reply and have a nice day! :)

Reply Good karma Bad karma+2 votes
FrozenShade666 Author
FrozenShade666 - - 6 comments

There was a bug in the translation. We don't connect a particular key to the lock, instead certain types of keys just fits locks of the same type (rusty key can open only rusty lock and so on). The connection and state transfer is defined for two objects, for example for a lock and door. The lock (keyhole) on the location 3,4 placed on the north wall have its constant address [XY:3,4,NO,KH], and that address is used later in the level programming.

Reply Good karma+3 votes
rit2040
rit2040 - - 61 comments

Ah, I see, Thanks for the clarification!

I guess I was overthinking about the key and door mechanics presented in the article and video.

Thanks again for the article, and best of luck on your project!

Reply Good karma Bad karma+2 votes
Guest
Guest - - 688,627 comments

This comment is currently awaiting admin approval, join now to view.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: