开发者

Choosing a scripting language for game and implementing it

I am currently developing a 3D Action/RPG game in C++, and I would like some advice in choosing a scripting language to program the AI of the game. My team comes from a modding background, and in fact we are still finishing work on a mod of the game Gothic. In that game (which we also got our inspiration from) the language DAEDALUS (created by Piranha Bytes, the makers of the game) is used. Here is a full description of said language.

The main thing to notice about this is that it uses instances moreso than classes. The game engine is closed, and so one can only guess about the internal implementation of this language, but the main thing I am looking for in a scripting language (which ideally would be quite similar but preferably also more powerful than DAEDALUS) is the fact that there are de facto 3 'separations' of classes - ie classes, instances and (instances of instances?).

I think it will be easier to understand what I want if I provide an example. Take a regular NPC. First of all you have a class defined which (I understand) mirrors the (class or structure) inside the engine:

CLASS C_NPC 
{
    VAR INT     id                              ;       //  absolute ID des NPCs
    VAR STRING  name            [5]             ;       //  Namen des NPC
    VAR STRING  slot                            ;           
    VAR INT     npcType                         ;           
    VAR INT     flags                           ;           
    VAR INT     attribute       [ATR_INDEX_MAX] ;           
    VAR INT     protection      [PROT_INDEX_MAX];           
    VAR INT     damage          [DAM_INDEX_MAX] ;           
    VAR INT     damagetype                      ;
    VAR INT     guild,level                     ;           

    VAR FUNC    mission         [MAX_MISSIONS]  ;           
    var INT     fight_tactic                    ;           
    VAR INT     weapon                          ;           

    VAR INT     voice                           ;           
    VAR INT     voicePitch                      ;           
    VAR INT     bodymass                        ;           

    VAR FUNC    daily_routine                   ;       //  Tagesablauf
    VAR FUNC    start_aistate                   ;       //  Zustandsgesteuert

    // **********************                   
    // Spawn                                    
    // **********************                   
    VAR STRING  spawnPoint                      ;       //  Beim Tod, wo respawnen ?
    VAR INT     spawnDelay                      ;       //  Mit Delay in (Echtzeit)-Sekunden

    // **********************                   
    // SENSES                                   
    // **********************                   
    VAR INT     senses                          ;       //  Sinne
    VAR INT     senses_range                    ;       //  Reichweite der Sinne in cm

    // **********************                   
    // Feel free to use                         
    // **********************                   
    VAR INT     aivar           [50]            ;                       
    VAR STRING  wp                              ;           

    // **********************                   
    // Experience dependant                     
    // **********************                   
    VAR INT     exp                             ;       // EXerience Points
    VAR INT     exp_next                        ;       // EXerience Points needed to advance to next level
    VAR INT     lp                              ;       // Learn Points     
};

Then, you can also define prototypes (which set some default values). But how you actually define an NPC is like this:

instance BAU_900_Ricelord (Npc_Default) //Inherit from prototype Npc_Default
{
    //-------- primary data --------

    name        =   "Ryzowy Ksiaze";
    npctype     =   NPCTYPE_GUARD;  
    guild       =   GIL_BAU;      
    level       =   10;
    voice       =   12;
    id          =   900; 

    //-------- abilities --------
    attribute[ATR_STRENGTH]     = 50;
    attribute[ATR_DEXTERITY]    = 10;
    attribute[ATR_MANA_MAX]     = 0;
    attribute[ATR_MANA]         = 0;
    attribute[ATR_HITPOINTS_MAX]= 170;
    attribute[ATR_HITPOINTS]    = 170;

    //-------- visuals --------
    //              animations
    Mdl_SetVisual       (self,"HUMANS.MDS");
    Mdl_ApplyOverlayMds (self,"Humans_Arrogance.mds");
    Mdl_ApplyOverlayMds 开发者_如何转开发(self,"HUMANS_DZIDA.MDS");
    //          body mesh     ,bdytex,skin,head mesh     ,headtex,teethtex,ruestung 
    Mdl_SetVisualBody (self,"Hum_Body_CookSmith",1,1,"Hum_Head_FatBald",91 ,  0,-1);

    B_Scale (self); 
    Mdl_SetModelFatness(self,2);

    fight_tactic    =   FAI_HUMAN_STRONG;

    //-------- Talente --------                                    
    Npc_SetTalentSkill  (self,NPC_TALENT_1H,1); 


    //-------- inventory --------                                    

        CreateInvItems (self, ItFoRice,10);
        CreateInvItem (self, ItFoWine);
        CreateInvItems(self, ItMiNugget,40);
        EquipItem  (self, Heerscherstab); 

        EquipItem  (self, MOD_AMULETTDESREISLORDS); 

        CreateInvItem (self, ItMi_Alchemy_Moleratlubric_01);
        //CreateInvItem (self,ItKey_RB_01);

        EquipItem (self, Ring_des_Lebens);

    //-------------Daily Routine-------------
    daily_routine = Rtn_start_900;

};

