Skip to content

Archive

Tag: Game Programming

This is a rough introduction to the Bullet physics engine.

I keep this article for reference. If you want a clean setup for building bullet as an iOS ready static library, please check this post.

You’re crying out for a physics engine. You wanna make one of these gimmicky, fashionable and ever profitable physics puzzlers, right?

If it’s a 2D engine you’re after, try Box 2D (<= links to a pocket gamer article!), it’s free, worth millions to some.

I sweated over my laptop 4 hours putting this together. It’s not so very difficult, but it’s summer and it’s HOT.

The bigger they come… (1.30 pm)

I decided to have a shot at building Bullet for a start.

Downloading the source isn’t a problem; the archive itself is… …varied. Lots of stuff at the root level, and a promising ‘src’ folder.

I approached the “build issue” (with a pig) head on. I made a new project in XCode, added all files from the src folder, and started building.

Don’t do this (use => this article instead.)

I removed many things that stood in the way:

  • “imbsdk”/ folders
  • make files

Then the build started bumping on #include “foo/bar/file.h” style commands. Makes sense, because adding all the files the way I did puts all files ‘at the same level’, so there’s no need for a ‘foo/bar/’ unless you expect a collision. I didn’t expect a file-space collision, and I was right. Yea, I moronically search-killed all foo/bar/ sections from all files.

There’s another couple of libs I didn’t like. The multi-threaded library trashed the build and doesn’t seem that promising just yet. There’s also the mini-cl thing. I dunno what that is except it uses the multithreaded lib headers, so I unchecked a couple more files from that mini-cl thingy.

Yay. I built bullet, I haven’t read the docs or anything yet. I don’t even know if the build is any use to nothing since I trashed stuff on my way. But hey, it’s building, right?

Surgery time: ~35 minutes

Examples, any? (2.45 pm)

I searched for sample code in the archive. Maybe what I was looking for was a quick ‘falling ball world’ with stdout logs to explain what’s going on. I know it’s unpretty but it does away with the need to show things and I didn’t expect GL-ES ready code in the demos either.

Then I found:

  • The Bullet user manual [link now fixed]
  • Their hello world tutorial.
    (please have a look, the remainder of this article is about getting this example to compile and run in an Objective C environment).
  • This (although ‘it’ may be out of date).

They do have an article showing how to include Bullet in an MacOS-X project. It looks like it would take no more than 10 minutes. I skip-read that it causes a framework to be generated. I may be wrong, but I don’t think we can include any framework other than what ships directly with iOS devices. We need static libraries, right?

Clock ticking (3.45)

I did nothing impressive in the past hour. I pasted the example from that hello world tutorial (link, above) in a char testBullet() function. I put that in a *.cpp file, not just wishfully but because the includes won’t compile as C (well, it is C++ code – and by the way that didn’t work, see below… ).

Surely I made a sample project to contain this stuff. Any project template will do, just for a test. So I created a project, pasted the code in test_bullet.cpp which contained this char testIt() method (don’t google these names, follow the link to the tutorial instead).

Calling this cpp code from an objective C class caused a linkage error, leading to a shocking discovery.

Interlude

I like the hello balls or watchamacallit example A LOT. Because it has this line in it:

std::cout << “sphere height: “ << trans.getOrigin().getY() << std::endl;

For whatever obscure reason this great classic glitched my build. So I substituted a greater classic:

printf(“sphere height: %f\n”,trans.getOrigin().getY());

Overall the test didn’t go too well, expect it worked (ha!).

I muddled endlessly over the “btBulletDynamicsCommon.h” include. This includes this, this and that, and cutting and pasting the include folders into Build Settings > User Header Search Paths takes AGES (If you now a better way, absolutely please leave a comment after this post)

The shocking discovery

I started considering mournfully how I don’t really know how to bridge C++ and Objective C. Then I found a doc claiming that…

you can dump whatever C++ you want in an Objective C file as long as you substitute .mm to .m

Not believing it one bit, I renamed my .cpp file to .mm; wrapped my test code as a class function, like this:

#include “TestBullet.h”

#include <stdio.h>

#include “btBulletDynamicsCommon.h”

@implementation TestBullet

+(BOOL)test{

// Welcome to C++land.

// Paste here sample code found at the bottom of that page.

// …

// … what are you waiting for?

}

@end

Supernaturally, this compiles without twisting a nail.

Understanding is nothing (4.42pm)

The world is unfair. I picked bullet first because of all the hype surrounding it (July 2011).  I’m sure there are many decent physics engines out there, but that is that.

Other than producing a nasty project file that can build the library, I know nothing about Bullet physics. Feels good, in a kind of way.

Looking at the fine print below – a mantra much simpler than the sample code – there’s a couple of things we can infer:

  • There is a ball. Likely, a solid thing. That’s a bit cheated from the console output but let’s believe it.
  • There is gravity. Gravity’s good.
  • There is a ground abstraction. Ground is good.
  • No input file or likewise cheat. Everything is setup programatically.

Their sample code is 65 lines long. Didn’t expect anything better (sigh). For better and for worse, the tutorial explains this step by step. Maybe I’ll understand if I set a few hours aside to read it.

Attaching to process 2589.

