前言:本文并不会教你基础的编程知识,阅读过程中遇到无法理解的名词或段落请善用百度功能.


AngelScripts是一个基于cpp的脚本语言,因为某些的原因,SC Team选择了他作为SC的官方唯一指定插件语言,本文就介绍AngelScripts的基本知识和编写第一个插件展开。

1. 第一部分

配置你的AngelScripts环境

Sven Co-op在某次更新后,本身便集成了完整的AngelScripts环境,实际上,我们并不需要像编写一个普通的AngelScripts程序那样为你的VC++或VS安装复杂的AngelScripts环境,你需要准备的只有一个令你感觉舒服的文字编辑器即可

此处推荐的编辑器有

    - Notepad++

(官网连接)

    - Visual studio Code

(官网连接)

这两个编辑器带有强大的功能和丰富的社区,并且两者都支持AngelScripts的代码高亮(VSC需自行安装)
当然,你用记事本或者word或者wps编辑也不是不可以

好!我们现在有了工具,有了开发环境,既然这个语言与Java和Cpp很像,那么我们可以立即开始编写自己的第一个插件了!

namespace Programe
{
    void Main(string[] args)
    {
        printf("Hello World!");
    }
}

全文完

好吧并没有,继续往下看吧

2.第二部分

AngelScripts特点

现在你有了一个顺手的编辑器,在开始编写第一个插件之前需要了解一些关于Sc使用的AngelScripts的特点。
AngelScripts是一个基于cpp开发的脚本语言,所以其保持了C系语言的基本结构,是一种典型的结构化语言
总的来说,AngelScripts介于cpp和java(或者cs)之间,但他也有一些独有的地方

1.没有指针
    尽管他是基于cpp的脚本语言,但是AngelScripts没有指针,这里和Java类似
2.class外默认为静态类
    与Java和cs不同,AngelScripts可以在class外放置你的代码,这些代码会被默认为静态类编译
3.所有函数默认为public
    与Java和cs不同,AngelScripts所有函数默认为public,除非你手动加上protect或private
4.没有试错
    SC使用的AngelScripts实际上将所有的代码放置在一个try内运行,并把Expersssion由控制台输出,SC的AngelScripts是没有试错的
5.拥有三个程序入口点
    SC因为游戏运行的特点,AngelScripts编写的程序实际上拥有三个入口点,分别为PluginInit MapInit和MapActivate,这三个有什么区别后文再讲

由于AngelScripts默认公开的关系,请尽量将你的代码写在一个namespace或class里,否则你所取的变量名或函数名很可能与系统保留字冲突,引起一些奇怪的bug

变量名称等的区别下文再提

那么我们写的插件是不是应该变成

void PluginInit()
{
    printf("Hello World!");
}

安装运行

Angelscript: e:/steamlibrary/steamapps/common/sven co-op/svencoop_addon/scripts/plugins/Test.as (1, 1) : Compiling void PluginInit()
ERROR: Angelscript: e:/steamlibrary/steamapps/common/sven co-op/svencoop_addon/scripts/plugins/Test.as (3, 2) : No matching symbol 'printf'
ERROR: Angelscript: Plugin script compilation failed

咦?怎么又出错了呢,AngelScripts的官方手册就是写printf啊,怎么会找不到呢?
不要急,我慢慢告诉你
SC为了游戏的需要,对AngelScripts进行了一些修改,比如移除了printf等,类似于cs的Console.WriteLine,SC也为print信息添加了一些接口

接口名称 用法
g_PlayerFuncs.SayText(CBasePlayer@ pTargetPlayer, const string& in szText) 在目标玩家左下角聊天栏输出信息
g_PlayerFuncs.SayTextAll(CBasePlayer@ pOriginatingPlayer, const string& in szText) 以一名玩家为源,向所有玩家聊天栏发送信息,实际上游戏内按Y聊天就是调用此
g_PlayerFuncs.ClientPrint(CBasePlayer@ pTargetPlayer, HUD iMsgDest, const string& in szMessage, const string& in szLine2 = "", const string& in szLine3 = "", const string& in szLine4 = "", const string& in szLine5 = "") 在指定玩家屏幕指定位置输出信息
g_PlayerFuncs.ClientPrintAll(HUD iMsgDest, const string& in szMessage, const string& in szLine2 = "", const string& in szLine3 = "", const string& in szLine4 = "", const string& in szLine5 = "") 在所有玩家屏幕指定位置输出信息
g_PlayerFuncs.CenterPrintAll(const string& in szMessage, const string& in szLine2 = "", const string& in szLine3 = "", const string& in szLine4 = "", const string& in szLine5 = "") 在所有玩家屏幕中心位置输出信息
g_PlayerFuncs.ShowMessage(CBasePlayer@ pTargetPlayer, const string& in szString) 在指定玩家屏幕上内输出信息
g_PlayerFuncs.ShowMessageAll(const string& in szString) 在所有玩家屏幕上输出信息
g_Game.AlertMessage(ALERT_TYPE aType, const string& in szFormat, ?& in, ?& in, ?& in, ?& in, ?& in, ?& in, ?& in, ?& in) 在主机指定位置内输出信息

