李锦俊(mybios)的blog

游戏开发 C++ Cocos2d-x OpenGL DirectX 数学 计算机图形学 SQL Server

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  86 Posts :: 0 Stories :: 370 Comments :: 0 Trackbacks

公告

QQ:30743734
EMain:mybios@qq.com

常用链接

留言簿(16)

我参与的团队

最新随笔

搜索

  •  

积分与排名

  • 积分 - 362217
  • 排名 - 66

最新评论

阅读排行榜

评论排行榜

注意:本文纯属是本人从研究魔兽争霸III地图编辑器的过程中的一种猜想,所以,不当之处,还请高手指出。谢谢!

  魔兽争霸III我以前玩得比较多,也听说他的地图编辑器非常牛X,以前也曾经想编辑个地图出来。但是,限于当时得水平问题,没有成功。直到最近在研究如何制作游戏的时候,打开魔兽争霸III的地图编辑器来看,突然有一种扩然开朗的感觉!哦!原来地图编辑器是这样出来的!闲话不多说!马上进入正题!

  魔兽争霸III的地图编辑器使用了一个文件来代表一个地图,地图里包含了什么东西?我无从得知,但是,从他的编辑器上看,看到编辑器能对地图修改的东西,就可以大概猜想到有哪些东西。仔细看地图编辑器,看到有那么几个模块:地形编辑器、开关编辑器、声音编辑器、物体编辑器、战役编辑器、AI编辑器、物体管理器、输入管理器。我把我的理解逐一说来。

  1.地形编辑器
    主要用来编辑地形,例如某个地方摆放什么地形,什么地方摆放什么物体、英雄、灯光等等东西。那么,这些东西在地图文件中我想是以数据的形式来存放的,而不是脚本!因为,我测试过,放了一个英雄到地图中,然后导出脚本,但是脚本里没有任何关于这个英雄的资料!那就证明了是用某种格式保存在地图文件中。整个地形都是这么存放。

  2.开关编辑器
    开关编辑器,也就是触发事件编辑器。这个东西在地图编辑器中比较的高级,人们都说魔兽争霸III的地图编辑器是万能编辑器,很大的原因就是因为有了这个东西!这个东西是如何实现的呢?说起来很简单!就是脚本实现!我分析了一下,一个触发器,分成三个部分:发生事件、触发条件、执行动作。那么三个东西在脚本中和程序中是如何实现?
    让我在编辑器中新建一个触发器解释一下。新建一个触发器,叫做TestTrigger,在这个触发器下新建一个事件“玩家 - 玩家1 (红色) leaves the game”,新建一个条件“TRUE 等于TRUE”,新建一个动作“Do nothing”。然后导出脚本,看看脚本如何:
//===========================================================================
// 
// 只是另外一张魔兽争霸III的地图
// 
//   Warcraft III map script
//   Generated by the Warcraft III World Editor
//   Date: Sat Nov 18 23:35:12 2006
//   Map Author: 李锦俊
// 
//===========================================================================

//***************************************************************************
//*
//*  Global Variables
//*  全局变量
//***************************************************************************

globals
    
// Generated
    
//  我们的触发器保存成一个全局变量了!!
    trigger                 gg_trg_TestTrigger         = null
endglobals

function InitGlobals takes nothing returns nothing
endfunction

//***************************************************************************
//*
//*  Triggers
//*  触发器!
//***************************************************************************

//===========================================================================
// Trigger: TestTrigger 我们的触发器的触发条件
//===========================================================================
function Trig_TestTrigger_Conditions takes nothing returns boolean
    
// 如果true == true ?? 不正是我们设置的“TRUE等于TRUE”吗
    if ( not ( true == true ) ) then
        
return false
    endif
    
return true
endfunction

// 我们的触发器的执行动作!!
function Trig_TestTrigger_Actions takes nothing returns nothing
    
// DoNothing ?? 不正是我们设置的“Do nothing”吗?
    call DoNothing(  )
endfunction

//===========================================================================
// 初始化我们的触发器
function InitTrig_TestTrigger takes nothing returns nothing
    
// 创建一个触发器,保存在一个全局变量里
    set gg_trg_TestTrigger = CreateTrigger(  )
    
    
