meta data for this page
  •  

📚 差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
scripting [2023/03/17 11:27] bibiboxsscripting [2025/07/25 18:03] (当前版本) bibiboxs
行 2: 行 2:
 # 脚本开发及API # 脚本开发及API
  
-在《沙盘引擎》开发流程中,脚本(JavaScript)是赋予世界声明最关键的部分。 +文档篇幅较长且相当重要,开发之前务必预览**至少一次**!
-只有通过编写脚的方式才可以实现开发者所想象的绝大多数事情。+
  
-《沙盘引擎》框架将脚本开发分成了两个部分——==**“客户端”和“世界端(服务端)”**==。 
-> **本文档篇幅较长且比较重要,建议开发者务必至少预览一次。** 
  
-## 目录索引 
-<dokuwiki> 
-{{indexmenu>:scripting|js}} 
-</dokuwiki> 
  
-## 前置概念+## 索引
  
-在准备开始进行脚本开发及设计时,一定要**做好客户端和世界端的功能分配**,要进行合理设计功能API交互解耦,避免出现**过度相互依赖、粘合**的情况。+<script type="text/x-dokuwiki"> 
 +{{indexmenu>:scripting|js}} 
 +</script>
  
-常规情况下,建议开发者**优先考虑世界端API来实现主要逻辑**,可能需要帧同步或者确实需要客户端执行的内容,才放到客户端脚本内进行编写或接收交互。 
  
-> **正确举例:**制作一个载具的进度条(或仪表盘),首先在客户端创建一个进度条GUI,然后创建一个更新函数,在客户端Update Event进行调用,直接从客户端获取Vehicle.Speed进行进度条赋值,这样每帧更新且没有卡顿。 
  
-> **错误举例:**同上制作一个GUI进度条,然后在服务端判断玩家上车后,帧循环给玩家发送**自定义客户端数据(ServerToClientData)**,参数附带Character.Vehicle.Speed,客户端收到后把参数赋值给进度条Progress。这样虽然能实现相同的数据效果,但是进度条会因网络等原因不流畅(虽然可以通过Lerp方式处理,但这个例子说明有些功能并不适合服务端脚本开发,甚至应该避免这种快速每帧、定时循环发送客户端数据的行为)。+## 前置概念
  
-### 脚本开发逻辑指引+沙盘引擎 脚本采用**世界脚本&客户端脚本”二合一**的开发方式。
  
-沙盘引擎脚本功能设计偏向于==【世界主逻辑】==+引擎每次**加载世界场景**时,将加载一个整体**脚本组**(`World.js + Client.js`),前者负责实现世界主要逻辑(服务端),后者负责实现客户端逻辑。
  
-也就是说**能在World端实现的功能,不要在其他脚本执行**,Client端API比较有限(更多是客户端地内容,而不是客户端世界内容)**主要世界玩法功能API应该在World端得到体现**+> 模组加载完成后引擎会默认加载`Main`脚本组内本,即加载`Mod/Script/Main`目录下`Client.js`和`World.js`脚本
  
-> **例如:**开发者希望实现“当角色手中的物体变成了【马桶】,那么将在此玩家的视角里弹出一个GUI窗口(上面可能写着一些文本或按钮)” 
-> 
-> 在沙盘引擎的脚本开发设计下,**正确做法应该是:应该首先在World端找到`当角色手中的武器被改变`的事件,然后检查角色是否为玩家、角色手中物品是否为【马桶】,然后发送一条自定义网络消息(或使用客户端反射API)告知客户端打开指定的GUI界面。(关闭GUI逻辑相同,只需判定手中物品不再是马桶)** 
-> 
-> **反例补充:**同样,你不应该在Client脚本中(也可能根本没有类似的事件)实现这一检查逻辑。 
-> 
-> 如此设计是为了考虑开发后期对于逻辑的理解性,相对避免出现Server&Client双核大脑开发逻辑冲突耦合等问题。 
  
  
 +### 脚本分配指引
  
-## 脚本文件+在设计脚本时,应该做好**世界脚本及客户端脚本**的功能责任分配,避免出现**过度相互依赖**的情况,有助于后续的版本开发和日常维护。
  
-沙盘引擎的**所有模组脚本**`模组目录/Script`目录+常规情况下,应优先考虑**世界脚本API**来实现主要逻辑,需要**客户端本地**相关的内容,才**客户端脚本**进行设计和实现
  
