LO's Sharp Devs

C# , XNA Game Development Blog. Intentionally more than only for game authors but for C# programmers in general.

Moje zdjęcie
Nazwa:
Lokalizacja: Poland

środa, 14 kwietnia 2010

[Game Design] Artificial Stupidity ™

Almost anyone knows what Artificial Intelligence (AI) is – set of data and algorithms that simulate intelligent (Or not-so, if developers were lazy or just noobz) behaviour of NPC characters in game. And scripted events / actions / behaviours – are definitely not an AI – they are just scripts. Nor path finding is.
I would define game to have an AI when units DECIDE what to do accordingly to possessed DATA (knowledge, experience, predefined information) and actual situation in the game. So when there is in game routines algorithm – it’s not enough. When it’s defined, that at 7 NPC leaves house, then arrives his workplace and leaves it at 16 – this is again – scripting. Let us just imagine mixing an RPG characters with a bit of Sims. Developers only define FACTS (where person has house, which is his bed, where he ACTUALLY works), and PARAMETERS (how tough he is, how resistant, what are his moral principles etc.). There shall be also RESOURCES assigned to NPC that change in runtime accordingly to FACTS, PARAMETERS or EVENTS (like money level, health level, stamina, hunger etc).
What shall be done now – yes, we cannot avoid coding at this point – is do write right algorithms that will properly take care of maximizing or balancing these resources (NPC character or morality might have high impact on priorities). Now this algorithms might use knowledge of the NPC (where I can actually obtain food? How much time do I have before I starve?), knowledge balanced path finding (What would be FASTEST way to get to the shop? What would be the best way to avoid these thugs on the way I know about? I guess if that gate is open today so I can go through shortcut?) and predefined behaviors (as many as developers imagine for realistic experience).
And another thing – when NPC is going to actually shoot at someone – it shall NEVER miss! This is Intelligence – absolute, it does not makes any errors! This is not realistic – for RPG games this aiming skill would define failure chance. This is some solution. But how for FPS? Only random number telling if aiming was right? How fast gamers find that enemies just shoot at random?
Now we came to this post main point. There is need to implement some misbehaviour, mistakes, errors, non-optimized paths that will move along with game progress or difficulty level. Writing absolute algorithms is as easy as writing completely poor algorithms. Writing algorithms that can adapt their efficiency or result quality accordingly to changing parameters is not an easy task. There are some games that tried to incorporate sandboxed, nonlinear NPC behaviour and interactions (just to name S.T.A.L.K.E.R.), but it seemed so hard to balance, that planned solution was reduced so game could be playable.
Hard to say where they failed – lack of skill, lack of time? I would rather suggest – that developers used not enough parameters, resources and knowledge that could simulate socialized units and balance their coexistence. Does fully simulated, working world is ever possible? Just Cause 2? Elder Scrolls V? Something not yet announced? Or all artificial worlds, as models are either caused to collapse or forced to be externally controlled and rebalanced by developer’s god hand? I hope no.

Etykiety:

czwartek, 2 lipca 2009

VS 2008 - Disappearing dependencies

Hi, after a long break I have something interesting to post :) I've encountered lately some weird behaviour of Visual Studio 2008 Installer Projects. I checked roughly Google, to find if anyone have solved that - but seen only problem mentioned. Therefore I will describe the issue and then how I dealt with this.