附:HUD和ALERT_TYPE的所有类型

HUD 解释 ALERT_TYPE 解释
HUD_PRINTNOTIFY 屏幕左上角 at_notice 在控制台和Log内输出NOTICE:开头的信息
HUD_PRINTCONSOLE 控制台内 at_console 在控制台内输出信息,只有developer大于0时可以看见
HUD_PRINTTALK 左下角聊天栏 at_aiconsole 在控制台内输出信息,只有developer大于等于2时可以看见
HUD_PRINTCENTER 屏幕中心 at_warning 在控制台和Log内输出Warning:开头的信息
at_error 在控制台和Log内输出Error:开头的信息
at_logged 在Log文件内输出信息

另附:SC所有接口
注:按照SC team的命名规则,将类名CABCD改为g_ABCD便是对应的实例名

所以,我们的代码实际上要写成

void PluginInit()
{
    g_PlayerFuncs.ClientPrintAll( HUD_PRINTCONSOLE, "Hello World!" );
}

像cs java一样的又臭又长
保存,再运行

Angelscript: Loading plugin lists
Angelscript: Loading plugin list 'default_plugins.txt'
Angelscript: Loading plugins from file 'default_plugins.txt'
Angelscript: Beginning plugin 'Test' compilation
Angelscript: Starting compilation: 1 scripts
Angelscript: Plugin script compilation succeeded
ERROR: Angelscript: Plugin failed validation

咦,明明都编译通过了,怎么还会说他failed validation呢?
我们回想写其他程序,无论你是用什么工具写的,最后编译时都会被加上一个数字签名来表示下身份,AngelScripts插件也如此,你连签名都没有,SC怎么会知道是谁写的插件呢?
所以,无论你写什么插件,都得在PluginInit里加上这两行

    g_Module.ScriptInfo.SetAuthor("It's me");
    g_Module.ScriptInfo.SetContactInfo("Mario~");

字面意思,分别是作者名称和联系方式

再编译

WARNING: Angelscript: No such plugin 'test', cannot remove
WARNING: Angelscript: No such plugin 'test', cannot reload
Reloading plugin list file to locate plugin data
Angelscript: Loading plugin lists
Angelscript: Loading plugin list 'default_plugins.txt'
Angelscript: Loading plugins from file 'default_plugins.txt'
Angelscript: Beginning plugin 'Test' compilation
Angelscript: Starting compilation: 1 scripts
Angelscript: Plugin script compilation succeeded

全部通过,重载游戏

哎?怎么我在控制台里还是看不见???

别急别急,其实这和游戏启动的方式有关,下文会继续讲

我们只需要把代码改成

void PluginInit()
{
    g_Module.ScriptInfo.SetAuthor("Oh no~");
    g_Module.ScriptInfo.SetContactInfo("MamaMiya");
    //作者与联系信息
}

CClientCommand g_HelloWorld("hello", "Hello", @helloword);
//定义一个客户端命令,变量名g_HelloWorld,命令.hello,描述Hello,callback helloword
void helloword(const CCommand@ pArgs) 
{
    //客户端命令的callback
    g_PlayerFuncs.ClientPrintAll( HUD_PRINTCONSOLE, "Hello World!" );
}

然后重载插件
在控制台输入.hello

] .hello
Hello World!

注:所有由插件添加的命令均为.开头

哇哦!你成功了,你成功编写了你的第一个SC插件,巨大成功!


附录

1. 三个程序入口点的区别

在讲这个之前,我们需要了解SC载入地图时,后台发生的事

一般的来说,SC在你创建服务器或是输入map指令时(无论listenserver或专用服务器),会按照这几个步骤进行