-其中入口脚本主要分为两个组成,分别是**Client.js**与**World.js**两脚本+**正确举例:**制作一载具的进度条(或仪表盘),首先在客户端创建一个进度条GUI,然后创建一个更新函数,在客户端每帧进行更新,直接从客户端获取`Vehicle.Speed`进行进度条赋值,这样每帧更新且没有卡顿
  
-| 脚本名称  | 说明                                                         | +**错误举例:**同上,制作个GUI进度条,在服务端判断玩家上车后,在帧循环事件中给玩家发送**自定义客户端数据ServerToClientData**,参数附带`Character.Vehicle.Speed`,客户端收到后把参数赋值给进度条Progress。这样虽然能实现**数据效果**,但进度条会因**网络延迟**等导致**显示不流畅**虽然可以通过**插值方式**处理,但有些功能并不适合服务端开发通常应该避免这种**快速每帧定时循环**发送客户数据的情况,并且造成了不必要的**网络通讯**开销浪费。
-| --------- | ------------------------------------------------------------ | +
-| Client.js | 主动入口脚本<br />负责切与本地客户端相关的内容如GUI、相机视角、本地事件、本地世界事件等) +
-| World.js  | 主动入口脚本<br />负责一切与主世界内容(服务端权威如世界时间天气、其他服务及玩法类逻辑) |+
  
-**需要特别注意,因为客户端和世界端(服务端)逻辑同步有很多不同的地方,所以两种脚本的Event、Function并非是通用的。** **(两个脚本分别是不同的工作空间。)** 
  
-> 具体Client、World脚本开发API,请参考**脚本开发**文档下的子分类,有些API可能很相似,除非完全一样,否则是不能通用的。 
-> 
-> 除此之外,也有一些**原生通用Native**的代码API,这种通常是可以在客户端和主世界通用的代码。 
  
 +### 功能开发的常规步骤
  
 +1. 需求分析:确定想要实现的功能,对其进行简单的描述
  
-### 加载子脚本+   例如:想要设计和实现一个赛车的玩法,需要清楚规则是什么,路线是什么,哪些车参赛,人数怎么安排,比赛怎么进行等
  
-实际开发过程中,特别是针对游戏定复杂的游戏显然只有两个脚本文件是不利于开发者便利的。+2. 计:根据需求分析开发层面进行抽象、分类和归纳,总结出需要存在哪些数据,功能,流程
  
-**沙盘引擎脚本**允许开发使API加载其他脚本文件(或叠加意思)这样开者就可以根据模组情况自行分配好多个脚本。+   例如:设计赛车的路线由一个个点连接组成,参赛者用列表统一管理,参赛车有什么属性、功能,人数限制功能描述什么时候在什么地方会生什么事(流程)等
  
-> 针对设定复杂游戏模组,分配好合理易懂的脚本分类可让开发更清晰,帮助开发者提高开发效率。+3. 实现:用具体代码和环境等来实现上的设计(也就是编写代码)
  
-加载并成功注册子脚本后,实际上相当于**保在同一个开发工作空间**并不代表真正意上“其他脚本”或“其他类”。+   例如:赛车的路线用Checkpoint(检查点)来实现参赛者用数组的方式以及自定的各种函数来实现具体的功能等等
  
-```javascript +4调试:开发的最后一步,及时发现异常,确保项目能正常运行
-//伪脚本示例 +
-LoadScript("newscript.js"); +
-LoadScript("Func/Functions.js");+
  
-//当开发者需要使用时直接执行函数、常量即,并不需要以XXX.Function()等方式使用。 +   例如:从使用者角度,对各个功能进行实际的上手测试(黑盒测试);从开发者角度对代码能存的隐患边界条件进行实际上手测试(白盒测试
-//任何其他脚本(除非只有ClientWorld入口脚本是两个不同空间建立的变量、常量、函数,在子脚本中也正常使用,由此需要留意避免重名的情况。 +
-```+
  
-新建后的子脚本必须放置在Script目录内(也可以放置到新建子目录),放置后的脚本必须通过API进行**加载脚本**,否则没有作用(但也可以举一反三,通过此机制实现模块化开发)。 
  
  
 +## 脚本文件
  
-### 脚本加密+沙盘引擎的**所有模组脚本**均在模组目录下`Script/XXX`目录。
  
-在沙盘引擎模组开发,无论是客户端还是世界端脚本,都将在模组打包时被附带到模组目录中,因此**模组开发源代码、脚本是透明的**。+中**入口脚本**有两个,分别是`World.js`与`Client.js`
  
