Gaming Your Way

May contain nuts.

Decision Style #4: Activity-Focused Design

I'm still awe and wonder about what shit comes up when you search for our post titles. This time it's a tech-talk-bullshit gem.

Anyway, it's time to get going with the prototype game and start piecing together all the little tools written so far. I mentioned it on g+ already, but I reached the point where I needed more than my usual ToDo list to keep track of what I want/need to do next.


Just a small part of my "map helper"

The first map I used for testing was quite big and complex and with some 20 rooms and even more doors a bit - shall we say - shit to keep track of. So I invested 2 hours to make a smaller map, then rendered it out and drew my notes on it (making revisions on paper is a bit messy after a while).

Color coded rooms help to maintain overview and layers in photoshop make it incredibly easy to keep track of items to place and doors to connect. Right now I'm placing these in Unity and wiring things up to be able to do a "play through" with the main objectives: find docs (USB drives), codes and key key cards and avoid being caught.

The next thing on my ToDo list however is the minimap, which wont show you the location but items, guards and cameras (and their "senses"). With that in place it's finally time to add security to the map.

Maybe I should add a trigger for the exit first so I can have a "well done" screen up and running (and make it feel more like a game than a pure game mechanics test bed).

-- Oliver / nGFX

Inheritance is your friend.

Let's talk about items in the game prototype (which is actually a bit more than a prototype if I look at it).

