Jump to content
模组网
icedream

上古4脚本学习

Recommended Posts

本文转自《上古4脚本学习》,原作者为
一个月前才开始玩上古,被这个游戏异常开放的引擎所吸引,最近开始研究脚本,学习之余,想写一些中文化的教程之类,就是不知道是否游戏快过气了而没人响应,呵呵。。。不管吧,有兴趣的同学接着往后看。


总论:
上古4的脚本并非完整的语言
有别于魔兽使用的Lua,Lua是一门完整的自适应的精巧的编程语言,但上古使用的只是一些规则化的描述,甚至连循环语句都没有,写script的人只是使用这些简单的规则将上古开放的N多小功能块组织起来(当然,还包括obse增加的若干功能),完成想法。


要求:
1,有一定的逻辑思维能力
2,英语还算凑合过得去
3,记忆力大于等于常人
写上古4的script,要求最高的就是记忆力,并不需要太多编程技巧,基础的就行。为了搞懂每个功能怎么使用,一般的英文能力也是需要的。
为了简单明了,我会略去很多细节,这样比较方便新人学习,由于我也是最近才开始接触上古4的script,肯定会有一些错漏,各位发现请指正。

完整的上古4脚本教程请参考上古4的wiki:http://cs.elderscrolls.com/index.php?title=Main_Page

好了,我们开始!!!

一:字符和注释
1,空白字符都会被上古4的脚本引擎略掉,你可以写一个空格,为了排版,你也可以写N多空格
当然,你不能把一个英文单词拆开。。。

2,脚本引擎发现“;”时,会略去此分号后面直到行末的任何信息,所以,尽可能用“;”来增加注释,注意,此分号为英文分号!

3,有条件嵌套时,尽可能在前面使用tab键来进行缩进排版,我不知道老滚的脚本引擎是否会像Python一样强制要求tab键缩进,但为了符合规矩,请大家一律使用tab进行缩进,不要用空格代替。

4,所有字符大小写不敏感,也就是ABC和abc是一个意思,类似windows

二:变量和语法
1,上古只有4种基础变量类型
short            用于比较小的整数
long             用于比较大的整数
float            浮点,也就是小数
ref              其它对象的参考,或者引用,总而言之代表任何一个东东

2,当你需要保存一些状态,标志等信息的时候,就可以定义一个变量,然后把信息存储进去。使用方法:
short   svalue        ;一个短整形
long    lvalue        ;一个长整形
float   fvalue        ;一个浮点数
ref     this_is_ref   ;一个引用

3,变量赋值
上古4的变量赋值跟一般语言不同,是强制使用set命令来置值,语法:
set <某变量> to <某值>

变量在set之前必须先定义,范例:

float fvalue
set fvalue to 0.2
set fvalue to 0

ref a_ref
set a_ref to player   ;player代表玩家,也就是你的角色

4,全局or局部
上古4定义了一堆全局变量,这些变量可以在任何时候访问,你也可以增加全局变量,但绝大多数情况下,没必要这么干,全局变量的使用还有一些细节,这里略过。

在你的脚本文件中,你使用上述方法定义的变量,全部都会被当成闭包变量处理,老滚在这一点上做的非常优秀。

什么是闭包变量呢?
有编程基础的同学都知道,定义一个局部变量后,如果程序离开定义块,这个变量将失效。
闭包变量,简而言之就是对于你来说,这个变量是全局的,每次进入到你的脚本中,这个变量的值都不会失效,上次进入你的脚本,你把某值设置成了1,那么下次进入,这个值还会是1。
而对于老滚的引擎来说,这个值它是看不见的,也就是你不需要担心你定义的变量是否会跟其它人定义的变量名字相同而冲突。

一个特殊变量:fQuestDelayTime
它的值代表你想让你的脚本执行的频率多高,设置成1就是每秒执行1次,0.5就是每秒执行2次,但最快也就是1帧1次。
set fQuestDelayTime to 0.001            ;这样你的脚本将会在生效期内每0.001秒执行一次,但不会超过每帧1次

