Starbound

Starbound

48 ratings
Starbound Lua入门教程
By What happened to you
一份Starbound lua编程入门教程,不定时更新,需要的可以看看。
   
Award
Favorite
Favorited
Unfavorite
前言以及基础知识
steam指南无法排版,已经将教程传到B站https://www.bilibili.com/read/cv5983713
-------------前排-----------
这是一份starbound lua入门性教程。
学习条件:
①你需要至少学过一门编程语言,不是Lua语言也可以比如C语言,但是必须学过任意一门编程语言。如果还没有任何编程基础,建议学一下Lua的基础语法。
②非常熟悉starbound中各种json文件,可以自己独立的做json的东西,比如常见的items、projectiles,objects、monster等。如果并不了解可以看我另外一个基础的mod教程,那份教程是json入门教程。
以上是必备条件,如果不具备这个条件,这个教程很难对你有用。
如何学习:
刚开始入门是非常痛苦的,每一节都有我演示写的东西,你需要自己重新写一遍教程里的演示的这些代码,写的多了慢慢自己就可以独立写东西了。

Starbound官方提供了doc文件,在starbound/doc/lua下,这些文件打开就能看到官方提供的各类Lua函数。
如果你觉得打开这些文件比较烦也可以访问维基wiki[starbounder.org],打开这个链接就可以在线去查,维基这边应该有些1.4版本更新的函数没有,不过影响不是很大。
从游戏技术层面来说,比如Starbound,他的底层语言是是C++,lua是一个中间性的脚本语言,可以比较方便的编写逻辑,而json文件则是一个配置。一个实体的运行机制你可以简单的理解为lua或者底层C++读取json配置,获取相关的参数,然后按照编写的逻辑运行。关于这部分可以了解一下,实际要比说的复杂的多。

Hello Lua!
学习之前,我们先定一个小目标,做一个最简单的科技型mod,效果如图地址[www.bilibili.com]
按下键盘上的G即可瞬移当前光标的位置。
为了弄清科技mod怎么写,先来分析一下原版里的dash这个科技。定位到解包文件中的/tech/dash文件夹,实际上dash这个科技也就三个文件。dash.tech 是个json文件,你应该比较熟悉了,这个json文件配置的脚本文件就是dash.lua。
打开dash.lua文件,简单的用notepad++打开即可。这个lua文件有一百来行,对于新手来说读起来还是很困难的,并不需要读懂,这次我们只需要分析lua函数调用规则即可。(为了截图方便有些函数我把他缩进了)
相信你在编写Starbound Json类mod时应该接触过很多东西了,比如items、projectiles、monster等等,这些我们统一称作实体。Starbound的所有实体的lua脚本都会有init、undate、uninit这三个函数,tech(科技)实体也不例外,上面的dash.lua就有着三个函数。这三个函数是由Starbound脚本引擎自动调用的,分别对应实体初始化,实体更新、实体销毁这三个过程,可能你还是不清楚这三个函数是什么时候被脚本引擎调用的,那么我们来做一个实验就知道了。先来看一下官方提供的一个函数 sb.logInfo,这个函数在官方文档utility.md可以查找到。他的功能呢类似于lua中的print()。作用就是在log文件中打印字符串。
我们尝试直接在dash.lua的init函数中加上这一句(注:这里为了讲的方便所以直接改源文件了,下一节我会详情说怎么以MOD形式展现。除此之外为了让这个改动生效你需要把原来的资源包删除,也就是packed.pak,只留你解完的包,这会让第一次打开游戏变得非常慢可能需要等十来分钟,原因我也不清楚需要耐心等待,可能是Starbound加载这种解完包的文件比较慢,总之这种方式只是为了测试方便)

打开游戏后,我们装上冲刺科技,来看一下log文件,可以看到已经打印出hello lua这个字符串了。
你可以尝试传送到一个星球,再看看log会发现又打印出一条,其实这就是init函数的调用规则了,当冲刺科技这个实体初始化的时候,init函数会被lua引擎自动调用,所谓冲刺科技初始化你可以把他看做是人物初始化,因为科技是安装在人物上面的。人物在传送进世界时都会出初始化一次,离开这个世界时会被销毁也就是uninit,至于update呢,你可以试试自己加一条 sb.logInfo函数到update函数中看看log的情况。
这次先说到这里,下次会告诉大家mod方式怎么做。