FUNC VOID Rtn_start_900 ()
{
    TA_Boss         (07,00,20,00,"NC_RICELORD");
    TA_SitAround    (20,00,24,00,"NC_RICELORD_SIT");
    TA_Sleep        (24,00,07,00,"NC_RICEBUNKER_10");
};

As you can see, the instance declaration is more like a constructor function, setting values and calling functions from within. This still wouldn't pose THAT much of a problem, if not for one more thing: multiple copies of this instance. For example, you can spawn multiple BAU_900_Ricelord's, and each of them keeps track of its own AI state, hitpoints etc.

Now I think the instances are represented as ints (maybe even as the id of the NPC) inside the engine, as whenever (inside the script) you use the expression BAU_900_Ricelord it can be only assigned to an int variable, and most functions that operate on NPCs take that int value. However to directly modify its hitpoints etc you have to do something like var C_NPC npc = GetNPC(Bau_900_Ricelord); npc.attribute[ATR_HITPOINTS] = 10; ie get the actual C_NPC object that represents it.

To finally recap - is it possible to get this kind of behaviour in any scripting languages you know of, or am I stuck with having to make my own? Or maybe there is an even better way of representing NPC's and their behaviours that way. The IDEAL language for scripting for me would be C#, as I simply adore that language, but somehow I doubt it is possible or indeed feasible to try and implement a similar kind of behaviour in C#.

Many thanks


C# can be used as a scripting language.

Apart from c#, lua is very popular as a game scripting language

Your npc example could be solved as following:

  • Create a baseclass npc
  • inherit the npc class to create a custom npc with specific characteristics/behaviours
  • create instances of the inherited class


I think that whereas you are hoping for 3 levels of class/instance supported by the language, it helps if you actually have just 1, and build everything from there.

Static languages typically have 2 levels - the 'class', defined at compile-time, and the 'instance', created from the class at run-time. This makes a lot of sense given that it's practical to know exactly how many classes you want up-front, and usually impractical to know exactly how many instances. Classes become the cookie-cutter and instances are the cookies.

But ultimately, set aside the notion of classes and instances being totally different and consider that a class is a Thing that (a) allows you to create new Things and (b) serves as a central reference for how those new Things should act. Those Things in turn could also create new Things, and so on.

In languages like C++ and Java, we normally perform (a) by allocating a block of memory with a set of properties defined by the class constructor, and (b) by maintaining some reference back to the class's methods (eg. via a v-table). But you could just copy an object, including all the method references within it, one of which would be the constructor for that object's 'instances'. This is prototype-based programming and provides a very simple inheritance model in that your 'classes' are just the same as any other object, except that they provide a way to create new objects that count as instances.

It just so happens that the world's most popular scripting language - possibly the world's most popular language - gives you prototype inheritance. Javascript is what I am referring to and it's a pretty good choice for embedding in a game, with the benefit of being widely known by developers and being very actively worked on today. There are many tutorials online, but one covering various inheritance approaches is here. It can take a while to get used to, especially if you're coming from a traditional OO background with a clear class/instance division, but it's well worth learning the differences and evaluating the pros and cons for yourself.


I'd go with Javascript. I'm working on a game engine myself that implements the V8 javascript engine used in Chrome. It's super easy to implement and Javascript is a very powerful language for that sort of thing, being entirely prototype-based.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