5,操作符
+:加
-:减
*:乘
/:除
%:取模
这些都不用解释了吧,注意:*和/的优先级高于+和-。%的优先级高于+和-但是低于*和/,所以在你不清楚优先级时,尽量多使用小括号()来确保你的意图。

&&:两个条件与,也就是这个符号左右的条件需要同时满足
||:两个条件或,也就是这个符号左右的条件满足任意一个即可
注意:||的优先级小于&&,所以,请多用()
还有一些比较高级数学操作,比如DR里面就使用了很多对数和三角函数。

6,条件
这是上古4的脚本中唯一的分支语句,语法:
if (条件)
  你想干的事
endif

if (条件)
  条件满足你想干的事
else
  条件不满足你想干的事
endif

if (条件1)
  条件1满足你想干的事
elseif (条件2)
  条件2满足你想干的事
else
  都不满足你想干的事
endif
这个也没什么好说的,任何一个语言都会有条件分支语句,条件中,你可以把N多条件使用&&和||放在一起,注意多用()保证意图就行。

三:执行块
begin <执行定义>
...
...
end

记得把你的所有变量定义放在begin的前面
而在begin和end中间放置你的所有执行语句
执行语句中可以使用return强行退出执行

执行定义:
游戏定义了很多执行定义,就是你脚本执行的触发时机,我们先了解最简单的一个:gamemode
意思是在所有非暂停(比如说你打开道具栏时)情况下,你的脚本将会每帧都执行,当然,受限于全局变量fQuestDelayTime
是的,每帧都执行!所以,请保证你的代码尽可能的快,在条件不满足时尽可能的早结束,如果太慢,影响FPS的程度可是很明显的哦!

好了,我们先了解这么多吧,下面是一个简单的定时器小例子,wiki上有原文。

我们要做一个简单的定时器,从游戏load后开始计时,5秒后在屏幕上打印一行信息,嗯,就这么简单,脚本如下:
 
scn timerTestScript
float timer
float fQuestDelayTime
short doOnce
begin gamemode
     set fQuestDelayTime to 0.001
     if ( doOnce == 0 )
         set timer to 5   ; Initialize the timer variable
         set doOnce to 1
     endif
     
     if ( timer > 0 )                                                
         set timer to timer - getSecondsPassed        
     else                                                               
         Message "Your 5 seconds are up"
     endif  
end
说明:
1,第一行scn timerTestScript
scn是引擎内定关键字,意思是脚本名称,timerTestScript就是我们这个小例子的名称。
所有脚本都要把这行放在最前面。

2,使用了begin gamemode执行块,这样从游戏load开始,在非菜单模式下,我们的脚本将每帧都执行

3,使用了特殊变量fQuestDelayTime,我们设置成0.001,这样,保证我们的脚本每帧肯定都会执行

4,if ( doOnce == 0 )
   我们这次执行是第一次,那么,我们初始化一个定时器变量timer,设置成5,然后把doOnce设置成1,表示我们已经初始化过了

5,if ( timer > 0 )                                                
         set timer to timer - getSecondsPassed
   如果timer的值目前大于0,也就是还没到5秒,我们更新这个定时器,把剩余时间设置成timer - getSecondsPassed。
   getSecondsPassed是内建函数,它会返回当前时间和上一次执行我们脚本的时间差,以小数计量的秒值。

6,else                                                               
         Message "Your 5 seconds are up"
   时间到,使用Message函数打印一行信息到屏幕上

很简单是吧,呵呵。。。其实老滚的脚本并不难,关键在于细心。

经常在混战中一不小心把自己的同伴MM给斩首了,哪怕设为皇冠也无用,异常郁闷。。。

Share this post


Link to post
Share on other sites

一起学习如何制作一个简单的法术mod

第一期介绍了一些上古4基本的脚本语法,看起来还有很多观望者,对脚本这潭水的深浅还持有怀疑态度,诚然,要想写出诸如midas,dr5这种高级脚本,是很困难的,但一些简单脚本并不难写,在第二期中,我将手把手的图文介绍如何写一个简单的脚本,如何使用CS制作一个简单的mod,以及一种通用的自动增加魔法或道具的方法。

