前言:本文并不会教你基础的编程知识,阅读过程中遇到无法理解的名词或段落请善用百度功能.
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 星期五
一脸懵逼
ABC NB