Lua mod编写(上)
前一节我给大家分析了dash这个科技的lua脚本,我们得出一个结论,当玩家进入一个世界时(dash实体初始化),init函数会被调用一次,玩家离开世界时(dash实体被销毁),而update函数其实是无时无刻在被脚本引擎调用。其实也很好理解,就算再简单的游戏,无论是怪还是玩家,只要是动态的,我们都必须不停的刷新他的动画状态,一旦他的位置或者图像发生变化,我们需要重新设置,不然他就是静止的了,这就是update函数。
对于新手来说,完全编写一个新的科技mod困难度是非常大的,我们可以借用原版的科技添加自己的逻辑代码来实现我们一开始定的目标。如何借用呢,我们已经分析了dash.lua中函数的调用规则了,所以我们可以把自己写的代码挂接到dash.lua上。具体方法如下:
在mods文件夹建立以下结构,
这是标准的patch结构,相信你已经很熟悉了,我们对dash.tech 这个json文件进行patch
含义就是给 dash.tech 的scripts属性之增加一个demo.lua,之后在demo.lua中写入以下代码
这就是一个最简单的lua类型Mod了,在打开游戏后,你会发现效果和我们刚开始前直接改动源文件一样。简单的看一下这段代码,非常简洁。当我们在 dash.tech 中的scripts属性中加入了demo.lua后,科技实体在加载dash.lua时也会加载demo.lua。demo.lua做的事很简单,把原来dash.lua中的init,update、uninit函数都先保存到变量ini、upd、uni中,自己定义了init()、update()、uninit()这些由脚本引擎自动调用的函数,相当于覆盖了原来dash.lua的这些函数。那之后脚本引擎就会调用demo.lua中的init、update、uninit函数了。但是我们需要同时调用一下原来dash.lua的这些函数,不然dash这个科技原来自己的功能就没有了,所以会有这样一个架构,这也是一个通用的挂接型架构模板。那么下面的事就很简单了,你可以在你自己写的demo.lua中随意发挥了。






Lua mod编写(下)
为了实现瞬移到光标位置,我们要来查文档了,看看有没设置玩家位置的函数。定位到actormovementcontroller这个文档,可以看到有一个mcontroller.setPosition的函数,这个就可以设置当前实体的位置了,可能是玩家也以是monster、npc。看你把这个函数用在哪里了。这里我们用在科技实体上当然是设置的玩家的位置了。有人可能会问你怎么找的这些函数,官方提供了很多函数,这需要你经常去翻这些文档,每条函数的下面都写了他的作用。可以看到这个函数接收一个位置参数,但是这个参数类型是Vec2F,这是什么类型?并不是lua的基础类型。
这里你可以用FileLocator去搜索解包文件中的lua文件,肯定有Lua文件用到这个函数,然后看看他的参数怎么定义的。这里我就直接说吧,Vec2F其实就是个两元素的数组,比如mcontroller.setPosition({200,300}),这句话就可以把你设置到你当前世界的(200,300)坐标。
下面就是把这句话加到我们的demo.lua中,先加到init中,看看有没有效果。
这里教大家一个快速调试的办法,在游戏中用/reload就能让游戏重新加载修改后的文件。可以看到我一用/reload结束,人物就被设置到200,300这个位置了(使用指令/debug可以看到调试信息),调试的时候最好,把这个坐标改一改,这个坐标可能是个虚空。