I've been re-factoring rather complex yet simple project with many project files in this. More to say: web-service, web-client, windows-service application (with own installer for each one), bunch of plug-in files used by windows service, couple of tool libraries, business layer library, DAL library, communication library, testing projects, etc.
I wanted to get rid with DAL and business layer being directly or indirectly referenced by plug-ins or web-client which I'd considered at least a "bad design". Therefore I've created two separate libraries with almost only interfaces within them - let say BaseInterfaces.dll and Interfaces.dll (data object and root classes were already separated into another file or files so when I tried to use only one interface library there occurred circular reference, which I did not intended to fix at the moment) .
I changed then almost all projects and libraries within solution to use them, and after a standard juggle with code I removed bad references to Business layer and DAL. Compiled everything, verified that all executes correctly, packed installers and delivered to first line QA.
Then bump! Windows service crashes on start! Quick check - and there is missing Interfaces.dll. What?! It works here on testing framework, web page and web-service install correctly...
So then I've checked installers and it appeared that this file was not installed into root folder, however - while another copy being referenced by plug-ins (that were located in sub-folder of Windows service) was installed correctly! It of course also installed right for web-service and web-client.
I've checked project outputs, references and dependencies, tried to remove outputs from installer and put them back. I even tried to add that missing library output directly into installer - file was still missing (So not even appearing on the list of the files going to be installed into "Application Folder").
Then I started investigating Windows service app once again. I've seen that it referenced directly to Interfaces.dll (because was calling some object in BusinessLayer.dll that implemented one of the interfaces contained within, and in such situation just "inheriting" reference in is not enough) and "inherited" BaseInterfaces.dll the normal way from BusinessLayer.dll (that used both interface libraries of course). That second one has been built into installer correctly. And both appeared in both Windows Service and BusinessLayer.dll "Bin" and "obj" folders.
Then I just added that BaseInterfaces.dll directly to Service references. And yes! It disappeared from the installer project too! What the ...? Ah so!

After small code change I was able to remove Interfaces.dll reference from Windows service project and then all went fine from that point...

CONCLUSION:

It appears, that installer optimization is sometimes too "smart". It has found that I'm referencing that DLL in the "outputted" project, but also, that I'm not using it directly there, so it considered to mark it as not required or used at all. It seems that it anyway did it it in such way that covered that this library IS actually used by other libraries that are called by the service itself, or even more - when it is directly added to the installer project! I, therefore, consider this behaviour of installer as a bug.


I hope that it could help someone. Any comments?

Etykiety: , , , , ,

niedziela, 11 stycznia 2009

[Game Design] Dynamic Quests

I could just not stop myself from extending that idea that I roughly sketched in the previous article.

First - imagine that we have several classes that describe quest(s) "statically" - so how this is presented in Quest Editor.

Therefore we have some Scenario object that is being processed by responsible ScenarioManager class. During the game that manager class founds that some activation (like this is stored in scenario - dialog option, item picked, area entered or whatever) of a new quest happened. It then sends identification number of that quest and pointer to the activator to contained class of QuestsManager to take care of the rest.