// 看这个英文的函数名。。我翻译一下应该是“触发器:注册玩家单位简单事件”
    
// 再看看参数
    
// 第一个参数是我们的触发器的全局变量
    
// 第二个参数是Player(0)啊,不就是我们设置的“玩家1”吗?(语言上从0开始,显示上从1开始,习惯了)。
    
// 第三个参数EVENT_PLAYER_UNIT_DEATH,翻译一下应该是“玩家单位死亡事件”,哈哈!很明显又是我们设置的
    call TriggerRegisterPlayerUnitEventSimple( gg_trg_TestTrigger, Player(0), EVENT_PLAYER_UNIT_DEATH )

    
// 翻译:“触发器:添加触发条件”,然后参数就是上面那个触发条件的函数,函数里就是我们设置的条件
    call TriggerAddCondition( gg_trg_TestTrigger, Condition( function Trig_TestTrigger_Conditions ) )

    
// 翻译:“触发器:添加执行动作”,然后参数就是上面那个执行动作的函数,函数里就是我们设置的动作!
    call TriggerAddAction( gg_trg_TestTrigger, function Trig_TestTrigger_Actions )
endfunction

//===========================================================================
// 地图初始化的时候都会调用这个函数初始化所有自定义的触发器,当然也有我们的触发器了。InitTrig_TestTrigger不就是刚才那个函数了吗
function InitCustomTriggers takes nothing returns nothing
    call InitTrig_TestTrigger(  )
endfunction

//***************************************************************************
//*
//*  Main Initialization
//*  main啊!这么熟悉!一定是此脚本文件的入口函数了!在C++主程序中调用的!(我暂且认为WarCraftIII是用C++写的了)
//***************************************************************************

//===========================================================================
function main takes nothing returns nothing
    call SetCameraBounds( 
-3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), -3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), -3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), -3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM) )
    call SetDayNightModels( 
"Environment\\DNC\\DNCLordaeron\\DNCLordaeronTerrain\\DNCLordaeronTerrain.mdl""Environment\\DNC\\DNCLordaeron\\DNCLordaeronUnit\\DNCLordaeronUnit.mdl" )
    call NewSoundEnvironment( 
"Default" )
    call SetAmbientDaySound( 
"LordaeronSummerDay" )
    call SetAmbientNightSound( 
"LordaeronSummerNight" )
    call SetMapMusic( 
"Music"true0 )
    call InitBlizzard(  )
    call InitGlobals(  )
    
// 上面执行的这几个函数主要是设置一些环境变量,什么摄像头、日昼几何模型、背景音乐等等
    
// 这个函数就是初始化我们的触发器啊!
    call InitCustomTriggers(  )
endfunction

//***************************************************************************
//*
//*  Map Configuration
//*  地图配置,估计也是在C++主程序中调用的!
//***************************************************************************

function config takes nothing returns nothing
    call SetMapName( 
"只是另外一张魔兽争霸III的地图" )
    call SetMapDescription( 
"没有描述" )
    call SetPlayers( 
1 )
    call SetTeams( 
1 )
    call SetGamePlacement( MAP_PLACEMENT_USE_MAP_SETTINGS )

    call DefineStartLocation( 
0-1409.3219.2 )

    
// Player setup
    call InitCustomPlayerSlots(  )
    call SetPlayerSlotAvailable( Player(
0), MAP_CONTROL_USER )
    call InitGenericPlayerSlots(  )