One gameplay element is finding keys / codes to open doors (although you can also hack them, but that's another story). In the first version these items were separated into Doors and searchable Items, both with a lot of duplicated code, on the item and on the player object that had to interact with these items.

After everything worked like expected (ie: walk up to a crate, press the action button and wait, find the key, walk to the door and unlock it), I started the second refactoring pass. First thing added was the ItemData class, which holds most of the vital data and exposes them to the editor. ItemData covers basic items and things in the game, a wardrobe you can search through is using ItemData as well as a bed or a crate.


Basic ItemData in place.

I already mentioned that doors are a bit tricky when it comes to making them work with generic code, so adding ItemData values to a door meant some messing. As you can see ItemData contains two fields for interaction: action and secondary (mapped to different buttons) and this also the trouble with doors. Doors (at least in this game) have more than one action, based on the door's current state (locked, closed, etc) plus different actions for the same state. If you stand in front of a locked door, but don't have the key, the action is "hack door" - if you have the key, the action is "unlock door".

Exposing that to the editor is more than a major ball ache, except you want to write your own editor panel - which I very much don't want to.

This is where inheritance comes into play. So the Door (DoorController Class) is inherited from ItemData. The trick is that the code that manages the door's state also changes the "action" of the underlying ItemData. As doors are always work the same way this doesn't have to be exposed to the editor. So when the state of the door is changed the "action" is updated based on the new state. Quick, simple and clean - not as flexible as going through the editor, but that's a small price to pay.

Another (big) advantage is that items and doors now all run through the same code for displaying button hints, tooltips and message boxes, so no special cases to tackle.


For comparision: the door's editor values.

Now that this is done, I'm going to add guards wandering around the map and I fear I also need to get my head around doing a minimap to guide your around (without being a spoiler).

... and with this, "Hail to the king and his epic saga in search for candy".

-- Oliver / nGFX

The bad gains respect through imitation, the good loses it especially in art.

Friedrich Nietzsche.

I doubt good ol' Fritz had "kings" in mind when writing this, but such is life (though, respect for "kings" seem to fall apart lately...) .


Changing from MonoBehaviour to PanelSettings as base class ...

I've started a first refactoring pass today, one of many, as always. I like doing that every time I have a set of working functionalities that I can group together to make things easier to use / handle.

The code above is a part of the class that handles the tooltips in my current prototype game. The PanelSettings class is basically a wrapper for NGUI panels, adding a name and two methods: Show and Hide (which allow for no-brain fade in / fade out).

Together with the PanelManager singleton it makes things incredibly easy. Instead of creating connections via the Unity editor (read: linking panels to objects who might control them), Panels register themselves in the PanelManager to allow for easy access.

To fade in a panel it's simply:

PaneleManager.Show("mypanel");

// or, if you need a callback:
PanelManager.Show("mypanel", CallbackMethod);

As the tooltips need to be faded in out it was a good idea to use what the PanelSettings class has to offer and remove duplicated code from the ActionTooltip class.

... and with this I continue my refactoring saga and have some candy along the way.

-- Oliver / nGFX

I AM NOT YOUR F1 BUTTON!

Code won the game.

This only makes sense if you've read last week's post about FSMs. At some time during clicking and dragging states and trying to fit my idea into the corset of the FSM, I figured I need to write my own actions to make things worthwhile. So I started to write my own action. The problem is once you start writing your own, you need to find a point to stop and expose values to the FSM (that can then fetch these and pass to the next action ...).

With this working quite well, I came to the point where I needed to pass more than a single string or float, but my own class with data (or I had to pass a set of values) and suddenly the beauty of this new toy (the FSM and it's supposed easiness) started to fall apart. Years of coding took over the show, brain cells kicked into crunch mode and in an act of fury I deleted the FSMs from the game. A good deal of hours fiddling and working against instinct paid it's price and code once again won the game.

A few hours later the door control was coded and working, multi language ready, too:

 


Tooltips and control hints up and running and talking to each other via SendMessage, so no links in code are needed.

The interface for using the tooltips is unified and based on "Items" that carry a name, a set of actions and "results" (inspired by classic adventure games). So the Control panel right of the door has the "Item" component attached, it is also linked to the door (to request the door's state). In the image above the door's state is "locked" and the action for the control panel is "unlock door". The result is a new door state: "closed" and a changed action on the control panel "open door", plus the control hint is being updated.


I need more time...

Of course opening a door using a code (or hacking it if you don't have the code yet) takes time, this is also handled by the item, allowing to set a time these action needs to be performed - from 1 sec to a few minutes (it also handles if the time should reset if you leave before the task is performed).

Searching items is done as well, so if you search one of the blue boxes you find the key code for the door and can use it.

Next on the list are guards and the security cameras with these in place the map will be more or less playable and can be dressed for a quick game.

But that's for next week's post.

-- Oliver / nGFX

Strangely addictive

I've been toying with playmaker (a visual state machine) for while now and I'm still not quite sure what to think of it. On one hand it can make things incredibly easy, on the other hand it can be a huge pain in the ass to get things done.

Let's take one of the easier examples: a simple door.


Ingame door, developer art :).

All the basic FSM tutorials consist of the same basic parts: a door, a trigger and a set of animations to open/close the door. Of course you could also code the opening/closing animation... I deal with that later. Let's take a look at the FSM for this door (not as basic, though).


Even in this image there's trouble ahead.

The tutorials get along with four states: closed, opening, open, closing. When the state is closed the FSM listens to the trigger and when it is fired it goes to "opening". The "opening" state simply plays the animation and continues to the "open" state. The open state then listens for the trigger and if fired goes to" closing". Viola.

The problem is that in my case I have some more conditions to check and I want to paste some initial values as well. A door can be open, closed or locked. Additionally, if the door is open, it should test if the should stay open, and if the door is looked which key is required.

The FSM above could be done in a way better organized way, but this is the very first working draft...

...and to be honest, will be replaced by code.

I find a strange attraction in the thought to be able to do things visually and it's always worth a try, but at some point one has to admit that it would be easier to just code it the darn thing. The FSM works great, but it took me a good while to get my head into it - and there we have the result of years of coding: I have an idea how to code this right from "I need a door" and for me it is a lot of work to leave this path and break things down into parts I can built as FSM. This time the FSM lost, it may win next time.

Now with "Realistic-looking flames and colors" ...

... now that's the thing you want to read on an artificial fire log.

Prototyping stage.

I've been hammering out tiles over the last couple of days to be used in the prototype of my pet-project game I'v started. This time I'll try something different and do a very basic prototype first (before jumping right in and doing full fledged 3D models before even knowing if the idea is valid)

Though, I must admit that I couldn't resist and make the tiles just a wee bit more pleasing to the eye than plain grey-boxed ones...


Busy building the dummy map ...

As dummy maps go, this is a fairly bigger one than what I would normally do for level 1, but it's also a testbed for all the things to come. Mainly guards, cameras, locked doors and drawers to search through.

Unlike the usual pointless bullshit I write every week I have something helpful in mind for the next post ... something about culling and exporting stuff ...

-- Oliver

I do not fear computers. I fear the lack of them.

... Isaac Asimov.

I'm not sure if I mentioned it, but MTR ("The Client Edition") went live the other day. Well not exactly. It will be delivered on CD-Rom as client give-away. I'd like to post some screenies of that, but we're not allowed to take credits for the game (but then it also uses slightly different graphics). The AI in the client version is - to say the best - quite basic, basically because the game has to get done (and because no one noticed that the enemies driving like being on rails).

This out of the door I can finally overhaul the AI of the public version, adding some new patterns (like flocking, follow the leader and so on) and moving the AI processing away from each car into a central FSM. This may sound like a step backwards, but in reality it makes things a lot easier when adding flocking). Not to mention that this will also remove one component from the cars (the AI controller).

Anyway, there's some reading involved and a lot of client work to do before I can dive into it. One thing I added this morning, though, is the car selection screen. It's not quite done yet (I need to model the "podest" the car will be placed onto) but here's a screenshot. Oh now that I look at it I also should add the track selection sooner or later ...


Oh, I just see next / prev buttons are missing in this shot ...

... Next week then.

-- Oliver / nGFX

Lessons learned the hard way, part 100

Right now I have next to no time for working on MTR, as it's December - which means that about a million client projects should be finished by the end of the month.

Getting my ass up and continue coding after doing it for 12 hours is, let's be candid here, not easy and most of the time I just want to drop myself onto the couch and play for an hour. Right now I'm playing Assasin's Creed 4 (Black Flag) on the XBox One, which is reasonable fun, if you overlook the fact that the novelty has worn off a bit after 30 games with the "same" theme. But the open world and the the lush caribbean environments make up a lot. I event spent some time just sailing between the small island and hunt down the "hidden" items scattered around the map.

Back to the title of the post, which isn't as unrelated to the post as usually.

In the post about the new HUD I mentioned that I want the thing to be dynamic and showing the order of the cars while they progress through the race. With this idea I coded a little sort function that creates orders the cars according to their position on the track. The idea is simple enough: every time a car hits a waypoint, I store it away and use it to sort, higher waypoint id means better position. As the ids start with 0 after a round I just add 1000 for each lap and that problem is solved, too.

There might be the chance that two cars share the same waypoint and this is taken care of by comparing the time when the cars hit the waypoint making the one with the lowest time first.

The first version of this updated the position of the HUD each time a waypoint was his and espeacially in the early stages of the race this could change quite frequently, causing an ever moving HUD (which was quite distracting, tbh). The second thing that didn't work out like planned (quite logical now, but I didn't see this when I thought of it) is the way the time is displayed. I only wanted to show the current time on the first car and show only the difference to the first on the other cars. Yet again, during the start of the race there is nearly no difference, resulting in changing 1/100 secs, which didn't help at all. I'm going to test if I may stop the time for 2nd, 3rd and 4th car, showing a fixed difference (instead of a running time) to the first car and update it only every odd waypoint. Either that or I drop that and just show the race time ...

No image this time (it would just show the track, 4 cars and the HUD anyway), maybe next week ...

-- Oliver/nGFX

How the MTR car AI works ...

... or should, once I find the time to finish that slog. 

I must admit it was one of my worst ideas to take a stab at a racing game (and sell it to a client). The only luck so far is that it's a "when it's done" deal, so there's no deadline on this except my own wish to get it done and collect some cash for the client version.

The basic idea to do a "quick" little racing game wasn't that bad, but I really, really underestimated the amount of work I had (or rather still have) to poor into it. One of the most time eating tasks still is writing the car AI, which completely didn't appear on my radar until it was way to late.
I've written my share of AI code so far, so I thought this one wouldn't be much different. I think I was wrong with that.

The main big different with what I have written so far is that character can move freely, if something blocks the way, turn left or right and continue moving until you can resume the old path (or just run the a* again to find a new way to the player). For cars this doesn't quite work this way (now that's a surprise).