Now QM checks the type of the quest and expected result from manager (In many cases it might be specific branch of dialogue, message to be displayed, or player's log entry or all at once. Anyway QM shall also check if quest that is not generic has not been already finished or started - internal integrity check that shall be found during QA process but also left in runtime - especially if game provides custom content. So far it goes normal. Generic quests is a matter for another entry so let us just consider that we just found a pure new dynamic quest started by picking cursed Sword of Slow Death...

In the script we have written that the curse cannot be taken away by normal methods or dispells but requires special treatment. In the quest the item is not dynamic, only the temple and person-to-talk are picked randomly. While there is no direct entry dialogue we must consider serious modification of game world for that quest. So there is no specific result returned to ScenarioManager.

QM orders QuestFactory class to build runtime data for given quest. QF loads static data and first of all creates array of variables that might be used during it initialization script. In our case there is going to happen quite much in that script - first of all it shall check if the activator is really that sword and if the curse flag remains in place. Therefore we are sure that default behaviors of the cursed possessions apply. We then check if expected special script is attached to the Sword's OnSpellCast() event. Last thing is attaching to the player curse script - that is also not a general one.

Curse script is gradually reducing player's endurance (just reducing, any curing spells or potions restore it) and each game hour it displays message - "You feel weaker..." when that happens. Much more interesting is script that is being executed each time the spell is being cast upon the sword. In just allows spells work normally until they have effects of dispell, uncurse, disintegrate, identification or similar (no exact spells, that might be created after quest script but particular effects that we are interested in).

When player tries to remove curse (dispell, uncurse) we return 100% resistance / failure and for FIRST such spell effect we register additional dialog branches for ANY priest or mage in the world (yep, game engine shall support that of course). For desintegrate spell there is 95% chance of failure and same registration of dialogs. If desegregation succeeds - quest and curse are just being removed, but not curse effects that shall be cured normally. For identification spells we display to the player some extra box with message "There is so strong curse on that Sword, that only highly qualified Priest or Mage can know more about it" and then allow standard identification dialog to appear with curse details (or not? SilentFail status? up to the Designer)

Then player have to visit any Mage or Priest and go that dialog branch for the cursed sword. He might be luck enough to ask person proficient enough at first try - then we just jump to the next stage. If not - there is first dynamic modification - we pick accordingly to the interlocutor 15 lvl Mage or Priest he might suggest to us (if our game has living world it may also be that he shall also KNOW that person exists - the he would only point us higher) to talk to. We shall have vast amount of options to define how this randomization shall happen - like localization, availability, if that location was already visited or do we have current entry permission there or just ignore all such obstacles and let the player find solution (by generic quests). We choose them all in Quest Editor and feed randomization engine. If there is NO location or person - engine shall be capable of creating one in game environment dynamically - and such change shall be therefore constant (well, until someone kills the mage...).
Anyway, quest factory then executes ScenarioManager.RegisterKeyElements() with the mage himself and his location. Therefore player will be warned when he tries to lock / kill / destroy object or person that he has quest(s) attached to. Of course player (or in living game world - other NPC or monster) could kill such person and not just being faced immortal character. If so - the quest shall generate a WORKAROUND branch - returning to last mage or other temple and ask again, or finding some scroll with the mage that informs about his master, or whatever. Such things shall be possible to hardcode in quest's script or generated by engine accordingly to similar rules used to generate dynamic branch. For example we shall decide if we just want replacement for current step or way that lead us back to previous one to try again. In our situation it shall be some cynical comment told by avatar - "Well, I just guess that I shall look for another mage..." and reset script few steps back.
For more details - we can then join such us dynamic quest with generic quests - for example our mage knows resolution for our problem and can do that for free but or requires some favor (service or possibly a "kiss" from female character) or component that is required to fulfill the spell. Anything - we can define strict steps in the quest or just define criteria for generic quest generation and wait with baseline until finished.

Let us then imagine that we bring that compound and find mage dead. In traditional scripting (and storyline) it would be intentional event with ready solution - player is supposed to find some track, person or anything that would lead him deeper into the story. This leads dynamic design much more complicated - we shall create workaround that will not interfere with already done parts and will not generate possible infinite loop (let us just imagine that some NPC just gone on his rampage and murders all high level mages...). Probably best solution would be to find yet another mage and continue from there. But how to prevent another generic quest from him? If previous quest was a simple service we might just pay him for casting spell and quest ends. But if we were supposed to find some compound required for spell this gets to be a little harder...

Game shall track therefore all such generic subquest progresses and have additional dialogues to make it smooth - like we just telling new wizard that we have required spell component. This also leads to another idea - that all generic subquests shall be prepared with special care for failures, additional dialogue lines - preferably also with randomization and variatization. A lot of work, carefull and complicated work. But final results might be astonishing!

We can also define maximum or default amount of generic subquests or default quest lines if generic fail... A lot things to consider by the designers.

In the end, after quest finishes all remaining scripts, dialog options that are still bounded to the quest are removed (so after curse is taken out of the sword, except taking out curse spell effect from itself and player, curse flag and scripts there also shall be removed quest id from the sword - all shall be considered by QuestEnds() script). This not touch log /diary entries - these are just moved to "Finished" branch. Therefore old data is no longer wasting place in save and memory.
All KeyElement flags are also removed from objects.

I hope that this article would help one build his own great questing system :)

sobota, 10 stycznia 2009

[Game Design] Synthetic voices part. 1

I consider that topic realization as far future, but would share my idea with the industry - maybe someone already started that and would like some bright review on the topic :)

Currently we have two realizations of voice acting in games - there are no voices in games (cheap productions) or voices are recorder from (professional) actors. While for simpler games it's possible to do that easily (not so much text to record) it's getting harder for adventure or RPG games - there is so much text that voiced is only part of it, or eventually dialogs are very limited and considerably short (see Oblivion or Fallout 3).