前言:
第一期的回复中,有某人YY天上会掉下个林妹妹,呵呵,在上古的世界里,只有想不到,没有做不到,所以,我们下面要做的魔法名字就叫做:

天上掉下个林妹妹

先看看效果吧:
【图片已失传】

某日,我百无聊赖
【图片已失传】
 
好无趣啊,老天给点乐子吧!
【图片已失传】
 
嗯?天上貌似有什么东东。。。
【图片已失传】
 
哇!掉下来一个美女,还是。。。全裸的。。。
【图片已失传】
 
呃。。。居然是。。。林妹妹!!!


呵呵。。。有点意思吧,如果你对这些东东有兴趣,不妨准备好你手头的工具,跟我一起做!

必要准备
1:CS
2:OBSE

为什么要obse呢,我们的简单mod中没有用到obse的任何附加功能,但作为一个modder,各位打开CS的方式要改变一下,建立一个快捷方式:
【图片已失传】

以后我们每次双击这个快捷方式来打开CS,这样会让你在CS中拥有OBSE的辅助功能!

首先打开上古4的主esm文件:
【图片已失传】
 

上古中的脚本有三种:
Object:跟对象绑定的script,什么苹果桔子,家具碗筷等都是Object
Magic effect:魔法效果脚本
Quest:线索任务类脚本
我们的小例子中,将会用到后2种



好,一切就绪,我们正式开始!!!


一:制作新法术
我们需要制作一个mod,新建一个法术,首先找到左边浏览栏的spell标签
【图片已失传】

然后在右边那一堆法术框的任意地方右键,选择new
输入ID和name。name随便写,ID你可以使用aaa开头,这样这个法术会排列在spell列表的上方,方便查找。
Type是类型,我们要增加的是法术,所以选择spell,当然什么次级威能之类也是可以选的
【图片已失传】

其它都不用动了,注意把右下角的Auto-Calculate勾掉,不要选(我图上忘了勾掉)
这样我们的法术将不会有任何施法限制!

法术新建好了,但它怎么体现作用呢?我们需要的是脚本效果,所以我们要先写script
【图片已失传】

然后
【图片已失传】
 
在打开的编辑框里,我们输入以下三行:
【图片已失传】
 
这个脚本没有任何效果,先暂时卖个关子,让大家着力与建设,后面我们再回到这个脚本上来
注意上面script type中要选择Magic Effect,表示这个脚本是魔法效果脚本
好了,保存,关闭

回到我们新作的那个魔法窗口,在右边空白的Effect栏中任意地方右键 -> new
【图片已失传】

effect下拉框中,选择script effect,表示这个法术使用脚本作为效果,还有很多别的,各位有兴趣可以看看
其它的就不用动了,保持默认就好,range表示这个魔法是对自身的,接触类的,或是目标类的
在下面的script下拉框中,选择我们刚写的那个魔法脚本,也就是第一行scn开头的名字,CS会自动把这个脚本添加到
下拉框中,其余不用动了,点OK!

嗯,到这里应该就算基本完成,我们制作了一个法术,并使用一个脚本作为法术效果,现在可以点击保存:
【图片已失传】


然后用OBMM加载,就完成了这个新mod的制作工程!

等一等,等一等!这个法术俺在游戏中怎么用?不会还要用控制台吧,能不能想大多数增加法术的mod一样,自动让俺学会呢?

呵呵,当然可以!下面我使用一种比较通用的方式,教大家自动把魔法或物品添给玩家

二:自动增加
看了一些法术类水晶球类的mod脚本,差不多都使用同一种方法,简而言之,就是借助任务线索,来自动完成物品添加

既然是Quest,那么我们首先要新建一个小Quest
【图片已失传】

这样会打开quest窗口,老方法,任意地方右键 -> new 新建一个
【图片已失传】

在弹出的编辑框中随便写一个ID,只要自己记住就行
点下OK后,会弹出Quest的详情框
【图片已失传】

名字随便写,Priority写上60,默认情况下,Start Game Enabled前面的复选框是勾上的,也就是当你load一个存档或开始新游戏时,此Quest自动生效!
右边,我们又看到了Script字样!

