【发布时间】:2013-01-15 06:39:15
【问题描述】:
我目前正在用 C++ 开发一款 3D 动作/RPG 游戏,在选择一种脚本语言来对游戏的 AI 进行编程时,我想要一些建议。我的团队来自模组制作背景,事实上我们仍在完成游戏哥特式模组的工作。在那个游戏中(我们也从中获得了灵感)使用了语言 DAEDALUS(由游戏制作者 Piranha Bytes 创建)。 Here is a full description of said language.
需要注意的主要一点是它使用实例多于类。游戏引擎已关闭,因此只能猜测该语言的内部实现,但我在脚本语言中寻找的主要内容(理想情况下会非常相似,但最好也比 DAEDALUS 更强大)是事实实际上存在 3 个类的“分离”——即类、实例和(实例的实例?)。
我认为如果我提供一个例子会更容易理解我想要什么。拿一个普通的NPC。首先,您定义了一个类(我理解)它反映了引擎内部的(类或结构):
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
};
然后,您还可以定义原型(设置一些默认值)。但是你实际上如何定义一个 NPC 是这样的:
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");
};
如您所见,实例声明更像是一个构造函数,从内部设置值和调用函数。如果不是因为另一件事,这仍然不会造成太大的问题:此实例的多个副本。例如,您可以生成多个BAU_900_Ricelord,每个BAU_900_Ricelord 都会跟踪自己的 AI 状态、生命值等。
现在我认为实例在引擎内部表示为整数(甚至可能是 NPC 的 id),因为无论何时(在脚本内部)您使用表达式 BAU_900_Ricelord 它只能分配给一个 int 变量,大多数在 NPC 上运行的函数都采用该 int 值。但是,要直接修改其生命值等,您必须执行类似var C_NPC npc = GetNPC(Bau_900_Ricelord); npc.attribute[ATR_HITPOINTS] = 10; 的操作,即获取代表它的实际 C_NPC 对象。
最后回顾一下 - 是否有可能在您知道的任何脚本语言中获得这种行为,或者我是否坚持必须自己制作?或者也许有更好的方式来代表 NPC 及其行为。对我来说,编写脚本的理想语言是 C#,因为我只是喜欢这种语言,但不知何故,我怀疑尝试在 C# 中实现类似的行为是否可行或确实可行。
非常感谢
【问题讨论】:
-
C# 被编译为 CLR,因此您可能不应该将它用于“脚本”,这通常是解释的。
-
请原谅我的命名约定,在这种情况下,我的意思是在不属于引擎代码但在引擎代码之外的任何内容中编写脚本,我实际上认为如果确实编译了脚本会更好(即DAEDALUS 也是如此)
标签: scripting