Anyway it's still A LOT to record - not even mentioning mastering that to have normalized volume output - from many actors, record same sentences from different actors, or record several similar sentences from one actor for different situations or even mix of two. The serious drawback is that therefore some dynamic texts that could be visible in dialogs (for example changing rank of the hero when he is being addressed to) are or skipped at all or only written in captions. This also makes writing dialogs and texts at all harder - designers have to consider all those limitations.

Another limitation is localization and internationalization support - while English without conjugation, declination and sex independent sentences is quite charming for that - localizing dialogs that do not consider differences is pain in the ass. Some dialogs that might appear in game are prepared dependently of the sex of hero - but not all, and this leads into even more voices to record. And only consider that sex of both sides of one dialog can vary...

This limits also creation of "generic" or "dynamic" quests. The first category could be incorporated into advanced roguelike games - where same quest line could be modified only with different locations, items or people names to get new quests of similar type. The second one could be used to increase playability factor by assigning limited quests to different people, places or items for each new game (or when key person or item is no longer available - this could help the game to fight so unnatural existence of immortal people in Fallout 3 or Oblivion, without ruining quest plot). Again - in English this is very easy. Consider some classical dialog line like:

I think that for more information about that Ring of Destruction you might ask John Bussack up there in Rovannon.

Fine, let us imagine that considerable dialog script looks like:

I think that for more information about that [Item(CurrentQuestItem(3)).Name] you might ask [Person(CurrentQuestPerson(1)).Name] up there in [Location(CurrentQuestLocation(2)).Name].

Complicated? Not so. But there is no information about sex of person that says that what would led into more complicated script.