So cars need to use throttle to speed up and breaks to slow down you need to use steering angles to change direction and you must obey the limits (otherwise it'll looks like cheating). Moving the car along the track is quite easy if you have a fixed track, just draw a spline and let the car move along it. Too bad MTR has a built in track editor, so we need to construct the path for every track. (I covered this in an earlier post).


Waypoints for the AI.

Trouble enters the equation when we add other cars to the track and we cannot follow the waypoints anymore if some other car blocks the path. I calculate the throttle / steering values in steps, blending the values in the end (and then blend the desired values with the current ones).

First step is looking at the waypoints ahead and decide which lane is the shortest (using floating waypoints), then the desired throttle is calculated based on the distance between the last waypoint we've passed and the next. The shorter the distance the slower the car should go, I use 100% for the max distance and 75% for the smallest distance). 

Next step is deciding what angle we turn the wheels in order to head towards the next waypoint and if the angle is greater than the allowed one, we reduce speed further.

After this I have a basic throttle value (say 90%) and a steering value (say 20%, of the max steering angle).

 


Damn obstacles.

Now it gets a bit more complicated, we need to look for obstacles that might block the path we've just selected. The image above shows the problem, the red line shows the desired next spot we want to reach, the yellow line shows the current throttle, with the dark blue line showing the max throttle (but that's only for debugging). Important is the cyan line that is shown at the back of the green car, as it shows where we would hit the green car if we would follow the current path.