当然这个函数放在init中只是为了测试而已,我们的目标是按下键盘的G后把自己瞬移到光标位置。在update中加入以下代码。
args.moves['special2'] 其实就是获取当前的键盘G键按下信息,关于args的结构如下:
{ "dt": 0.0166667, "moves": { "jump": false, "special3": false, "right": false, "primaryFire": false, "altFire": false, "up": false, "run": false, "left": false, "down": false, "special1": false, "special2": false } }
科技lua的args参数可获取的值非常丰富,这也是为什么选择把自己要实现的功能挂接到科技lua上,根据moves表,我们可以判断是否按下了一些键。可以判断的键有这些up、down、left,right,special1、special2、special3。其中special1、special2、special3分别对应键盘上的F、G、H(默认情况,你可以在设置里改),up、down、left、right比较好理解不多说了。primaryFire、altFire分别对应鼠标左键和右键。此外还能获取到当前人物是否是跑动的比如run。这些都是科技lua独有的,假如你用的其他实体的lua比如activeitem就不一样了。
到这里就实现了我们刚开始定的小目标了。这虽然是一个很小的demo,甚至特效也没有做,但是如果你能完全理解这些,恭喜你已经跨入Starbound lua编程的大门了!
注:此次写的demo也传到创意工坊了,需要的可以下载解包看看。demo




Lua mod编写小结
教程到这里,其实你已经有独立学习其他实体lua的能力了,Starbound的lua脚本不外乎init、update、uninit这三个函数。当然不同的实体可能会有额外被lua引擎调用的函数,比如在activeitem实体中,会有activate这个函数,这个函数会被调用当使用这个物品时。object实体中,会有onInteraction这个函数,当物品被交互会被自动调用。在维基这里也有人整理了,你可以点到这里去看。
这里是别人整理的一些实体的回调函数,如果你需要研究一种实体的lua脚本最好还是去Starbound解包文件中找,虽然很多lua文件可能写的比较复杂,但是如果可以坚持去看对你的编程水平提升会有很大帮助。
关于官方提供的这些函数在使用时需要注意一点,有一个“域”的问题。当然这是我个人起的名词,简单来说,不同实体他能使用的函数都不一样。这一点你翻文档时,在文档最上面是有介绍的,比如下面这个tech.md。
这句话的意思就是,这里的函数只用于tech实体,如果你写的是一个activeItem的lua脚本那是不能使用这些函数的。同理在tech实体的lua脚本中你也不能使用activeItem提供的函数。同样你可以去看一下我们用的那个设置玩家位置的函数的Actormovementcontroller介绍,就明白我的意思了。






服务端和客户端的一些知识
    在开始编写lua mod后,我觉的关于服务端和客户端的一些知识你也应该有所了解。其实游戏中各种实体其实可以分为两类,一类是客户端型,一类是服务端型。个人理解客户端型实体不说百分之百,至少百分之九十以上功能是在客户端上实现的。服务端型同理。
    如果你曾经联机过或者进过服务器玩过,应该比较好理解这个分类。有些mod在服务器上并没有安装,但是你仍然可以携带这种mod进入服务器并且功能一切正常。这种mod则属于一种客户端型mod。他的功能全部在本地端运行,服务器对此类功能完全忽略。
    我大体整理了一下游戏中的各种实体的所属的类别,这些一部分是我自己总结的,另外一些是在国外论坛上看到的,不一定完全对,仅仅做个参考。
    有一些实体在可能是客户端实体也可能是服务端实体,比如中间交叉的那一部分需要根据你的编写情况确定。