sphere height: 49.997223
sphere height: 49.991669
sphere height: 49.983334
sphere height: 49.972221
sphere height: 49.958332
sphere height: 49.941666
sphere height: 49.922222
sphere height: 49.900002
sphere height: 49.875000
sphere height: 49.847221
sphere height: 49.816666
sphere height: 49.783333
sphere height: 49.747223
sphere height: 49.708336
sphere height: 49.666668
sphere height: 49.622223
sphere height: 49.575001
sphere height: 49.525002
sphere height: 49.472225
sphere height: 49.416668
sphere height: 49.358334
sphere height: 49.297222
sphere height: 49.233334
sphere height: 49.166668
sphere height: 49.097225
sphere height: 49.025002
sphere height: 48.950001
sphere height: 48.872223
sphere height: 48.791668
sphere height: 48.708336
sphere height: 48.622223
sphere height: 48.533333
sphere height: 48.441666
sphere height: 48.347221
sphere height: 48.250000
sphere height: 48.150002
sphere height: 48.047222
sphere height: 47.941666
sphere height: 47.833332
sphere height: 47.722221
sphere height: 47.608334
sphere height: 47.491669
sphere height: 47.372223
sphere height: 47.250000
sphere height: 47.125000
sphere height: 46.997223
sphere height: 46.866669
sphere height: 46.733334
sphere height: 46.597221
sphere height: 46.458332
sphere height: 46.316666
sphere height: 46.172222
sphere height: 46.025002
sphere height: 45.875000
sphere height: 45.722221
sphere height: 45.566666
sphere height: 45.408333
sphere height: 45.247223
sphere height: 45.083336
sphere height: 44.916668
sphere height: 44.747223
sphere height: 44.575001
sphere height: 44.400002
sphere height: 44.222225
sphere height: 44.041668
sphere height: 43.858334
sphere height: 43.672222
sphere height: 43.483334
sphere height: 43.291668
sphere height: 43.097225
sphere height: 42.900002
sphere height: 42.700001
sphere height: 42.497223
sphere height: 42.291668
sphere height: 42.083336
sphere height: 41.872223
sphere height: 41.658333
sphere height: 41.441666
sphere height: 41.222221
sphere height: 41.000000
sphere height: 40.775002
sphere height: 40.547222
sphere height: 40.316666
sphere height: 40.083332
sphere height: 39.847221
sphere height: 39.608334
sphere height: 39.366665
sphere height: 39.122219
sphere height: 38.874996
sphere height: 38.624996
sphere height: 38.372219
sphere height: 38.116665
sphere height: 37.858330
sphere height: 37.597218
sphere height: 37.333328
sphere height: 37.066662
sphere height: 36.797218
sphere height: 36.524998
sphere height: 36.249996
sphere height: 35.972218
sphere height: 35.691662
sphere height: 35.408329
sphere height: 35.122219
sphere height: 34.833328
sphere height: 34.541660
sphere height: 34.247215
sphere height: 33.949993
sphere height: 33.649994
sphere height: 33.347218
sphere height: 33.041660
sphere height: 32.733326
sphere height: 32.422215
sphere height: 32.108326
sphere height: 31.791658
sphere height: 31.472214
sphere height: 31.149992
sphere height: 30.824991
sphere height: 30.497213
sphere height: 30.166658
sphere height: 29.833324
sphere height: 29.497213
sphere height: 29.158325
sphere height: 28.816658
sphere height: 28.472214
sphere height: 28.124992
sphere height: 27.774992
sphere height: 27.422215
sphere height: 27.066660
sphere height: 26.708326
sphere height: 26.347216
sphere height: 25.983326
sphere height: 25.616659
sphere height: 25.247215
sphere height: 24.874992
sphere height: 24.499992
sphere height: 24.122215
sphere height: 23.741659
sphere height: 23.358326
sphere height: 22.972216
sphere height: 22.583326
sphere height: 22.191660
sphere height: 21.797216
sphere height: 21.399994
sphere height: 20.999994
sphere height: 20.597218
sphere height: 20.191662
sphere height: 19.783329
sphere height: 19.372219
sphere height: 18.958330
sphere height: 18.541664
sphere height: 18.122219
sphere height: 17.699997
sphere height: 17.274998
sphere height: 16.847219
sphere height: 16.416664
sphere height: 15.983332
sphere height: 15.547221
sphere height: 15.108333
sphere height: 14.666666
sphere height: 14.222222
sphere height: 13.775001
sphere height: 13.325001
sphere height: 12.872224
sphere height: 12.416669
sphere height: 11.958336
sphere height: 11.497225
sphere height: 11.033337
sphere height: 10.566670
sphere height: 10.097226
sphere height: 9.625005
sphere height: 9.150005
sphere height: 8.672228
sphere height: 8.191673
sphere height: 7.708341
sphere height: 7.222230
sphere height: 6.733342
sphere height: 6.241676
sphere height: 5.747232
sphere height: 5.250010
sphere height: 4.750011
sphere height: 4.247234
sphere height: 3.741679
sphere height: 3.233346
sphere height: 2.722236
sphere height: 2.208348
sphere height: 1.691682
sphere height: 1.172238
sphere height: 0.650017
sphere height: 0.720013
sphere height: 0.787232
sphere height: 0.851673
sphere height: 0.913337
sphere height: 0.972222
sphere height: 1.028330
sphere height: 1.081660
sphere height: 1.132212
sphere height: 1.179986
sphere height: 1.224983
sphere height: 1.267202
sphere height: 1.306643
sphere height: 1.343306
sphere height: 1.377192
sphere height: 1.408299
sphere height: 1.436629
sphere height: 1.462181
sphere height: 1.484956
sphere height: 1.504952
sphere height: 1.522171
sphere height: 1.536612
sphere height: 1.548276
sphere height: 1.557161
sphere height: 1.563269
sphere height: 1.566599
sphere height: 1.567151
sphere height: 1.564925
sphere height: 1.559922
sphere height: 1.552141
sphere height: 1.541582
sphere height: 1.528245
sphere height: 1.512130
sphere height: 1.493238
sphere height: 1.471568
sphere height: 1.447120
sphere height: 1.419895
sphere height: 1.389891
sphere height: 1.357110
sphere height: 1.321551
sphere height: 1.283214
sphere height: 1.242100
sphere height: 1.198208
sphere height: 1.151538
sphere height: 1.102090
sphere height: 1.049864
sphere height: 0.994861
sphere height: 0.995889
sphere height: 0.996711
sphere height: 0.997369
sphere height: 0.997895
sphere height: 0.998316
sphere height: 0.998653
sphere height: 0.998922
sphere height: 0.999138
sphere height: 0.999310
sphere height: 0.999448
sphere height: 0.999559
sphere height: 0.999647
sphere height: 0.999717
sphere height: 0.999774
sphere height: 0.999819
sphere height: 0.999855
sphere height: 0.999884
sphere height: 0.999907
sphere height: 0.999926
sphere height: 0.999941
sphere height: 0.999953
sphere height: 0.999962
sphere height: 0.999970
sphere height: 0.999976
sphere height: 0.999981
sphere height: 0.999984
sphere height: 0.999988
sphere height: 0.999990
sphere height: 0.999992
sphere height: 0.999994
sphere height: 0.999995
sphere height: 0.999996
sphere height: 0.999997
sphere height: 0.999997
sphere height: 0.999998
sphere height: 0.999998
sphere height: 0.999999
sphere height: 0.999999
sphere height: 0.999999
sphere height: 0.999999
sphere height: 0.999999
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000
sphere height: 1.000000