点击三个点的按钮,打开脚本窗,我们用前面的方法再新建一个script,写上如下几行:
【图片已失传】

意思简单解释一下:
1,上面的Script Type记得选中Quest
2:第一行文件名,不用多说
3:fQuestDelayTime为特殊变量,表示脚本执行的频率,我第一期中有详细描述
4:关键来了:“player.addspell aaajokeSpell”,就是这行帮你增加了法术,后面这个ID就是之前我们新做法术的ID
5:增加完了,避免系统开销,aaajokeQuest这个Quest已经可以结束,是填你刚新建Quest的ID哦,不是名字!
完事,保存并退出脚本窗

回到Quest的详情窗,这里CS有个小毛病,刚新增的脚本不会即使刷进script的下拉框,所以,我们先退出详情窗口,然后在双击进去,script中选择我们刚新建的Quest脚本
【图片已失传】

点击OK后,我们在看一下法术列表:
【图片已失传】

users已经变为了1,说明有1个绑定了!

接近完成了,各位再坚持一下,最后回到我们之前没有写完的法术效果脚本上来,以下是这个简单的脚本:

scn aaajokefalllinscript
long hp
ref miss_lin

Begin ScriptEffectStart
Set miss_lin to player.CreateFullActorCopy
miss_lin.SetActorFullName "林妹妹 "
miss_lin.RemoveAllItems
miss_lin.PlayGroup idle 1
miss_lin.moveto player 0, 0, 800
End


说明:
1:ref miss_lin
定义一个引用变量

2:Begin ScriptEffectStart
法术执行块,此块会在法术施放的一开始就调用,而且只会调用一次

3:Set miss_lin to player.CreateFullActorCopy
林小姐不过是我们主人公的一个拷贝,这个拷贝包括武器,道具,等级,技能等等所有的东西

4:miss_lin.SetActorFullName "林妹妹"
嘿嘿,原来是叫做“林妹妹”啊
注意,SetActorFullName会改变所有引用对象背后的真实base的名字,因为林妹妹是从你身上拷贝的,所以现在你也叫做“林妹妹”了,呵呵。。。

5:miss_lin.RemoveAllItems
林妹妹被晒光。。。我承认我邪恶。。。

6:miss_lin.moveto player 0, 0, 800
林妹妹从800米高空急速拥抱你。。。


啊,终于收工了!保存esp文件,进游戏看效果喽!!!!!

load完成后,我们看到了熟悉的提示
【图片已失传】

打开物品栏
【图片已失传】

呵呵。。。一切都那么自然,是吧,找个空旷的地方接你的林妹妹去吧!!!

Share this post


Link to post
Share on other sites

有同学PM俺如果写script,看来有必要把这个帖子再顶一顶。。。
如果你具有以下条件,就完全可以自己写老滚的脚本:
1,有基础的编程功底
2,心中有剑手中无剑的逻辑思维能力
3,有适当的英文能力,至少要能大致看懂老滚的wiki
4,有强烈的将YY变为现实的决心和自己动手丰衣足食的信心


最重要的是:一定要实践!!!!!
俺也是从一个简单定时器开始学习的,大家起点都差不多。。。

如果就这么顶一下,有挖坟的嫌疑,所以写下第三讲
我知道没几个人会认真看,只是希望为真正有心写脚本的人稍微降低一点门槛。。。


正文

一:上古4的MOD术语和概念
1:EditorID 和 FormID

EditorID
* 大多数物品都有EditorID,但不是所有!

* EdirtorID是用于标识一个物件的名称化ID,在你的mod中,你可以而且应该使用EditorID来作为你的操作对象。
* 但是EditorID不是上古游戏引擎认识的ID,所以如果你要操作其它mod中的物品,必须使用FormID。

FormID
* 所有物品都有FormID!