endfunction

  我加的注释里已经写得很清楚了,很明显我们在地图编辑器里面设置的所有触发器的东西,都会以一种脚本的形式生成,然后程序在根据地图数据初始化地图之后调用这个脚本的main函数和config函数。在游戏进行过程中,TriggerRegister开头的函数注册了一些事件开端,简单的实现就是维护一个列表而已。然后游戏进行到这个事件的时候(例如刚才的例子里是TriggerRegisterPlayerUnitEventSimple,则玩家单位发生一些简单事件的时候就会执行),就遍历这个列表的每一个元素调用他们用TriggerAddCondition注册的条件,如果为true,则执行TriggerAddAction函数的内容!触发器的实现原理基本上就是这样了!当然,真正实现起来还是有很多东西要做的,例如要有很多很多的函数要绑定到脚本引擎中!

  3.声音编辑器
    这个我没研究过啊,待续....

  4.物体编辑器
    里面就是所有单位、建筑物、物品(简称Items吧,中文翻译过来就变味了,呵呵!)的一些属性设置,这些属性都是以数据的形式保存在地图中的,程序运行的时候就以这些数据来基础运行起来,例如某某英雄声明值是多少云云....

  5.战役编辑器
    没搞过,不多作评论

  6.AI编辑器
    我只知道AI不是脚本引擎实现的!他只是一些数据,至于为何不用脚本来实现?我想应该还是执行效率问题吧!(猜想而已...)

  7.物体管理器
    用来管理场景上出现的物体,也是数据保存。

  8.输入管理器
    实现一个简单的单文件系统,就是说在一个地图文件里包含多个附件进去,例如:模型、贴图等等。那么就可以实现一个自定义的东西了(例如自定义一个以我的形象做出来的英雄!哈哈!)。然后程序可以直接调用这个模型。

  另外,说说C++程序中应该如何导入绑定函数到脚本中,绑定的方法很多文章都有说,我的BLOG上LUA那部分也有几篇这样的文章。我现在要说的是应该绑定什么函数进去脚本引擎中。我觉得,脚本引擎主要是实现一些速度要求不高,但是逻辑性又极强的代码。那么,相反,对速度要求高,逻辑性比较固定的,就不应该写到脚本引擎中。
  举几个应该在脚本中实现的例子:注册触发器、触发器的执行条件、触发器执行的事件、地图初始化之后应该设置的信息(例如摄像机的位置,Load一些东西),这些东西都是逻辑性比较强,也就是说变化是比较大的,如果用数据来驱动,估计很难驱动得了(就好像,N个条件组合,还有逻辑判断的东西,如何能用数据来表示?例如:3D中的可编程流水线(Shader脚本驱动)就有很多固定流水线(渲染状态数据驱动)无法实现的)。
  举几个不应该在脚本中实现的例子:寻路算法、读取文件等等需要比较多系统资源的操作。AI,是一个特例,他执行速度要求比较高,但是他的逻辑性又比较强。暴雪公司的选择是速度,那么他们就需要作一个比较庞大的数据来驱动这些AI(这里编程估计比较复杂了)。我还没学过AI的算法呢,只知道复杂。不多作评论了,还请高手们给指点指点。
  举了几个比较有代表意义的例子。简单的说,就是应该把一些固定功能的函数尽量用C++来实现,然后绑定到脚本引擎中,然后由脚本引擎来实现相应的逻辑。

  好了,大的东西基本上都说完了。好像也没说什么,主要就是分析了脚本的实现了。可能说得不明白啊,大家回帖讨论一下。

  PS:第一次写这种文章,写得不好请见谅!

如果本文对你的开发有所帮助,并且你手头恰好有零钱。

不如打赏我一杯咖啡,鼓励我继续分享优秀的文章。




posted on 2006-11-19 00:20 李锦俊(mybios) 阅读(8533) 评论(9)  编辑 收藏 引用 所属分类: C++LUA

Feedback

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本) 2006-11-28 16:16 没仔细看
搂主继续努力  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本) 2006-11-28 22:28 李锦俊
谢谢支持  回复  更多评论
  

# 我最近也在研究魔兽地图编辑器的脚本实现 2007-07-23 11:10 goodboy
受益匪浅,希望楼主多发布一些相关的文章。  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本)[未登录] 2007-08-08 09:21 初学者
杀死敌人单位(非中立敌对)后怎样能获得金钱?(请各位帮忙)  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本) 2007-08-20 08:24 jj
不错不错!!o稀饭^_^  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本)[未登录] 2008-02-10 17:10 QQ
怎样用魔兽地图编辑器把一个不是英雄的单位变成英雄 说说吧  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本) 2008-02-14 11:27 #
这个把单位变英雄我会,只要调模形,声音,图标和武器声音。单位就能变英雄了。  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本) 2008-02-14 11:29 接上
补充下:改的单位要是英雄才行!  回复  更多评论
  

# re: 【原创】魔兽争霸III 地图编辑器原理(关键是脚本) 2009-03-03 10:21 peerincle
加油啊~~
期待着你的最新研究成果~  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理