-由于**JavaScript脚本**的透,如果不希望别人查看自己的脚本,可以通过网络上混淆加密工具进行加密,但是**【务必确保你手里有源文的备份,否则可能是不可逆的】**,建议只在确认打包发布之前进行一次混淆,本地测试阶段并不需要混淆和加密。+脚本      | 说明                                                         | 
 +| --------- | ------------------------------------------------------------ | 
 +| World.js  | 主动入口脚本<br />负责一切与主世界相关的内容(服务端权威,如世界时间、天气、其他服务端及玩法类逻辑) | 
 +| Client.js | 主动入口脚本<br />负责一切与本地客户端相关内容(如GUI相机视角、本地事本地世界事件等) |
  
-==***注意:混淆工具并不是完全不可逆的,只能起到加密和阻止完全修改作用。***==+> 特别注意:两种脚本的作用空间**不是互通**的,只在各自代码空间生效 
 +
 +> 除此之外,也有一些**原生通用(Native)**的代码API,此分类下的代码可以在两种脚本空间通用,但数据通常不互通。
  
-> 网络联机情况下,会自动验证加入客户端的同步性,避免本地脚本被修改后的错误同步(Client.js如果与服务端下的Client.js不同步,将会被禁止连接)。 
  
-**出于脚本和资源的开放和部分透明性,《沙盘引擎》官方将尽可能保护原创模组的版权,对于受到盗版争议、未经允许或恶意破解修改已有模组的内容(非法修改版模组),引擎官方将可能在合理范围内对问题模组进行屏蔽阻断和下架处理。** 
  
 +### 加载子脚本
  
 +实际开发过程中,特别是针对**设定复杂**的游戏,显然只有两个**脚本文件**是不利于开发者**逻辑清晰**的。
  
-### 脚本加密延伸:模组定位+模组允许开发者**使用API加载其他脚本**,开发者可以根据情况**自行分配**多个**子脚本**,帮助开发者提高开发效率。
  
-开发者建立模组初期就应该对“自己模组”进行一个属性定位,究竟要为一个**衍生游戏**还是一个**开放性联机游戏**+> 成功加载**子脚本**后,实际相当于将代码**叠加一个作用空间**,并不代表真正意义分割为**“其他脚本”**。 
 +
 +> 当发者需要使用**子脚本**的方法时,直接正常执行即可,不需要增加文件名前缀等。
  
-例如《战地1942》与《战地2》,以及和《战地5》进行一个对比,就可以解释这件事情。+```javascript 
 +LoadScript("newscript.js"); 
 +LoadScript("Func/Functions.js"); 
 +```
  