Let us consider now translating this into Polish (language that uses postfixes to determine exact language forms). And consider that we have already implemented library that will do all conjugations and declinations automatically (or basing on dictionary - it doesn't matter now).

Wydaje mi się, że aby uzyskać informacje o tym [Decline(Item(CurrentQuestItem(3)).Name, Declinator.Locative, NounNumber.Singular] [ConjugateForSex("powinien", PlayerSex(), VerbNumber.Singular, VerbPerson.Second, VerbMode.Supposition, VerbTime.Past)] [Conjugate("pytać", VerbMode.Fulfillment, VerbPerson.Infinitive)] [Decline(Person(CurrentQuestPerson(1)).Name, Declinator.Accusative, NounNumber.Singular] w [Decline(Location(CurrentQuestLocation(2)).Name, Declinator.Locative, NounNumber.Singular].

Uhh... hard one, isn't it? I would therefore reconsider all conjugation grammar and just write the sentence splitter - or in the code or directly storing 2-4 versions of the sentence for each combination of interlocutors' sex. In first example it could look something like (sentence is constructed so sex of person that talks doesn't matter so we care only of the sex of addressed person):

Wydaje mi się, że aby uzyskać informacje o tym [Decline(Item(CurrentQuestItem(3)).Name, Declinator.Locative, NounNumber.Singular] [IF(PlayerSex(), Sex.Male)]powinieneś[ELSE]powinnaś[ENDIF] spytać[Decline(Person(CurrentQuestPerson(1)).Name, Declinator.Accusative, NounNumber.Singular] w [Decline(Location(CurrentQuestLocation(2)).Name, Declinator.Locative, NounNumber.Singular].

Or even such IF could be made for whole sentence for better readability. Expected result might be something like (for first example it of course depends on gramatization engine) - if player character is male:

Wydaje mi się, że aby uzyskać informacje o tym Pierścieiu Zagłady powinieneś spytać Johna Bussacka w Rovannon.

This of course do not takes information that form of "o tym" is dependent of the "sex"of the ring (both in English and in Polish - this is masculine) that would considerably complicate the script. Therefore I suppose that creation of such dynamic sentences is a far future (until very good translation supporting tools - here this would be great if script is being generated from EXAMPLE translation and supported by handy list of usable items obtained form original sentence). So I think this is still a far future, not even mentioning recording dynamic voices for all this things, or even mastering them so such dynamic sentences built from different sound parts sound correctly. Some resolution may be such construction of translations that are independent of all this changing forms and only switch the dependently of the sex. But suspected results most of the time will not sound very natural.

What about when voice of the person just not "fits" to how this person looks like? The exact timbre of the person depends on many different elements, but construction of the skull bays is one of the most important - this is resonance box of human being. Therefore we subliminally expect person of given features or body build to sound in some way or another. And when he or she sounds different - we are surprised. How therefore align same sentence to different people? They shall not sound the same!

In the next part of this article I will share with you some concept I have to deal with that voice recording problem.

Etykiety:

wtorek, 25 listopada 2008

[Testing Environment – DB p. 1] Authorization

I’m going to use this DB entry article to introduce notation I’m going to incorporate In following articles. First of all I put in charge READIBILITY – as more important in those rather learning materials, than any common (or custom) notation or standard.

First of all – table naming conventions:
• Names are simple, singular words, separated with underscore, describing the content or entity
• Tables containing dictionary data start with prefix "dic_"

I’m not designing there multilingual support (but I left this an open issue – I use dictionary tables whenever translatable data appears, so one can easily add translations table for them if necessary) or care for all fields that might be useful – I’m going to incorporate as few fields only when they are needed or excused by current article. I also do not care now schemas, ownership or complicated indexes (well I put them in place, but I will not mark them on diagrams or discuss this area).

System is being designed in the way that can be used both by stationary users, offshore ones as well with closed and public beta testers. Therefore a set of permissions is going to be implemented. System is also designed to support many games (as well generally other IT projects) and allow access one parson to many games. Here it goes basic DB schema:



Some notes about tables and fields:

Game – main table, now just containing game identifier and simple name.
User – table containing users, fields are obvious, but one might require additional comment – it’s display_name, that is going to be used on forums, lists etc. Generally it shall be name + surname, but is intended to be unique (but for security reasons different than login).
Dic_user_role – intended to contain main role that user has in given game. It does not grants any permissions – it’s rather used as sub header on forum, discussions, logs etc.
Dic_access_permission – defines available permissions to the system
Assigned_access_permission – lists permissions that has been granted for specific game access. Field value might be used by some permissions, but we do not use it yet.
Game_access – defines user has access to specific game (with dates) at particular role with set of granted permissions.

There might be defined specific default template of permissions depending of assigned role to just assist in creation of user access. This might be stored in db or be defined only on backend application. Administrator shall have full possibility to limit or extend that list for particular user.

środa, 19 listopada 2008

[Game - Audio] Dynamic Music

Yes, today it is time for well known subject, that various developers tried to beat many times already. Dynamically changing music. Thing simple as an idea but very hard to implement.

I will just run through various attempts or ideas I've seen or thought about and in the end propose my own idea, that is obviously some kind of compilation of best solutions :)

In most cases there are just two (sometimes more) types of music in ordinary game: "normal" music that goes through most of the time and "dynamic" one appearing during fight or something. There sometimes appears "special" music connected to particular event or location. Typical game may contain several tracks of each type picked randomly and in line with current game situation.

In old days there were just midi tracks, that were swapped, eventually swapped channel by channel to receive "smoother" transition. This was quite simple to implement and resulted with nice transition. Now, even in era of advanced sound cards (X-Fi?) midi is simply too poor and simple, and of course sounds very poor on weaker cards.

Similar results could be obtained using mods, where samples of instruments (or even voices) were digitalized, not synthesized in real time. This improves quality on low-end cards, but still is no good quality.

Then designers started using music on CD or MP3 (and similar formats). The quality can be very high but is has real drawback in smooth transition - most of the time developers rely on external libraries that have to buffer tracks before play, can play only one track at the moment etc. This gives no way for nice transitions - (like in Morrowind / Oblivion where game frozen for second or two, then another track started from the beginning), or when this is fluent - one can easily determine where one track ends and another starts - what is big clue of being attacked, or even tracked by the enemy - what shall not happen in horrors and similar games. This is bad also for themes that have to warm up first - so are silent in the beginning etc.

My proposal is to develop own replay library that can buffer several tracks and then design GROUP of tracks that are connected by some "theme" and then prepare special matrix (for example with accuracy of 5 seconds) which points into which second of track of other type to jump when being in particular place in current track. Of course such places in tracks shall be first composed by musician, but having that in mind - themed groups can be doable.

It can be even upgraded, so particular transition may be done in several ways, of one will be picked randomly, giving feeling of variability. Or also may be programatically changed from "rapid" change in violent situations into fluent change (jumping into warm-up section?) in more "thrilling" moments. This matrix may also contain information of conditional loops in one track to avoid "repetition" feeling (hearing warm-up section is quite significant in my opinion) in selected situations.

Of course that requires careful music design - so all track types in one theme group
have several ways of jumping from one to another. One may even design special themed "jingles" (short pieces of sound) that would help in jumping from one tension to another (sudden chord that matches following battle music or in the opposite - fading ending of rush music) or just build bigger diversity in transitions. But this is also a great benefit itself - having music themed and dynamically changing inside one theme adds to the game movie soundtrack feeling, where music is written up to accompany current scene. This shall add great value to the game immersion.

Comments? You are welcome.

środa, 8 października 2008

[Coding] Dealing with multithread

Today's thing that may be little tricky for a newbie developer, causing sometimes a bunch of frustration - I mean dealing with access to main-thread-only objects [MTOOs] (like windows and (suggested) files) form other threads.

Using BackgroundWorker.OnProgress seems to work fine, and does not require writing synchronization code. But what with other threads or accessing MTOOs directly from OnWork method?
Solution is fairy well designed by .Net designers and simple but little troblesome to implement. When in need to display some text for example in "textarea" one could just write:
form1.textLog.AppendText("some text");

But this would just throw InvalidOperationException. What do then?
One must use delegates. So first of all - code that will be synchronized shall be placed into separate method. There are two possible ways to do, because of delegate required:
1. Use one of the existing delegates (EventHandlers) and create method's arguments to fit it.
2. Write your own simple delegate to pass required set of arguments with it.
Sample declaration may look like:
public delegate void DebugMessageInvocation(string message, ErrorSeverity severity);

And then just delegated method:
private void WriteLog(string message, ErrorSeverity severity)
{
  form1.textLog.AppendText(message);
}

And we just call the function instead:
WriteLog("some text", ErrorSeverity.Info);

Of course this will not fix the problem yet, nor it explain why did we used delegate parametrization. The answer is System.Control.Invoke() method. It's supposed to execute given (by delegate) method in context of the main thread! Yes - it is what we were looking for. And why did we required delegate. Now it can be implemented in two ways: making delegate call directly from "main" code, or encapsulating all the logic inside delegate function using some simple "tricks":
private void WriteLog(string message, ErrorSeverity severity)
{
  if (form1.textLog.InvokeRequired) // yes, we are checking if we are already in the main thread, very nice thing :)
  {
    form1.textLog.AppendText(message); // safe, we are already in main thread
  }
  else
  {
    form1.textLog.Invoke(new DebugMessageInvocation(WriteLog),
      new object[] { message, severity }); // [1]
  }
}

Eventually one may negate condition and put all the logic further in the method body - preferred way of doing things.
What is going on up there? I think that only required explanation is for line [1]. Is what happen in the way:
1. We are building array of all arguments passed to the method - we are responsible for right order etc.
2. We create back delegate for the very same method we are in
3. We order System.Control to invoke this delegate in the main thread context and pass to this given array of parameters (will be unwrapped on the way).
And that's all - when function is invoked, it just passes to the main branch and does what it was supposed to do.

One important thing to add: I was talking about Invoke being executed into the main thread, what is not essentially the truth. In fact both using controls require and invoking code results with is to be in the same thread that when control was created. While majority of controls is being created in the main thread this works fine. So I strictly recommend to create controls from inside the main thread - and the best solution for that is to use... Invocation again (using a main form control for that)!

Have nice time. Hope this will be something new and interesting for some devs :D