制作一个探测器(上)
    从这一节开始,会带大家做一些简单的mod帮助大家熟悉Starbound中的各种函数。首先我们来做的是这样一个小工具,演示地址如下探测器[www.bilibili.com]。左键按下这个探测器会在光标处显示当前光标位置物品的id和position。

    我觉得这个工具对于Modder来说是很有用的,你经常需要参考一些原版里的物品来当模板,如果知道ID就能很快定位这个物品。功能比较简单,接下来我们一起来做吧。

    首先建立以下结构:
    其实这个mod就这三个文件,为了要符合原版"activeitem"物品的标准我才建立这样的结构,你放在其他文件夹其实也行。首先是superinspection.activeitem这是这个物品的json配置文件,他的内容如下,因为这个教程主讲Lua,所以关于json方面的东西我都尽量简化了。
    这个配置比较简单,还记得在第一节的内容吗?Starbound的运行机制就是底层C++或者Lua调用配置文件来运行的。这里这个配置文件的上面几个属性比如inventoryIcon、description他会被底层调用来设置探测器这个实体的图标描述信息等,最后一个mode属性是我自定义的可以在lua中进行获取使用。

    下面这个是animation文件,是物品的动画配置,关于动画部分我想有机会在后面单独起一节来说,或许你在编写json类型mod时已经自定义过动画了,但是仍然对这部分可能比较模糊,因为动画配置相对来说还是比较复杂的,这里暂时跳过。
    接下来是比较重要的lua文件,这是一个标准的activeitem lua文件结构,这四个函数都会被lua脚本引擎自动调用。前三个你应该比较清楚了,第四个函数是当这个物品使用时会被自动调用,其中fireMode表示按下的鼠标左右键,shiftHeld表示是否按下shift键。
    先来做左键功能吧,左键按下时我们需要在光标处显示这个位置object的信息。思路则是:获取当前光标位置 ->根据光标位置查找此处的实体 ->显示实体的信息。定位到doc,既然是编写activeitem我们就来查一下他所能使用的函数吧。文档一打开我们就找到我们需要的函数了,第三条就是了。
    使用activeItem.ownerAimPosition()函数就可以获取当前光标的位置。在前面一节中我们也用过tech的一个函数获取了光标的位置,在这里那个函数是不能使用的,原因我之前也解释过了。得到光标位置后,我们需要查找光标处的实体了,在游戏中就同一位置来说可能有多个实体,比如你站在一个object(物品)前面,那这个位置的实体就有你和这个object,当然也可能有其他实体。Starbound的实体有很多类型,比如player、object、npc、monster这些你都能看到的,也有看不到的比较tech、quest等等。针对这些能看到的实体有个函数可能查找他们,定位到world文档。
    world文档中的函数应该是starbound中最常用的了,虽然函数比较多但是基本都很有用,所以多翻翻world文档吧。使用world.entityQuery这个函数我们可以查找对应位置的实体列表(同一位置可能有多个实体)。这个函数的第一个参数表示要查的位置,第二个参数则表示要查的范围。比如world.entityQuery({1,1}, 3)就表示查询以位置为1,1为原先半径为3圆内的实体。如果是world.entityQuery({1,1}, {2,2})就表示查一个矩形内的实体,你可以自己试试效果。

    现在可以在lua文件里写点内容了。如下,
    熟悉一个函数的作用,最有效的方法就是打印函数 sb.logInfo。 这个函数在starbound lua学习中有至关重要的地位,请务必重视。sb.printJson是按照json的格式进行打印方便查看。这里我们要在按下左键的时候去触发这个功能,所以加了if语句。其他地方应该都已经解释到了,我们来看看world.entityQuery到底返回了什么。    我在飞船上用这个物品在箱子上左键一下,可以看到log中打印了这样一个东西。
    可以看出返回的确实是一个列表,不过这个列表现在只有一个元素。其实 “2” 其实表示的是这个箱子在世界中的编号。在一个世界里,所有实体都有一个编号或者说是ID,这个ID在同一世界具有唯一性不可重复,用于标志这个实体。

    那么这个实体ID怎么用呢?我们继续往下翻这个world文档。
    