* FormID才是游戏引擎认识的ID,但在CS中看到的FormID和游戏中看到的FormID是一个意思,但可能不是同一个值!
* FormID的格式为16进制的8个字符串:XXzzzzzz,如010003E7
其中XX为mod的加载编号,你用CS打开则是你勾选的esp的排列顺序,游戏运行时则是游戏加载mod的排列顺序,所以是不一样的。
比方说我们用CS打开了obvilion.esm和一个esp,那么所有在obvilion中出现的物品的XX都为为00;而这个esp中创建的物品的XX将会是01
但是你在游戏时,这个esp很有可能不是排在第一位,所以游戏中这个esp中的物品的XX就会变成游戏真正加载这个esp的编号!
注意:如果你想在脚本中操作其它mod的内容,必须动态计算游戏时这个mod的真正加载编号。

2:object和reference
上古4的CS已经有很多人在用,但是CS中的名称却很混淆,我简单归纳一下:

在CS左边浏览栏能看到很多下拉列表,最常使用的是NPC和ITEM,但这两类物品有本质的区别,这里使用一点编程术语来帮助理解:

* NPC可以看成是Class,它的FormID也是ClassID,在脚本中无法直接操作,必须先实例化,才能被操作。
NPC的这个Class称为base object,实例化出来的Object,称为reference。

* ITEM则不同,可以看成是static Class,它的FormID是可以在脚本中直接使用的;简单来说,对于ITEM,你可以认为base object和reference是一个意思。

wiki上对这两者给出了详细的解释,但不是很容易让新手理解,所以初期可以使用我的描述。

注意:有些函数是可以兼容base object和reference的,有些则只能用在base object上,有些无论使用哪个做操作对象,都会改变base object的属性值。


3,对象,永久对象,动态对象
这是老滚中最让人头晕的概念,可以这么理解:

比方说你在CS中将一个NPC拖到了一个cell中,这是,就从NPC的Class中实例化出来了一个Object,你可以指定这个Object是否成为永久对象(persistent reference)
一旦设为永久对象,这个信息就将记录在存档里面,在任何时候都是可以操作的。否则这个NPC被杀掉后,他的reference将可能会失效。

注意:在脚本中,无法直接从NPC的类中生成对象,也无法创建永久对象,只能在CS中静态生成!

动态对象
这是个可怕的概念,也是wiki和talk上讨论最多的概念,简单来说你最好避免在脚本中生成此类对象,也就是说无论何时何地,尽量不要使用PlaceAtMe函数!
哪怕就是DR这种牛人牛脚本都无法完全把控住这类对象,反正我是不敢尝试。。。



二:高级数据类型和流控
1,obse字符串和数组以及相关表达式语法

obse0016引入了字符串变量,0017完善了这一类型,并引入了数组变量,0018又进一步完善了数组功能。

现在很多mod都使用了这两种变量,其中包括大多数同伴mod和几乎所有的和谐mod以及著名的破损装甲framework。
所以请务必持续更新你的obse版本,目前最新的是obse 0018 beta4,注意这个包里的dll是0018.3,你需要下载obse 0018 beta5,并用里面提供的0018.4的dll进行替换

* 字符串变量定义:
string_var str

* 数组变量定义:
array_val arr

相关操作函数请查阅obse命令文档

2,obse引入的扩展表达式语法
obse的字符串和数组变量,有时候要求操作语句必须为obse的扩展表达式语法,就是类似如下语句:

let varA := varB

详细情况请参阅obse命令文档


3,pluggy字符串和数组
obse的插件pluggy,也提供了字符串和数组类型以及相关的操作函数集合,不能说pluggy的和obse自带的哪个好,只能说各有所长!
在俺的新mod中,两种字符串变量和两种数组变量同时都使用了。。。

pluggy的字符串和数组,复用了原始数据类型long

注意:obse的字符串和数组与pluggy的字符串和数组是独立的,不具有互用性!


4,高级流控
* cs1.0中,只有一种流控:if,elseif和else
* cs1.2中,提供了一种可用于循环的流控:label和goto
* obse中,提供了更为方便和直白的循环控制:while,foreach,loop,break,continue
* obse0018中,还提供了用户自定义函数,现在也已经被广泛使用,尤其是日系mod,不得不佩服小日本的钻研精神!




好了,工作已经成堆,先写这么多吧。。。希望有心人一起加入老滚的modder行列!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...