-> 《战地1942&战地2》是默认游戏本体就支持玩家自定义开服局域网、互联网),这就相当于一个**“开放性联机游戏”**,在原有玩法的基础上,玩家仍然可以通过修改数据等方式实现**游戏的二次创作**。 +任何新建的**子脚本**必须放置`Script`目录内或子目录),放置后的**子脚本**必须通过API进行**加载脚本**,否则没有作用(抛砖引玉,通过此机制实现模块化开发
-+
-> 反之 +
-+
-> 《战地5》游戏本体没有任何“以直接开服并修改”的内容,也就是说玩家只能体验开发者允许玩家体验的内容,这就是传统意义上的**“衍生游戏”**,玩家自然也无法通过常规手段对游戏进行二次创作+
  
-关于此定位完全决定模组发布时是否要进行指定脚本的加密。 
  
-结论:如果制作**“衍生游戏”**,则建议发布时加密全部脚本;如果制作的是**“开放性联机游戏”**,则建议只加密`Client.js`等客户端脚本,`World.js`服务端脚本可留给玩家进行二次创作。 
  
-> 注意:通常来讲,`World.js`服务端脚本包含着游戏玩法的核心代码,如果公开可能会造成修改、魔改版本,甚至脚本改编成新模组的情况出现,开发者在发布时应考虑周全,或者应该在模组根目录**写入许可说明**(https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository#disclaimer)。+### 脚本加密
  
 +在 沙盘引擎 模组开发中,无论是**世界脚本**还是**客户端脚本**,都将被放置在模组目录中,因此模组的**脚本源代码是透明的**。
  
 +由于`JavaScript`脚本的透明性,如果不希望别人查看自己的脚本,可以通过网络上的**混淆、加密工具**进行加密,但是==**务必确保源文件的备份,否则可能是不可逆的==**,建议只在**确认打包**之前进行一次混淆,本地测试阶段并不需要混淆和加密。
  
-### 服务端子脚本(概念阶段+注意:由于 沙盘引擎 的联机自动同步机制,即使在开发过程中,也可能有玩家会连接到当前服务器,并且会自动下载**最新的模组全部文件**(包括脚本),因此如果不希望其他人进入,可以提前设置服务器密码或相关权限。
  
-注意此功能仍处于概念阶段,仅供引擎开发计划参考和逻辑扩展,**不具有实现功能**。+JavaScript在线加密工具https://obfuscator.io/ 
 +
 +> 混淆工具并**不是完全不可逆**的,只能起到加密和阻止完全修改的作用***
  
-通过上文的解释,我们可以知道衍生游戏主要依靠Client.js和World.js脚本组成最终游戏。 
  
-但是在某些情况下,可能衍生游戏的开发组仍然希望“衍生游戏用户”可以自定义服务器功能脚本,但又不希望用户直接修改Client.js或World.js脚本(或以上两个脚本发布时已经加密混淆),这就有了**服务端子脚本**的概念,相当于在原有World.js的基础上,增加了一个与其相同作用的`WorldServer.js`。 
  
-因为模组开发者已经将Client\World脚加密,所以模组用户自然无法直接修改这些内容,但是可以通过子脚本实现和World.js几乎相同的功能(对于部分功能,可能会有削减),也应该支持World.js内的一些自定义公开API。+### 体思维扩展
  
 +[note2]
  
 +此部分可能包含过时内容,暂时仅供参考。
  
-### 开发思维扩展+[/note]
  
 通过了解,沙盘引擎两个主要入口脚本`Client.js`和`World.js`分别有各自的激活场景和作用。 通过了解,沙盘引擎两个主要入口脚本`Client.js`和`World.js`分别有各自的激活场景和作用。
行 133: 行 118:
 这其实也可以扩展出一些其他思维,比如你有版权或用户隐私方面的顾虑或其他打算,你完全可以进行`C\S`客户端和服务端的分别开发和打包。 这其实也可以扩展出一些其他思维,比如你有版权或用户隐私方面的顾虑或其他打算,你完全可以进行`C\S`客户端和服务端的分别开发和打包。
  
-也就是说,客户端如果没有“建立主机服务器”的需求,完全可以将World.js留空或删除,实现**网游化**的开发。+也就是说,客户端如果没有**“建立主机服务器”**的需求,完全可以将`World.js`留空或删除,实现**网游化**的开发。
  
-如可能开发者不想让任何人都**架设服务器**,那么就只需将World.js连同工程单独拷贝出来一份,很简单的形成了单独的“服务端”。+可能开发者不想让任何人都**架设服务器**,那么就只需将`World.js`连同工程单独拷贝出来一份,很简单的形成了单独的**“服务端”**
  
-*因为届时开发者发布给玩家的**模组本体不包含World.js**(服务端脚本),所以玩家无法建服**(客户端也不应该有开启服务器的UI接口)**,只能进行连接,而连接是不需要World.js的。*+因为开发者发布给玩家的**模组本体**可以不包含**世界脚本**(`World.js`),所以玩家**无法建务器**(客户端也不应该有开启服务器的UI接口),因此只能进行加入游戏,而**加入游戏**可以不需要**世界脚本**的。
  
-> 此开发思路只是针对**某些特殊玩法或网游服务器需求情况**,如果是常规**好友派对类型游戏**情况下,还是建议将建立主机的能力附带给玩家。+> 此开发思路只是针对某些**特殊玩法****网游服务器**需求情况,如果是常规**好友派对游戏**情况下,还是建议将**建立主机**的能力附带给玩家。
 > >
-> 简单举例:《我的世界》和《战地5》的区别;一个是任何人可以直接本地开启服务端游玩,另一个是只有官方拥有服务器,或者提供租赁服务。 
  
  
行 147: 行 131:
 ### 开放性利弊分析 ### 开放性利弊分析
  
-由于Javascript本身语言的透明性,无法进行二进制等真正意义的编译打包,尽管上面介绍了混淆加密工具的方式,但仍然会有小概率可能会被修改。+由于`JavaScript`本身语言的透明性,无法进行**二进制**等真正意义的**编译打包,**尽管上面介绍了**混淆加密工具**的方式,但仍然会有小概率可能会被修改。
  
-无论如何,进行混淆和加密有必要的,否则可能会出现一些预期之外的情况。+无论如何,最终版本进行**混淆和加密**或许是有必要的,否则可能会出现一些预期之外的情况。
  
-如没有进行任何混淆和加密,可能脚本会被其他人参考(这是的),但也可能会被他人进行违背作者初衷的魔改。+**例:**没有进行任何混淆和加密,可能脚本会被其他人参考(在某些情况下,这是有助于学习的),但也可能会被他人进行**违背作者初衷**的魔改。
 > >
-> 举例:开发者A制作了解谜小游戏,开发者B直接修改其未加密的代码,添加、开放了许多达到作弊效果的功能和指令,破坏了模组作者设计的初衷。+**举例:**开发者制作了解谜小游戏,玩家直接修改其**未加密**的代码,添加了许多达到**作弊效果**的功能和指令,破坏了模组作者设计的初衷。
  
-考虑到类似这样的情况,尽管官方会按照**初版发布时间**和**大众意愿评定**来进行区分和保护正版模组,但如果开发者抛开混淆和加密仍然有困扰,可以考虑[开发思维扩展](#开发思维扩展)的,综合判定是否要进行“独立服务端”的开发方式(因为世界服务端基本控制着玩法的核心,客户端只有Client.js是只有基础的功能的)。 +考虑到类似这样的情况,尽管官方会按照**初版发布时间**和**大众意愿评定**来进行区分和保护正版模组,但如果开发者抛开混淆和加密仍然有困扰,可以考虑本文其他的扩展方,综合判定是否要进行**“独立服务端”**的开发方式等。
- +
- +
- +
-### 联机脚本同步匹配 +
- +
-由于脚本在Native的支持下会有很多“危险”的指令(如引擎内文件操作、数据库操作),这有可能会产生部分恶意脚本影响正常游戏。 +
- +
-无论是作为客户端玩家还是服务端“开服者”,都不建议使用**完全未知且不可信任**的第三方脚本,除非你知道自己在做什么。 +
- +
-> 当玩家作为“服务端主机“开启联机服务器时(单机游戏、本地游戏除外),服务器会在每次玩家加入时**验证玩家`Client.js`的脚本是否与服务端同步**,如果出现文件MD5等数值不匹配,则被认定为文件不同(哪怕只有微不足道的改动),==服务端将会以`Client.World.ConnectResult == 5`(版本、协议不匹配)拒绝玩家连接到服务器==。 +
-+
-> 换句话说,无论是玩家或者服务端主机还是出于版权保护,都不应该修改`Client.js`文件(通常也是被模组开发者加密混淆)。 +
- +
- +
- +
-## 客户端(Client) +
- +
-> 客户端的操作范围:一切适合在客户端做的事情(如本地数据及复杂运算),以及只为玩家“本地”出现的内容。 +
-+
-> 客户端的内容是默认保存在本地的,且运算代码也将在本地进行,所以不用担心网络延迟等问题(但要适当考虑漏洞,避免利用本地客户端作弊)。 +
- +
-客户端代码除了允许的世界Event(例如onTimeChange、onCharacterAction)外,还包括一些世界端没有的独有逻辑(例如**打开客户端的某个内置界面**,当然也可以通过**自定义网络数据**以实现服务端命令的类似效果)。 +
- +
- +
- +
-## 世界端(World / Server) +
- +
-> 服务端的操作范围:一切需要安全保存的事情(如玩家血量、经验值等),以及需要网络同步的内容(例如一个操作需要让全部玩家都同步看到的)。 +
-+
-> 服务端的内容是仅保存在“主机玩家”设备中,运算代码都将在“主机玩家”设备进行,并且通常会在运算处理结束后同步给其他玩家,这就造成了无论是多小的运算,都会有网络延迟和影响,不建议频繁运算的功能使用服务端来进行(例如:线性改变玩家视角,虽然服务端API可以做到,但是应该用客户端API来做,因为每次服务端发送到玩家“改变视角”数据是至少需要时间的,看起来会不平滑)。 +
- +
-世界端严格意义上来讲**可以被称为“服务端”**,但是由于它只在World场景加载后执行,所以也被习惯性称之为世界端。 +
- +
-世界端代码包含大量与世界可见内容息息相关的部分,通常想调整世界内玩法相关的内容,均需要在世界端进行处理。 +
- +
-==世界端脚本只在主机玩家(或单人游戏本地玩家)执行,所以是服务器权威(网络同步概念)==+
  
  
行 197: 行 145:
 ## 脚本框架及来源 ## 脚本框架及来源
  
-沙盘引擎游戏本体是基于[Unity](https://unity.com/)进行开发的,引擎中动态编译解析`JavaScript`脚本的功能来源于[PuerTS](https://github.com/Tencent/puerts)框架(致敬感谢)。+沙盘引擎 游戏本体是基于[Unity](https://unity.com/)进行开发的,引擎中动态编译解析`JavaScript`脚本的功能来源于[PuerTS](https://github.com/Tencent/puerts)框架(致敬)。
  
 尽管引擎脚本是以`JavaScript`作为编译基础的,但引擎代码的设计思维来自于[松鼠语言脚本](http://squirrel-lang.org/)(另外一个脚本语言)。 尽管引擎脚本是以`JavaScript`作为编译基础的,但引擎代码的设计思维来自于[松鼠语言脚本](http://squirrel-lang.org/)(另外一个脚本语言)。
  
-> 如果开发者曾了解过[SA-MP](https://www.sa-mp.com/)或[VC-MP 0.4](http://vc-mp.org/),那么在沙盘引擎内你可能会有一些“亲切感”,并且会对沙盘引擎的API代码设计更快速的理解入门。 
-> 
-> *《沙盘引擎》部分代码开发者曾在VC-MP环境开发过许多内容,所以吸取借鉴了部分代码开发思路(如Event和Function)。* 
  
  
 +## 脚本API文档指南
  
-## 脚本API文档指南*+有关脚本API文档将被分成几个**主要部分**,分别是**原生及通用代码**、**客户端代码(Client)**、**世界端代码(World)**。
  
-沙盘引擎脚本API被分成3个部分,分别是`原生及通用代码`、`客户端代码Client`、`世界端代码World`+公开范围内所有的`Event`(事件)、`Property`(属性)、`Function`(方法)等代码都将在**以上三个文档内**记录呈现
  
-引擎公开范围内所有的Event、Function都在**以上三个文档**记录呈现有些可能没有被详细介绍的API可以由开发者自行探索,技多不压身。 +文档中部分代码(类)实际上可能是**静态类名**,这里需要根据API文档内**首字母大写**进行区分例如:`Timer`是一静态类,而`timer`则是一个生成实例)。
- +
-> 为了更好的理解API文档容和信息,下方介绍API文档内的一些文档规范和名词解释。 +
- +
-| 数据类型        | 说明                                                         | +
-| --------------- | ------------------------------------------------------------ | +
-| int             | 整数(50)                                                   | +
-| float           | 浮点1.0f或1.1234)                                     | +
-| double          | 双精度浮点小数(1.00000001)                                 | +
-| string          | 字符串(文本)                                               | +
-| char            | 单字符                                                     | +
-| bool(boolean) | 布尔逻辑型(真、假)                                         | +
-| any             | 非硬性检查的会根据具体逻辑进行装箱和拆箱<br />虽然没有固定限制,但是开发者有必要确保传递数据类型合法 |+
  
 ```javascript ```javascript
-//在脚本API文档中,可能会出现如下的案例+Timer.Create() [√] 
 +timer.Create() [×] //no instance function
  
-//如果是此类型情况,可视性比较高,所以可能不会标注player具体是什么类型 +Timer.Repeat == 0 [×] //No static property 
-//因为大概率可以直接看出它是一个player类型 +timer.Repeat == 0 [√] 
-function OnPlayerJoin( player )+```
  
-//如果是此类型情况,对参数名定义相对模糊可能会标注告知开发者具体类型(TypeScript类型方式) +> 为了更好的理解**API文档**内容规范,以下是文档的部分**类型名词**解释。
-function OnPlayerChat( player, text: string ) //参数后面": "跟随的就是此参数类型,表示text是一个字符串类型+
  
-//函API介绍同理 +据类型         | 说明                                                         | 
-World.SetPassword( pass: string ) //表示参pass需要是一个字符串类型+| ---------------- | ------------------------------------------------------------ | 
 +| `int`            | 整数(`50`)                                                 | 
 +| `float`          | 浮点小数(`1.0`或`1.1234`)                                  | 
 +| `double`         | 双精度浮点小(`1.00000001`)                               | 
 +| `string`         字符串(`"Hello"`)                                          | 
 +| `char`           | 单个字符                                                     | 
 +| `bool | boolean` | 布尔逻辑型                                                   | 
 +| `any`            | 通用类型,自动进行装箱\拆箱<br />虽然没有固定的限制,但是开发者应该确保数据类型正确 |
  
-//如果是此型情况表示此Event、Function拥有返回值(或要求返回值) +> 在本分下的API文档中您可能会看到类似`TypeScript`格式的**代码样式**,但通常并不能直接开箱即用,请使用最终`JavaScript`支持的标准格式。 
-function OnPlayerEnterVehicle( player, vehicle, seat )int //表示需要提供一个int类型的返回值这可能会影响此Event功能拦截或忽略) +
-+> 例如:`function OnPlayerJoin( player: Player )`,此代码不是标准格式,仅用于表示**类型、参数**等信息。 
-     +
-}+> 实际应用时应该是类似以下代码标准格式 
 +
 +> ```javascript 
 +> function OnPlayerJoin( player ) 
 +
 +>      
 +} 
 +> ```
  
-World.GetPassword(): string //表示返回一个字符串类型的结果 
  
-//如果是此类情况,表示某些参数是可空或默认参考 
-World.CreateMapMarker(icon: int, pos: Vector, target: SE.Player = null); //target参数表示可以为空,默认是null 
-``` 
  
 ### 脚本API文档标题 ### 脚本API文档标题
  
-由于引擎主要功能是由World、Client两个主类下的众多子类实现控制的,所以脚本文档采用了XXX 类参考的命名方式。+由于引擎主要功能是由`World``Client`两个主类下的众多子类实现控制的,所以**脚本文档**采用了**XXX 类参考**的命名方式。
  
-也就是说,例如你想查找一个关于世界下的玩家方法,那么应该前往[世界脚本——Player 类参考](scripting/world/player "世界脚本——Player 类参考")进行查找。+也就是说,例如查找关于**世界脚本**下的玩家方法,那么应该前往[世界脚本——Player 类参考](scripting/world/player "世界脚本——Player 类参考")进行查找。
  
 **除此之外,每个类型参考文档内拥有多个H1、H2标题进行分类,通常会按照以下命名进行区分。** **除此之外,每个类型参考文档内拥有多个H1、H2标题进行分类,通常会按照以下命名进行区分。**
行 271: 行 215:
 沙盘引擎使用`JavaSciprt`作为开发脚本语言,所以开发者**可通过任何文本编辑器进行脚本编写**。 沙盘引擎使用`JavaSciprt`作为开发脚本语言,所以开发者**可通过任何文本编辑器进行脚本编写**。
  
-脚本开发时并**不需要专门的IDE以及编译器**,只需要进行**增删查改**及修改后的**保存“文本”**即可,待下次引擎入脚本时将会**自动编译**。+脚本开发时并**不需要专门的IDE以及编译器**,只需要进行**增删查改**及修改后的**保存“文本”**即可,重新建立世界(重连、重时将会**自动动态编译**。
  
  
行 277: 行 221:
 ## 脚本开发基础入门 ## 脚本开发基础入门
  
-> 注意:此处指的**基础入门**并==不是==`JavaScript`语言的基础教程,而是指引已有编程逻辑基础的开发者,快速进行了解脚本代码的一些差异和规范。+> 注意:此处指的**基础入门**并==不是==`JavaScript`语言的基础教程,而是指引已有**编程基础**的开发者,快速进行了解脚本代码的一些**差异和规范**
 > >
-> 如果你是一位编程初学者或`JavaScript`初学者,可以考虑参考以下视频教程。+> 如果你是一位编程初学者或`JavaScript`初学者,可以考虑参考以下**视频教程**
 > >
 > 四十分钟JavaScript快速入门:https://www.bilibili.com/video/BV15L4y1a7or > 四十分钟JavaScript快速入门:https://www.bilibili.com/video/BV15L4y1a7or
 > >
-> 哔哩哔哩JavaScript入门教程搜索:[https://search.bilibili.com/all?keyword=javascript 入门](https://search.bilibili.com/all?keyword=javascript 入门)+> 哔哩哔哩JavaScript入门教程搜索:https://search.bilibili.com/all?keyword=javascript
 > >
  
-《沙盘引擎》的开发初衷就是为了**精简开发流程以及代码的复杂程度**,所以实际上对引擎模组脚本的开发并**不需要非常过硬的编程技术**。+《沙盘引擎》的开发初衷就是为了**精简开发流程以及代码的复杂程度**,所以实际上对**模组脚本**基础开发并不需要非常过硬的编程技术。 
 + 
 +如果开发者曾对`Pawn`、`Squirrel`或**其他编程语言**有相关经验,甚至不需要完全学习`JavaScript`,只需掌握**基础的语法**使用即可开始体验 
  
-如果开发者曾对`Pawn`、`Squirrel`或`其他高级编程语言`有相关经验,甚至并不需要完全学习`JavaScript`,只需掌握基础的语法使用即可尝试使用。 
  
 ### 事件和函数 ### 事件和函数
行 308: 行 254:
  
  
-### 类名区分及使用+### 默认空间
  
-在沙盘引擎的函数方法设计中,都是以Class类的方式的,也就是无论任何功能调用,均是从某个`Class类`来激活使用的+为了脚本开发思路更加清晰,所以引擎采用了**必要名前缀**调用方式际上类似于**命名空间**概念,。
  
-**由此得知,绝大多数情况下名的前缀是必须的。(除World.js下的SE.World分类等例子,因为本来就是在类下**+| 脚本环境         | 默认类   | 示例                                                     | 
 +| ---------------- | -------- | -------------------------------------------------------- | 
 +| `Client.js`      | `Client` | `Client.Core.XXXXX()`                                    | 
 +| `World.js`       | `World`  | `World.Core.XXXXX()`                                     | 
 +| 通用(`Native`) | `-`      | `CreateHost()` <br />`Random.Range()`<br />`Debug.Log()` |
  
-```javascript +> 注意:`Client.js``World.js`是**完全不同**的两个**程序空间**,即使有些内容(如`Player`类)命名相同,但不代表在底层是基于同一个类。
-//World.js+
  
-//设置世界天气 
-World.SetWeather(0); //标准形式,但是会多打几个字 
-SetWeather(0); //可以省略World,因为本身就在SE.World类下 
  
-//新建一个载具 
-Vehicle.Create(0, Vector(0, 0, 0)); //标准形式,注意(开头大写)Vehicle是类名,而不是vehicle(引用变量等) 
-vehicle.Create(0, Vector(0, 0, 0)); //错误,因为vehicle不是一个合法的名字,它可能是一个变量或其他任何内容 
-Create(0, Vector(0, 0, 0)); //错误,很明显这从可读性来讲都不通,因为没有指定“谁”来进行“Create” 
  
-//修改载具的生命值 
-let vehicle = Vehicle.Find(100); 
-if(vehicle) vehicle.Health = 500; //正确,由Vehicle类进行搜索某个索引的载具,并赋值给vehicle变量,然后进行修改 
  
-Vehicle.Heatlh = 500; //错误,因为Vehicle是一个引擎类(SE.Vehicle),它本身没有任何关于Health的静态方法\属性(只有实例化后才有) 
  
-//输出一段内容 
-World.DLog("Hello World!"); //错误,因为DLog并不在World类下 
-SEngine.DLog("Hello World!"); //标准形式,正确 
-DLog("Hello World!"); //正确,因为DLog是一个“原生通用代码”(详情参考文档:脚本开发——原生通用代码),此类代码可不写“类前缀” 
-``` 
  
-如果你能**比较清晰**的理解以上的代码示例,那么你在开发引擎脚本方面就没有太大问题啦! 
  
  
  
-### 默认类空间 
- 
-关于脚本编写时**“类名的使用”**在上方已经介绍过,实际上并不需要将此问题考虑过于复杂。 
- 
-为了脚本开发时思路和可视性更清晰,所以引擎内采用了**优先必要时类名前缀**的调用方式,但实际上某些类是存在**“默认工作空间”**的,这种情况下是不需要进行前缀调用的(虽然也可以写,但是可以忽略更精简一些)。 
- 
-| 脚本环境  | 默认类            | 说明                                                         | 
-| --------- | ----------------- | ------------------------------------------------------------ | 
-| Client.js | SE.Client         | 因为Client类本身就基于SE.Client下<br />所以在**Client.js**内可以省略`Client.XXXX`,而直接使用`XXXX()` | 
-| World.js  | SE.World          | 因为World类本身就基于SE.World下<br />所以在**World.js**内可以省略`World.XXXX`,而直接使用`XXXX()` | 
-| 通用原生  |  SE.Native | 在SE.Native下的类成员可直接使用<br />例如:`DLog()`或`Vector(0, 0, 0)`等均属于原生通用内容,不需要任何前缀<br />详情参考可见==脚本开发——原生通用代码== | 
- 
-**注意:`Client.js`和`World.js`是完全不同的两个类,即使有些内容(如Sound类)命名相同,但不代表在底层是基于同一个类。** 
- 
-> 例如:Sound类看起来名称一样,实际上在底层的表现可能为`SE.Client.Sound`与`SE.World.Sound`,而不是真正意义上的`SE.Sound`(这只是一种说明方式)。 
-> 
-> ==**这也就导致尽管类名相同,但在Client和World环境下具体用法可能有差异(因为根本严格意义上不是一个类),有些功能是客户端专用,有些是世界端专用,也有些是通用的功能。**== 
  
  
  
 </markdown> </markdown>