Wondering why I’m pasting a huge list of bugs on my blog? So I can analyze later. Don’t feel obliged to read this… …seriously.
For now I’ll just underline common pitfalls (if I spot anything like it, that is) – stuff that other iPhone/game devs mightn’t want to forget.
Maybe I should get myself a bug tracker.
Second Round
  • Remove crap showing on the edges of the screen (recheck all versions)
  • remove/disable assert
  • remove/disable NSLog (move to ‘build checklist’ article)
  • remove NSZombieEnabled flag (move to ‘build checklist’ article)
  • When restarting the game after end scene, controls are disabled (fixed in [Game setup])
  • ‘Tell a friend’ feature
  • Added a user feedback section
  • Created an online page with game credits and other info
  • Adjusted viewing distance per device (iPad increases minimum and maximum viewing distances)
  • Camera is now more dynamic
  • Arranged game help for iPad/iPhone4
  • Fixed main menu button hiding credits on iTouch
  • Locked the maximum angle on iPad for people using it flat
  • Created game covers for iPad/iPhone 4
  • Arranged welcome screen layout for iPad
  • Crash logs
  • Added copyright notice
  • Updated game name on home screen, ‘tell a friend’ email body, end credits, require game name;
  • Created a mail box for support
  • Cleaned bundle (remove all unused sounds; all unused images; anything else that seemed unused.
  • Removed rendering artifacts near the bear’s cave.
  • Crow did not seem to die anymore; maybe the delay was too short
    => if the armature is in edit mode in Blender, animations do not export correctly; should unselect/exit
  • Improve elder model transparency (tips and tricks)
  • There is still a hole in the landscape at wood cutting unit, and this unit still has no walls
  • Notice for large crow sighting appears even if they have disappeared (because the notice was attached to terrain transition)
  • Still had errors with status ailments when playing later chapters (???), likely after KO status ailments were reset.
  • Wrong caption when dying by status ailment
  • Removed broken bridge caption (because there was no broken bridge anymore
  • Increased length of play flute animation to match sound
  • Stag can react to flute multiple times (but shouldn’t)
  • Increased stag reaction range with flute
  • Incorrect captions to wait for cat (ensure new dialogs are in)
  • Made bear passive owing to various bugs (sigh)
  • Starting with chapter 3, we get music for chapter 2.
  • Mouth of white sheep is see-through- meeting the bear causes an exception
  • Had to pick the crystal twice (backwoods). Maybe this doesn’t get reset correctly
  • Made alter disappear after picking sword
  • Character (PC) was over-released every time we restated the game (zombie)
  • At single house, could improve circular, wooden area. Made the ground under the house simple to save poly count.
  • At woodcutter’s it was hard to get off the table trunk
  • Scenic node for the square platforms in industrial complex.
  • Crow feet polygons not face up.
  • Changed some allocations to realloc
  • Fixed guard condition in mesh tiling cell access
First Round
  1. Fix compile time warnings.
  2. Once on the red platform the bear may block the way back (reduce home range).
  3. [cannot leave without kitty] should not show before the platform is ready to move.
  4. At the end of the game remove ‘press home button’
  5. When the avatar dies, indicate the reason.
  6. When KO and go to home screen, then go back, PC is still KO and cannot wake up.
  7. It is hard to cross the bridge.The cat has a hard time following, e.g. crossing the bridge (teleport)?
  8. release resources when memory warning is received.
  9. When playing dialogs, distance between actors may be too short, and an actor may disappear.
  10. Add a transition effect (fade in / out) between stage 1 and 2
  11. UI controls may become inactive when holding a finger over the phone (single/multitouch issue)
  12. Returning to marine fields from stage 5 music should change back
  13. At the end of game there is a bug with dialog looping over
  14. While avatar walks, forearms are really rigid.
  15. There are no sheep anymore.
  16. We can see the back of the crow’s head through its mouth (single sided polygons)
  17. Use a scenic on last level – this will prevent the player from walking to the wrong place and will also allow the mice-men to follow a nice path.
  18. With fighting, hits have no effect (intermittent) – Because actors became tokens, between the time they were created and the time the fight started, they didn’t get a chance to setup their ActorAffectCondition caches
  19. Introduction dialog for Klinnburg chapter may be missing.
  20. Add wings to black angel
  21. Crow right eye doesn’t show
  22. Make crow beak double sided.
  23. Close gaps with stumps and terrain in wood cutting area
  24. Limit walkable area at circle house
  25. Close access behind/around bear cave.
  26. Limit walkable area in klinnburg so player won’t step through the ledge.
  27. In Klinnburg player feet are inside the ground.
  28. Fix neck of PC
  29. Close gap in characters’ mouth and make it a slightly darker color.
  30. On first level, white lines go out of the tile boundaries. not pretty.
  31. Can get stuck on top of tubes (level 5). change shape of tubes.
  32. Add a message after picking food/drink to confirm. likely, add a message after crow dies e.g. not just i feel hungry, but also i feel weak etc…
  33. Crash when hitting a sheep (missing assets)
  34. Crash when hitting the shepherd
  35. Review names of protagonists in dialogs.
  36. Recovering from KO, english text (‘wow, that was bad’) in chinese version.
  37. After KO, status ailments recur
  38. Make item pick dialogues blocking.
  39. After KO, player is sliding
  40. After second KO, player not teleported to checkpoint
  41. One sheep atop the shepherd house.
  42. Shepherd attacks without preamble.
  43. Make a separate field for sheep (? no – exclude the following: ew_bsh, coral_unit, shepherd_house
  44. With pick motion, object is picked at beginning of move, with dialogue insert (heavy device) this doesn’t look good.
  45. Detect iPod/iPad/iPhone to enable antialiasing
  46. Player and cat can get stuck together.
  47. Make a sound map with default volume levels
  48. Ensure translation bug with bridge discovery is fixed.
  49. Status ailment translations
  50. Add chapter unlock notices (feature)
  51. Add dialogue/quote skip (feature)
  52. Add translated names of actors for non player quotes (feature)
  53. Fix visibility bugs.
  54. Chapter unlock is incorrect. need several conditions to unlock a new chapter.
  55. Fix navigation controller (allow orientation to change even if finger outside the controller area)
  56. After restarting, UI feels unresponsive; try hide UIView instances; don’t remove them.
  57. With stage 3, apart from large crow disappearing, village is unchanged with all dialogues and items in place, that’s kind of wrong.
  58. When opening a stage, remove all previous scene discovery talkback.
  59. Reload translation when restarting the game (because of removed translations while playing, see above)
  60. Start village music immediately when entering village from north side.
  61. Deer sighting not correct anymore because there is no ref to deer for dialog.
  62. Character is at the wrong place moving from stage 1 to 2.
  63. Player disappears when using axe.
  64. In level 3, alter dialog breaks and the game freezes (dialogue was missing; fixed with assert)
  65. ‘main menu’ item in overlay view should be translated
  66. Missing translations on main menu
  67. Play icon in overlay view.
  68. Stag sighting appears (with technical platform label) when reaching scrap heap. weird.
  69. There is steady, unavoidable memory consumption(?) (even though that’s not ‘live’ memory). what happens when this reaches a really large number? do things get sorted or not? YES; this is not memory consumption, just overall usage since beginning of session.
  70. Don’t let the camera look from under. this will result in ugly stuff showing in many stages. Later i want to allow the camera to look from under when climbing a hill, this requires more kit.
  71. Remove all uses of onMessageEnd. This is now perfectly unsafe given it is language sensitive.

In space, nobody will hear you geek

There you are. I’ve just completed core gameplay programming for the first episode of… well… let me think up a name for it.

This looks like a small adventure/action game in real time 3D. Core gameplay can be summarized as follows:

  • 4 player actions
  • 9 different NPCs
  • 6 items to collect
  • 5 stages
  • dialogues, here and there, to orient the player towards the right solution

There’s a lot to get the game done, but I still hope this won’t spread over more than another month (part-time). What do I leave out from ‘core gameplay’?

  • Bugs. Many bugs.
  • Non-committal gameplay elements – more dialogues here and there, minor NPCs without complex behavior and so forth. Non-commital gameplay elements are important. That’s added value on my game. But one needs something they can add value to, right?
  • Game tuning – mostly timing / measurements related.
  • Prettier rendering
  • Production level artwork
  • Decent Sound

Technical Design, Methodology

Given the above, you bet I didn’t bother with either of the following:

  1. Getting stories ‘done done
  2. Unit tests
  3. … other nice things

Why? Is the nice, agile stuff not nice more? Why is missing the ‘done done’ over and over so irritating while doing team work, but somehow left out when geeking in a small studio? Do unit tests not warrant code quality and so forth? Or is it just that it’s faster to do without all this stuff?

Here are a few things I surely didn’t leave out:

  1. Story driven. Even though I had to build an engine before I could even get to thinking of a game, I’ve kept a steady emphasis on getting valuable items done.
  2. Separation. Didn’t write big classes, usually separated state from behaviors in the core model, defined a pseudo scripting interface (short of a scripting language integration) atop my game engine. Did write a small engine versus just a game, and so forth…
  3. Integrated workflow. I arranged that I could update my assets in one click. No muddling through with cumbersome export procedures. The same goes with poor code and documentation. Poor code might be OK for a while. Poor undocumented code is worth nothing. Good, undocumented code doesn’t generate integrated workflow – not knowing how the code works doesn’t flow much faster than no code, or code that doesn’t run to spec.
  4. Optimization. I did follow the time honored (ignored?) command: do not optimize your code… …unless you have to. So I always wrote first the simplest code that did a job, and faithfully relied on a profiler to help me frame-rate on a powerful, yet mobile platform.

Where is Attitude?

In a well funded team project (as in, bucks per hour), earning money needn’t be the primary motivator, but in the long run, there is no clear ‘finish line’ for your staff. They will work on and on, and get happy to get stories done done – work well done completed every one two three weeks.

In a solo project such as this one, or a small, non funded team project, getting the product done is the primary goal for the team, or the ghost out the machine. Psychologically, having half of the product done done half-way through feels much tougher (at least to me) than having the product about half-done half-way through.

So I what felt like was, OK, if I just sit and polish every detail one by one, I’ll be overwhelmed by the task at some point, whereas by attacking frontally every sizable, unavoidable task, I’d get to a point where it would be such a horrible shame not to finish that I’ll definitely manage.

Poor code, OK design

Do you remember your first game engine? Nothing to be proud of except getting a job done, right? Same here. Dead code, flawed code, even deep flaws in the design here and there, but it sort of works, and I would argue that’s enough.

This is what refactoring is for, and to some extent this is the (maybe illusory) benefit of strong ownership and solo programming. It’s much easier to get the product done and defer refactoring if you’re soloing. After all, if you don’t get any benefits from the product, you might just as well give up the code. I felt it was OK. On the other hand, really badly design code with large classes, no separation of responsibilities and no usable game development API, that didn’t seem tractable. Not only it could be impossible to refactor, it would have made finishing the product draft prohibitively hard.

I look forward to refactoring this stuff. In particular, I look forward to doing it while fixing the bugs. If a bug is impossible to fix with my current code, then there’s a case for redesign.

Docs are up

There’s been a point where I felt this stuff was so messy I’d never get out of it. Then I managed to delete a lot of dead code, crossing fingers every time that the code was really dead since I didn’t fancy getting it back from the attic, not even the recycle bin. That I mostly threw away whole classes rather than code fragments brings the point home regarding separation.

Then there was a point when I started designing the game, and I felt like throwing half of the working code away, because the code didn’t seem to support the design. So I did the unholy thing that you can only do if you control both the technical and artistic design – Instead of throwing my stuff away, I documented it, putting on top what seemed to actually work, and tried to redesign my game around the highest confidence bits of functionality. Finally this helped improving stuff that could be used in a design empathic to the engine, and throw stuff that was neither necessary, nor stable.

Looking at my docs, I’m reminded of a harsh, fun comment about docs masking poor code. But actually, even docs over poor code can be better than good. If code is bad enough that it can only be used in one or two well documented ways, that’s still functionality I can flow with, never mind indexing essential behaviors that should be maintained and improved, versus stuff that needs to be forgotten and deleted.

Spaghettis, Meatballs and a Haircut

In the next days, I’ll try to tend this blog more often. Because I’m gonna fix this mess, and hope to find design insights worth sharing along the way.

Until then, let’s write dirty code. Or if you can’t, get help from above.

Yea I know… screenshots… another time.

Well, I was supposed to provide better support for rendering ‘dummies’ – non-commital symbols representing game actors, to be used in game prototyping so we’re not tied to producing assets whenever we need to declare new characters and props.

Today I’m integrating realtime 3D character animations with my isometric view – how’s that for staying focused?

Isometrics again

While working out and testing Blender to GL export, I’ve setup my isometric view more reliably – the original setup was fiddly.

My unit conversions are getting complicated:

  • First I worked in pixels. This is great for 2D sprites, with 32 pixels amounting to roughly 1 meter.
  • Working with blender, I usually map 1 world unit to 1 meter.
  • To save space, my models are encoded as signed short values. To do this, I premultiply everything by 1000 (smallest resolvable unit = 1 millimeter).

Time to tidy up. Here’s my new setup for the orthographic projection:

[EAGLContext setCurrentContext:context];

glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer);

glViewport(0, 0, backingWidth, backingHeight);

// setup an orthographic projection (no perspective)

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

// The following indicate how many world units we fit

// in the width and height of the screen. So if you set this

// to say, 10×15, you show 15 x 10 world units.

//

// I just map pixels to world units, so at this point I ensure that

// 1 gl unit is 1 pixel.

//

// The depth parameter extrudes the screen into a box. Whatever’s

// inside the box is showing, the rest isn’t.

float width=GL_RENDER_AREA_WIDTH;

float height=GL_RENDER_AREA_HEIGHT;

float depth=GL_ISO_RENDER_DEPTH*5;

glOrthof(-width/2, width/2,-height/2,height/2, -depth/2, depth/2);

// Now we reset the ‘model view’. In other words we’re fitting the world

// inside our box.

// I rotate the world 30 degrees. This creates an isometric projection

// except I’m not rotating 45 degrees on the Y axis, which would give

// the classical ‘lozenge tile’ isometric effect.

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glRotatef(30,1.0f,0,0);

// At this point I rescale by ‘pixels per metre’.

// By default, 32 pixels is now 1 metre.

glScalef(pixelsPerMetre,pixelsPerMetre,pixelsPerMetre);

// Here I setup/clear the depth buffer.

glDepthMask(GL_TRUE);

glClearDepthf(1.0f);

glDepthFunc(GL_LEQUAL);

glEnable(GL_DEPTH_TEST);

// Clear with some non-descript dark gray

glClearColor(0.2f, 0.2f, 0.2f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Render the world

[contentRenderer render];

// Display the result.

glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);

[context presentRenderbuffer:GL_RENDERBUFFER_OES];

OK, now I need to correct everything down the chain. In passing, I removed a ’32′ scaling factor because both my actors and tiles already used world units. The data model still uses pixel units, I won’t touch that for now.

Blender Sprites?

I corrected the tile display – that’s not too important because tiles are really just make-does. I’m more excited about integrating my character animation from Blender. Pending the next serious article about the export/import code, drafted and tested this week-end, the main (and limited) interest of the integration code lies in the transformation sequence.

In the code below, ‘actor’ refers to the data model object. actor3d is a wrapper around geometry data. Since geometry data can be rendered using various methods, I’ve defined rendering code in a separate class, ActorRenderer3D.

This

// eval orientation angle

float angle=0;

Vector* u= actor.orientation;

if (u.length<0.001f) {

angle=0;

}else {

// here the angle is tested against the y axis because

// the orientation (from my legacy 2D isometrics) describes

// the actor’s direction in the model’s x-y plane, so I

// substitute y to z.

Vector* v= [[Vector alloc]initWithX:0.0f y:1.0f z:0.0f];

// [Vector angle::] returns the same values for x positive

// and x negative so I flip the angle to get the

// actual rotation.

angle=[Vector angle_deg:u with:v];

if (u.x<0) {

angle=-angle;

}

}

// transform and draw

glPushMatrix();

// we need to rescale by 32 because the model’s coordinates are in pixels

// (eventually the rescale factor should be passed down the render graph)

float tx=(actor.location.x-originX)/32.0f;

float ty=(actor.location.y-originY)/32.0f;

//

glTranslatef(tx,0,ty);

glRotatef(angle, 0, 1, 0);

[Actor3DRenderer renderActor:actor3d action:@"nod" frame:actor.phase];

glPopMatrix();


Right – that’s it for integration so far. I’ll have more to do later in two essential areas:

  • Selecting the animation frame to be displayed. The model’s ‘phase’ should be determined by the view. This is counter-intuitive (the model should drive the view, not the reverse) but logical (the animator/designer determines the action’s duration). A this point, phase is still determined (arbitrarily) by the model.
    I could export animation durations separately, but I don’t think this is a good idea. This is data duplication, and is kind of dishonest. On the other hand, it is likely that animation durations will require tuning later, so my bet is that I should (a)actually define animation durations somewhere else and (b) provide code that remaps animation frames to actual game loop frames.
  • Mapping/Matching model actions (ids) to animation names (strings) – I’ve hardwired an action name for now because I have no safety guards against an undefined action – an action for which there is no matching animation.

I have now resolved importing Blender data into my game (complete article with source code will be available next).

In this article I’m covering code structure changes required to migrate from a ’2D isometric view’ (CocoaTouch rendering) to a 3D isometric view (OpenGL-ES rendering). The calculations helped me understant glOrthof a little better.

My original plan was to provide first a ‘dummy view’ to allow level design without having to create dummy assets for every other character or tile. I’ll probably still do this, but because the dummy view required vector graphics, I dived head first into OpenGL ES – the downside being that this cannot integrate directly with my current view code.

Starting from the GLES2Sample example, I have done the following:

  1. modified it to remove GL ES2 support (at least for now)
  2. Added an ‘ESContentRenderer’ interface. This could be temporary; for now it’s just an acceptable way to separate GL setup/teardown from content rendering. ES1Renderer now calls [ESContentRenderer render] within its own render function.
  3. Arranged that EAGLView now extends my ActionView class (ActionView just converts simple taps into something like clicks).
  4. Created a StageView3D subclasser to EAGLView. StageView3D is like my 2D StageView class. This is for consistency.
  5. Added a StageViewRenderer class – this implements ESContentRenderer and is meant to do actual rendering work.

I somehow mean to define a common interface for StageView3D and StageView(2D). As it stands most methods in StageView were private – the view knows about the model, not the reverse – so there’s little gained in doing this.

With my new setup, I can instantiate StageView3D in place of StageView (duplicated the constructor). EAGLView owns the contentRenderer and passes it to ES1Renderer (could be renamed FrameRenderer). StageView3D extends EAGLView and provides StageViewRenderer as content renderer.

The above realizes two goals:

  • Put GL drawing in a separate class, part of my view rendering – EAGLView and ES1Renderer are part of my GL boiler plate.
  • Keep the new view setup compatible with my existing controllers.

I’ve been tempted to fire a paint message from ES1Renderer instead. I could handle that within StageView3D. which would keep it more like StageView. For now I don’t see the difference and feel a little suspicious about dispatching an event allowing classes to contribute drawing independently.

I still need to replicate the drawing of the actors, props and board, and (until I enable gl picking) I need to make sure that my isometric view is compatible with my existing (2D) picking code.

After I am finished with setting up my StageContentRenderer and StageView3D:

  • StageView3D duplicates StageView code for 2D picking. I won’t try to factor this out just yet (I will eventually migrate this to 3D picking I guess…)
  • StageContentRenderer graces me with a beautiful gray screen.

Drawing Actors, Props and Terrain with GL.

I already have a SpriteRenderer protocol. This was originally meant to support several implementations and hardly specifies anything, so I can likely create a new implementation for actor rendering; tile and prop rendering was too simple to deserve separate classes.

Pixels to World Coordinates

Previously, my sprite coordinates where defined in pixels. I have no reason to change that (and mess my 2D picking code – I still need it for now).

When rendering the first sprite, I just by-passed applying a translation.
I then use glPushMatrix, glPopMatrix to apply and remove the translation. First I directly use pixel coordinates (expecting to see that go out of the screen) – I do get a gray screen again (no sprite), but also get an interesting error in the console:

GLES2Sample[3130] <Error>: CGContextSaveGState: invalid context

Nothing happens when tracing GL calls. Aha! This is because I left 2D rendering code (calls to drawImage) as I’ve setup only one sprite. Since I’ve managed to run this a few times (without the transformation) and could render the model, this is something that can be ignored. Nice.

I learned a few things while trying to get this right; to setup my isometric view, I use:

glOrthof(-width/2, width/2,-height/2,height/2, -depth/2, depth/2);

Where:
- width is the height of the window/view we are rendering to.
- height is the height of the window/view we are rendering to.
- depth represents the bounds of the field of view. for now I use a parameter ‘like the width or height’

So what glOrthof  does is fit a box taken from the 3D world into the target UIView.

I got a few gray screens while fiddling with that. Common causes:

  • translating the target too much
  • using too small a value for the depth parameter
  • making the width/height too large (apparently if the model is too small, no pixel is rendered.

My test model is just a unit box. When the width and height match about 1 world unit for 1 pixel, this box is hardly visible. I think using 1 meter = 1 world unit is kind of nice (considering how it feels on the Blender grid), so I apply a scaling parameter for world unit to pixel conversion. For now I’d say 1 meter is about 32 pixels (my sprite for the player was 32×64 pixels).

Finally, here’s the code for my view/camera setup…

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float width=320;
float height=460;
float depth=460;
glOrthof(-width/2, width/2,-height/2,height/2, -depth/2, depth/2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(60,1,0,0);

…and the code for my model transformation. Note that all this stuff is ‘nearly correct, not quite correct’ because the isometric view now tracks X coordinates correctly, whereas we have a 2:1 ratio on Y coordinates and I’ll need to correct that later.

glPushMatrix();
float tx=actor.location.x-originX; // (see note for 'origin')
float ty=actor.location.y-originY;
NSLog(@"translate to: x=%f,y=%f",tx,ty);
glTranslatef(tx,0,ty);
glScalef(32, 32, 32);
// draw the model...
glPopMatrix();

The ‘origin’ is the position of the player avatar because I like to keep it at the middle of the screen. Either way the isometric view needs to have a kind of ‘camera location’ so there would be an origin – but then that should be composed once and for all as part of view transform setup.

Uhoh. That took quite a while to get through – next time I’ll do the board and props and it should be a little easier.

I’ve written a batch of work-in-progress articles about behavior programming for Non Player Characters (NPCs). The cornerstone of my approach is the use of scripts or pseudo-scripts describing NPC behavior using concise, powerful statements that look something like:

on [situation] take [decision]

and

on [affect] bear [effect]

These statements are put together using maps. In this context, exclusive maps typically help in deciding what a character should do, given complex situations in which they can only do one thing at a time. inclusive maps allow combining several effects – a character may bear a number of affects (walking on snow slows you down, being under a spell might paralyze, colliding…) all of which change the character’s state simultaneously.

In the meantime, my scripts are supported by a system that somehow emulates event based programming, but somehow lacks well formed, discrete events as a foundation for its operation.

In this article i’m putting together a quick list that can help repair this situation.

Problem to solve

The original problem comes from what constitutes a ‘situation’. Most of the time, the conditions that will result in a character taking action can be fairly complex, involving for example:

  • another actor
  • the distance between the first and second actor.
  • what the second actor does
  • what they are doing it with
  • whom they are doing it towards.

This is an awkward situation because combining all these factors into discrete ‘events’ isn’t a terribly simple task. Unlike discrete mouse or keyboard events, the above refers to lasting conditions that will intersect during a given timespan. Conversely, an actor will typically react by generating actions spreading across several frames.

So far, in my implementation, i’ve been proceeding as follows:

  1. Check if the actor is currently doing something. If so, continue.
  2. Otherwise, check several clauses in turn. Each clause associates a situation to a decision.
  3. For each situation, check whether the conditions are met. If the conditions are met, trigger the decision.

This approach, implemented using a game loop, is probably not too bad, but (a) I want to analyze it in some details to see how it could be improved and clarified (b) I am currently writing some event based (versus game loop driven) game programming, so I’d like to clarify how an event based, versus game loop based approach, would work. This is also important because (c)many current UI programming APIs are event driven (Swing, CocoaTouch…) and (d) high level game management (going to the next level, game over, checking equipment…) definitely requires event driven programming, so even if the game loop oriented approach works out, we still need to figure how a dual system can mesh efficiently.

This is what I’ll do in a couple of soon-to-be-written articles.

Interacting with tiles and props

In the data model supporting my engine, I distinguish three base classes:

  • Actors
  • Cells – cells are ’tiles’ on the game board. A cell defines a terrain (an id representing grass, sand, etc…), the solid property which determines whether an actor can go through the cell, a prop
  • Props. A prop is an accessory associated with a cell. This could be use for trees, walls, etc…

For performance reasons, it might be better for actors not to interact with cells and props. Also we could work around the problem using actors. I leave this question as an implementation detail. For now, I want to consider a few scenarios which might require interacting with the stage and props:

  • There used to be games based on a ‘color all tiles’ concept. We’d like to change the environment property of a tile given preconditions.
  • Suppose an actor picks a key and uses it to open a door. I would tend to define the door as a prop, and the key as either a prop or an actor.
  • Now suppose an actor activates a lever, this might open a door as well, or change something else

a. Specifying tile and prop interactions

Let’s see how this can be specified using our pseudo-scripting. I add the following methods to the Script class:

-(void)let:(act)reaction when:(tag)kind doing:(act)action within:(float)range has:(tag)object;
-(void)let:(act)reaction when:(tag)kind within:(float)range has:(tag)object;
-(void)let:(act)reaction when:(tag)kind is:(tag)status;
-(void)let:(act)reaction when:(tag)kind within:(float)range is:(tag)status;

This doesn’t require redefining the statement class, all that is new so far is that we can add conditions over the target that triggers an action – carrying an item or bearing a given status. So I extend the Statement class accordingly.

I have added methods checking for statuses and items to the Actor class; the actor class forwards hasStatus:(tag)status and hasItem:(tag)item to the hasTag:(tag)tg method; I don’t allocate new pointers at this point – I’ll be relying on an actor subclass if I need more flexibility.

b. Enabling tile and prop interaction

At this point, scripts using the new statement could work, but they would only work with actors:

  • The Interpreter doesn’t recognize tiles and props as type-binding targets
  • Tiles and props don’t have tags associated to them anyway

I modify Interpreter to allow type-binding using tiles. I won’t define per-tile tags. Each tile has an integer coding the kind of terrain used for the tile. I map these ids to tags in the Board class. Maybe later I can extend board to tag special tiles irrespective of terrain if needed.

The interpreter works by associating a TargetSelector to a Decision. Decision is tightly bound to the Actor class, so I define a BoardDecision class for tiles. TargetSelector only requires an actor’s location, so I updated this class to support tiles as well.

For now BoardDecision only recognizes changes to the solid/empty state of a tile (can actors pass through?) and wholesale changes to terrain type.

Finally, I decide I won’t support prop interactions after all. using an Actor as substitute for a prop is much easier.

Picking, dropping, exchanging and snatching objects

I still don’t have anything to support picking and dropping objects. Maybe a good place to implement picking is within BasicActorUpdates:

  • If an actor is on a tile with a pickable object, they can automatically pick an object with a suitable ‘auto-pick’ tag
  • If an actor is on a tile with a pickable, and they use a PICK action, they will also pick that object.

There are many issues related to picking, dropping, exchanging and snatching (forcefully acquiring) objects. To get started, however, I will only use the following statements, adding them to the script class:

-(void)drop:(tag)item at:(tag)location;
-(void)pick:(tag)item at:(tag)location;
-(void)pick:(tag)item;
-(void)give:(tag)item to:(tag)receiver;
-(void)steal:(tag)kind from:(tag)owner;

This allows a game designer to specify basic flows for goods collection, storage and exchange. Note that all of these are high level actions compared to what I have defined so far – given the opportunity, a character is expected to effect these actions, moving to reach the target when detected.

These actions all break down into a couple of actions, one to reach the target and one to effect the corresponding ‘material transfer’ (give/take/pick an object). This is reflected in my implementation of the new Script methods:

  • Each of the above functions generates an approach: statement as already defined by the Script class.
  • A required/desired item condition is attached to the generated statement.
  • A lower priority statement is added to specify the actual transfer.

I have tied up a quick implementation – for now the items to give/take and the intended target are bound to the target actor as a side effect of selecting target actors/locations. I’ll try to put together something more adequate later, but for now, let’s see if that works.

Minor changes?

There is an incidental difference between picking and other behaviors I worked with so far: the pick methods defined in Script may imply moving to a location defined by a cell on the board.
So far, all my tests only required actors to move towards/away from other actors. However, because I decided to rely on existing script statements to implement picking, the requirement that a target tag may refer either to a tagged tile or a tagged actor propagated.
I’ve stubbornly followed suit, for the simple reason that it was legitimate to want to move towards/away from a given tile rather than a given actor. Meanwhile…

  • I have a serious performance issue to take care of, because cells in the data model don’t define a location vector. I have a method that generates a location vector every time, but it is unthinkable to have this run for many tiles at every frame, as it would kill runtime memory in a very, very short time.
  • While it is legitimate to attempt reaching an item on a tagged tile, the algorithm for selecting a tagged tile containing an item is also a concern.

Next time I might look into this in some detail – probably we can improve all this a lot by defining a base class for tiles and actors; also, I surely need to run a fun script to test picking, giving and snatching.

This is my third post on programming NPC behavior in a prototype game engine. I summarize my design (it’s working!). Next I’ll look into other features I might want to add to the prototype.

Summary of design so far

The Script class provides methods to setup a script using simple, statement-like function calls. The Script class also realizes a data structure for the resulting script. Classes:

  • Script
  • Type
  • ScriptDef
    • Selector
    • Statement

The Interpreter class binds a script to a scene by setting up an action graph

An action graph is defined as a hierarchy of actions and conditions. The root of the action graph (typically a Transition instance) is invoked at every model update. Classes used in the action graph:

  • Action – defines the apply method
    • Transition – a simple action group
      • XTransition – extends transition with the ability to replace an action by an alternative if failed, or a next action if complete.
    • PrepareActors
    • BasicActorUpdates
    • XAction – adds the (BOOL)failed and (BOOL)completed methods along with definition for alternative/subsequent action
      • ReflexMap – Select and apply the first action that matches an associate trigger condition
      • Decision – Base class for actor actions (all decisions define an agent)
        • Do – general purpose action (these actions typically match to a sprite animation)
        • LookAt – make an actor look at another actor
        • Approach – come closer to another actor
        • Follow – come closer to an actor if not within a given threshold
        • Avoid – go away from an actor if within a given threshold
  • Condition – defines the (BOOL)check method; used in reflex maps
    • NearestTarget – evaluate the nearest target given an agent and a list of actors
  • TargetSelector – a complex action used to select one or several actors based on several criteria (min/max distance, actor tags, actor activity)

In my tests, I create an XTransition instance as root (this is invoked by an NSTimer) and add the following nodes in order:

  1. PrepareActors. This is used to clear some actor parameters. In my first tests, the speed vector associated with an actor wasn’t cleared. This would be OK if speed was redefined every time, but some actions (e.g: Avoid) are modifiers rather than setters.
  2. An XTransition node, used to hold actor decisions.
  3. An instance of BasicActorUpdates. BasicActorUpdates allows resolving conflicting decisions; for example, BasicActorUpdates prevents actors from entering a solid cell.

I have tested this design with a simple script and (sure, after some debugging) it works quite well. I’ll try to post a picture or video later on.

Main design features

  • The action graph separates game behavior from game data. It also allows specifying complex behaviors at runtime by adding and removing nodes. At compile time, the action graph requires the data model.
  • Tags are string or integer labels used to identify actors indirectly (used in place of data pointers). This allows defining both class and character specific behaviors.
  • Scripts are defined independently from either the action graph or the scene. Scripts have no compile time dependencies with either the data model or the action graph, so they allow simple, highly portable game behavior definitions – An interpreter is used to bind a script to an actual action graph and scene.
  • Separate decision and action. This is not required by the API, but simplifies behavior programming a lot. First all actors evaluate ‘what they would like to do’, next interactions are resolved locally, taking conflicts into account.

At this point, I have defined several classes that contribute to generating action graphs. An action graph is invoked at every frame. Roughly speaking, the action graph checks preconditions in the game world and applies post-conditions. Action graphs, however, are a little more structured and powerful than this given special constructs.

  • At the root of the action graph, the main Transition defines several actions to be applied at every frame.
  • A ReflexMap is an action that selects between competing responses in the presence of several stimuli.
  • A GearSelector is an action that maintains and effects a priority driven agenda. The GearSelector can enable/disable an action on the agenda, independently of whether the action is current or not. GearSelector allows mixing long and short term goals as it can pause a lower priority action until a higher priority action is complete.
  • Finally, XAction allows sequencing actions and using alternatives if an action fails.

All this stuff is fine, however, there are significant features of the action graph that can become problems:

  1. The action graph must provide nodes on a per instance basis. For example, if a type of actor is associated with a reflex map, we need to provide a separate reflex map for each actor of this type. This is because different actors are in a different context, so they do different things.
  2. Conditions often provide the basis for event driven behavior; however, conditions in the current implementation just return a boolean value. Often, even if a condition targets a specific actor, it can be fulfilled in various ways. We then typically would like to bind the matching event parameters in an associate postcondition.

Overall, it would be worth looking at this stuff from a more object oriented point of view, however we probably also want to keep objectified conditions and actions, because this provides a lot of flexibility at runtime.

How can we efficiently model high level game behaviors that are mostly dependent on ‘what goes on in the head of a NPC (non player character)’? In this article, I’m discussing approaches and sketching a model. I am mostly concerned about specifying in-game behavior concisely and correctly. If you want to apply what follows to an actual game, you’ll need to consider processing overheads, and scale behavioral models accordingly.

I started with three ideas in mind:

  • Call a decision function at regular intervals. Whenever the function is invoked, the character checks their situation and issues an instruction regarding what to do next. I call this a ‘decision loop’ and this approach is common in many games that require simple behavior. The problem with a decision loop is that we need either doing a lot of book-keeping or reiterating many considerations every time. It’s not just memory or processing time. Practically, this approach is counter-intuitive to most people, and doesn’t scale up to complex behavior.
  • We might layer several decision loops running at various speeds. The faster loops are used to take short term, tactical decisions. The slower loops are used to make decisions that are more on the long term. I find this idea quite attractive, but it would need to be developed to become practical – how do slow paced decision loops affect behavior? In a few trivial cases that might just involve carrying out a one-off action. In other cases, that could mean we need to make changes to faster loops. How do we translate ‘migrate to the south’ into the everyday behavior of a NPC?
  • Have actors execute high level scripts or programs. The idea is to have a separate execution context for each actor. The script controlling each actor is structured like any program in C or whatnot and runs on a ‘lazy thread’, usually waiting for input describing the result of low level interactions in the game.
    Affording a separate thread dedicated to effecting an actor’s behavior could make it much easier to specify and implement such behavior. However, we may not be able to use whatever language the game is written in to specify behavioral scripts, or maybe not very directly. Also, it’s not too likely that we can allocate system/native threads to our actors (I’ll come back to these issues in a later post).

OK, I’m not trying to write ‘regular AIs’, just NPCs. So the main problem can be divided in two questions:

  1. What is/are convenient ways to specify the behavior of NPCs.
  2. What do such specifications entail – what kind of software do we need to write so that the NPCs will behave according to the specification?

An example

I take the example of a farmer. Every day, they get up, get dressed, go to the field. They work in the field until twelve and then meet their wife near the well for a snack. Afterwards they work some more and go to the pub at the village. Once they are drunk they go home, undress (if able to) and sleep (A).

This doesn’t cover all the many behaviors of a farmer. Some examples:

  • They could find a pot of gold while digging and decide to become a banker (B)
  • They could encounter an aggressive animal while working. Then probably they would run and / or defend themselves (C)
  • It could be raining hard, so they would go back home (D)
  • If it rained the day before, they would try to avoid a puddle if they spotted one (E)

Script (A) is simple and linear. Starting from low level actions (move, plough, get dressed, drink, buy, sit…) that can be rendered as animations in a game, we would need to build each part of the script. That may seem a lot of work, but if we break down each part, we’ll soon find that we can reuse subscripts not just for this particular character but also for other characters. Also, I would argue that even a moderately realistic effort in this direction could make many games a lot more fun – after all, so many games have static NPCs, or NPCs that can handle only very restricted situations (e.g: shooting).

If we’re specifying script A, I would argue that many languages would do, ‘assuming we could use them for that purpose’. In other words, we would feel comfortable writing a program, maybe in C, Python or Javascript (many languages would do) that describes ‘a farmer’s day’ using a main procedure and it’s sub-functions. That would be more intuitive and definitely faster than implementing a decision function that gets called every time and representing the situation explicitly.

But then, surely we wouldn’t want to mix B-E with script A. This stuff could happen at any time and would need to be taken care of in priority. So we probably need.

  • Detection routines or notifications to trigger a response to unexpected occurrences.
  • A mechanism used to override a ‘main script’.

The actual specification of B-E could use a mechanism similar to script A, so it seems we have all the ingredients to offer a workable model.

Our model

Here’s the proposed model:

  1. The high level behavior of each character is defined by a set of scripts (To run these scripts, we may need a special interpreter).
  2. Scripts are arranged by priority. A higher priority script can override a lower priority script if needed – for example, fighting for life is more important than avoiding a puddle, so a ‘puddle event’ is ignored in the middle of a fight.
  3. A script can be attached to a condition that triggers the script. The script won’t actually take control of the actor unless it has suitable priority relative to the current script.
  4. A script can be invoked externally as a side effect of something else going on. Again, invoking means ‘attempting’. Another character might attempt starting a conversation with an actor under attack. That won’t actually start the conversation script.
  5. Once a script has completed, the interrupted script may resume, or maybe another, pending, higher priority script may start.
  6. Under special circumstances, a script may replace another (for example, our farmer can become a banker). This would probably require tagging scripts – the farming script is ‘occupational’, banking goes in the same category.

Needless to say, this can be improved in many ways – for example we may need to define ‘yield points’ – points at which it is graceful or safe to override another script… and so forth.

Pending, I would be very curious to hear about APIs allowing to do this or something similar… So please don’t hesitate to reply to this post if you have any idea.