Right now, if this happens I offset the waypoint until we can pass the green car (adjusting steering to -20%), but only if there is enough room (there are also checks to see if we can pass on the left or right side of the obstacle), if we cannot pass we need to reduce speed to stay behind it.

Now just blend the values and pass them to the car and repeat this every time we hit a waypoint or another car is close enough to overtake.

If I ever get that video grabber to run I'll post a video next week.

"TV and the Internet are good ...

... because they keep stupid people from spending too much time out in public."
― Douglas CouplandJPod

I wonder what spambots will make out of that.

Anyway, it's Sunday again and time for the weekly post. Problem is, there's nothing even remotely game-related to write about. I spent most of the time coding a client project and let me tell you: writing an e-mail verification system is oh-so-shit boring. Plus keep in mind: never underestimate the user to do something really bone-headed when presented with a simple form.

What's not to understand with "username, 8 to 16 characters, allowed are letters A-Z, numbers, minus or underscore, do NOT use ä,ö,ü,ß"? You know it when you've got the first one on the phone telling you "I can't enter my username - it's 'Hans Müller' " ...

Ok, to make this post just a tiny bit useful: what about using a cloud based sync and Unity projects?

I for instance gave up on the idea to store a Unity project directly in the Skydrive folder (but it's the same for Dropbox, google drive, ... whatever). The problem is that unity creates a shitload of temp files whenever you change code and it recompiles it. For me it worked for some time, but then it choked and resulted in missing files.

But I wanted to sync my current Unity project between 3 computers without using an USB Drive, the obvious and quite simple idea was: use 7zip and a batch file (two actually). I use 7z as it has a nice command line version and it creates a slightly better compression than zip and I can't stand rar.

So when starting to work I hit the "unzip.bat", which deletes the local project  and then unzips the project from the cloud (and starts Unity afterwards). When done for the day it's "zip.bat", which zips the local project and moves it to the skydrive folder. It really is a no brainer, but it saves a good deal of traffic and time - 26mb (7zip) instead of 157mb (uncompressed, ~4500 files) or 38mp (zip) ...

I'm going back to code some game related stuff tonight so at least I have something for next week.

-- Oliver (nGFX)