编译AngelScripts插件→创建服务器→遍历游戏所需资源→遍历地图所需资源→以localhost连接服务器→接受服务器信息→预缓存所需资源并生成Soundcache→载入地图→生成可控制游戏内实体→进入游戏并生成可控制玩家实体

而PluginInit和MapInit和MapActivate便是在不同步骤call

入口点 特点
PluginInit 在插件完成编译时call,一旦插件完成编译就不会再次编译
MapInit 在开始载入地图时call,此时地图只存在world_entity,并不存在任何可控实体
MapActivate 在地图生成完毕可控实体时call

所以call的顺序为

PluginInit→MapInit→MapActivate

注意:PluginInit在整个游戏运行过程中只会被call一次
注意2:在主机完全进入游戏前都不会视为游戏开始(即游戏内有可控玩家实体存在)
所以在三个入口点内所有的向玩家发送信息的函数均会无效(此时游戏内可控玩家数为0)

2. AngelScripts变量名等等区别

大部分于Cpp CS Java相同

关键字 特点
int64 最长,可以使用十六进制(0x????),-9,223,372,036,854,775,808~9,223,372,036,854,775,807
int 指long ,可以使用十六进制(0x????),-2,147,483,648~2,147,483,647
int16 指short ,可以使用十六进制(0x????), -32,768~32,767
int8 最小,可以使用十六进制(0x????),-128~127
uint64 与int类似,可以使用十六进制(0x????),无符号,0~18,446,744,073,709,551,615
uint 与int类似,可以使用十六进制(0x????),无符号,0~4,294,967,295
uint16 与int类似,可以使用十六进制(0x????),无符号,0~65,535
uint8 与int类似,可以使用十六进制(0x????),无符号,0~255
float 就是float,不然还会是什么,可以使用e+数字
double 在sc内会转换成float使用,插件内保持double,可以使用e+数字
string 字符串,不然还会是什么
char 特殊,不能正常使用,并不是单纯的字符,SC插件内所有char的内容都用字符串代替,哪怕只有一个字符
bool true和false呗,不然还会是什么
void void就是void呗,不然还会是什么
obj object,带@指handle
auto AngelScripts带类型,SC不允许存在,已屏蔽此类型
array<类型> 数组, 有且只有一维数组,你还想用SC算矩阵不成?(indexing operator时使用的是uint而不是int)
dictionary 词典,不然还会是什么
ref 需手动registe,如同object handle
weakref 与ref类似,会尽可能保持handle存活
enum 非变量,枚举

变量前加const便可变为只读变量,AngelScripts没有define

注释与其他语言一样,你可以使用//进行单行注释,也可以使用/**进行多行注释

void NB()
{
    //这是单行注释
    /***
        这是多行注释
    ***/
}

3. AngelScripts插件安装和编译调试

3.1 安装

3.1.1 以普通全图插件安装

将插件放入svencoop_addon/scripts/plugins文件夹或readme所写文件夹内,在svencoop/default_plugin.txt内添加

"plugin"
    {
        "name" 插件名
        "script" 插件位置(以scripst文件夹为起点)
        "concommandns" 插件所注册的控制台命令名称(如果有的话)
    }

注意需在最后一个}之前

3.1.2 以某地图插件安装

将插件放入svencoop_addon/scripts/maps文件夹
在地图cfg文件内加入map_script 文件所在位置(以scripts/maps为起始文件夹)

注:地图载入插件与全图载入插件并不在一个容器内,两者互相独立互不干扰,编译载入顺序为:全图插件→地图插件

3.2 编译和调试

理论上说,一个正常的插件安装后自动编译使用,不用过多的去管他,而自己写的插件难免会有这样那样的问题,那么调试过程为:

3.2.1 控制台输入developer 1打开开发者模式

3.2.2 控制台输入as_reloadplugin 插件名称进行重新编译

注意:SC使用的AngelScripts无法break,无法打断点,进行递归或循环或嵌套时请仔细检查


参考材料

SC使用AngelScripts官方git
SC使用AngelScripts接口目录
AngelScripts官方手册

2019-08-09 22:56:56 星期五

Categories: 教程合集

Dr.Abc

I ❤ Owl

2
说点什么

avatar
2 评论总数
0 回复总数
0 订阅者
 
最多互动的评论
最热评论总数
2 回复者
eebssk1null 最近回复者
  订阅  
最新 最旧 得票最多
提醒
eebssk1
管理员

一脸懵逼

M0N5T3R_null
管理员

ABC NB