在稍微往下一点的位置,我们就能看到大量使用实体ID的函数,比如world.entityPosition可以获取一个实体的位置。再往下一点我们找到可以获取这个物品的名字的函数。
    world.entityName返回的就是一个实体的名字,当然对于object这种实体他的名字就是他的ID。接下来改动代码如下。
    因为entitys是个列表,你可以用for循环进行遍历,这里我只取拿到的第一个ID,然后打印他的名字和位置。定位到log我们发现我们思路是正确的,名字和位置到打印出来了。(注:这里当时写错了 if entitys~=nil 应该改为if #entitys ~= 0 下一节有改正)
    至此我们离目标已经跨出两步了,目前只是在log中打印,还差在屏幕上显示这个信息,这个会放到下一节来说。这一节有点长了。
制作一个探测器(下)
    这一节我们完成这个探测器,在屏幕上显示信息比较简单了。我们可以产生一个粒子来说显示文字,就粒子而言他需要一个projectile载体。这些是json方面的内容了,你可能之前也写过了。所以重点是怎么产生一个projectile,其实很简单,一个函数搞定。翻看world文档。
    这里有个world.spawnProjectile函数就是用于产生projectile的,参数1是proj的id,参数2表示产生的位置,参数3表示产生源id,参数4表示方向,参数5表示是否跟踪某个实体,参数6表示自定义配置信息。正常制作mod我们需要专门写一个projectile文件用于来产生文字粒子。但是这里单独再写一个projectile类型其实很浪费了,可以使用自定义配置参数来改变一个projectile的属性达到我们的目的。代码如下:
    方框中是这次我们新加的代码,首先是将我们需要显示的文字信息拼接起来定义到text变量中,然后定义projectile的自定义配置信息到param中,你一定会觉得这好像一个东西,其实这就是lua层面的json写法,这样写的配置就和以前我们用指令去改一个物品属性一样。在lua文件中我们不能直接写json,我们需要使用table(表)来表示json。这样就是一个自定义的projectile了,然后用world.spawnProjectile产生我们自定义的tankmuzzleflash。

    OK,这次我们做的小工具就算完结了。不过对于你来说只写这么些是不够的,因为篇幅原因我说的东西都是都是压缩过的了,很多东西我都没有说。比如这个工具做出后你会发现握住了没法动,怎么让他根据手腕动呢?还有之前我们说的一个查询实体的函数world.entityQuery他的第三参数是可选参数,当时我没填,其实那个参数也很重要,你需要自己去探索他的作用。总之自己多多探索吧。

    注:这次做的小工具涉及内容比较多了,有json内容也有lua的一些基础,如果你觉得有很多地方完全不理解,那是因为你的基础不够。此次的mod也传到创意工坊了。链接
探测器本地化
    这次的内容是教大家如何本地化我们之前做的探测器,所谓本地化就是让我们的Mod变为纯客户端mod。记得之前给大家说过,activeItem实体是一个客户端实体,所以我们完全可以把我们做的探测器变为一个客户端mod,从而不依赖、不影响服务器。

    当然如果你对此不感兴趣完全可以跳过这一节,这不会影响你学习后面的章节。我们本地化mod的目的是为了更好的联机,如activeItem类mod,进行本地化后,你的朋友不用安装你的mod也能正常一起玩。

    首先对于activeItem实体来说,他的运行脚本也就是Lua文件是完全属于玩家自身的,其他玩家就算没有这个lua文件也不会产生冲突。我们唯一需要做一些修改的是这个物品本身的一些属性。你不能用一个新的activeItem类型,这会产生不兼容问题。我们得找一个原版的activeItem修改他的属性把他改成我们的探测器。我们选择原版水壶。
Y}Y.png]接下来就是json的内容了,我们只需要把水壶的脚本改成我们探测器脚本就行了。先用指令试试 /spawnitem wateringcan 1 '{"scripts": ["/items/active/superinspection.lua"]}' 注意这里的脚本路径,需要写成绝对路径,因为我们探测器的脚本并不在水壶这个文件的目录下。可以发现是有效的。
    这样我们的探测器就本地化了,至于其他属性比如描述、库存图片之类的你可以试试自己修改修改,这里不再说了,这次本地化的探测器也上次到创意工坊了,你可以下载研究一下。链接
7 Comments
绵绵 17 Oct, 2024 @ 8:52am 
大佬太厉害了,信仰玩家啊:steamthumbsup:
DEAD PUNK 5 Jan, 2022 @ 1:28am 
+100 social credit !:steamthumbsup:
Lane 1 Jan, 2022 @ 3:35pm 
DIH!DIH!
Pesarow 11 Jun, 2021 @ 9:00pm 
Nuclear Patriot 3 Jan, 2021 @ 2:40am 
那个,有没有怎么做衣服mod的教程
无花碎月 8 Sep, 2020 @ 10:34am 
好活多整点
周树入 12 May, 2020 @ 6:17am 
大佬nb!(欢喜