Nekoscript
介绍
NekoScript 是为 Nekoplay 游戏开发设计的自定义脚本语言。其语法轻量、表达性强,对于熟悉 JavaScript 的开发者来说很容易上手,同时包含专为游戏逻辑设计的独特特性。
目录
语法
变量
数据类型
运算符
条件语句
循环
函数
数据结构
常量
JavaScript 对象和函数
语法 →
语法
注释
Nekoscript 使用与其他语言如 JavaScript 或 C 相同的注释风格:
// 单行注释
/*
多行
注释
*/
大小写敏感
NekoScript 区分大小写。这意味着 Variable
, variable
和 VARIABLE
是三个不同的标识符。
行结束
不需要在每行末尾使用 ;
。解释器认为换行足以分隔指令,除非在可能产生歧义的上下文中。
变量 →
变量
在 NekoScript 中有两种主要的变量声明方式:local
和 global
。与 JavaScript 不同,不使用 let
, const
或 var
。
local
用于声明作用域限于定义它们的块(如函数或控制结构 if
, for
等)的变量。
此变量仅存在于定义它的块内。
global
用于声明可以从代码任何部分访问的变量。
global gameManager = {
currentLevel: "Stage_01_Rooftop_Idol",
playerCount: 1,
}
local idol_list = ["Lis", "米歇尔", "지운", "ちぇりー"]
local MimiMood = "serving"
多重定义
可以同时赋值多个变量,甚至可以交换它们的值
local Mimi.x, Mimi.y = getStagePosition("Mimi")
LisSong, MichelleSong = MichelleSong, LisSong
重新赋值
变量可以在之后重新赋值:
local JiwoonStyle
global concertLightLevel = 10 // 不能定义空的全局变量;
JiwoonStyle = "punk"
concertLightLevel = 75
self
Nekoscript 使用此关键字引用当前对象或函数的上下文。
idolProfile = {
name: "ちぇりー",
charmStat: 99,
coquettishRating: self.charmStat / 10,
}
数据类型 →
数据类型
NekoScript 使用多种数据类型,与其他语言类似,但在书写和使用方式上有一些关键差异。
数字
数字可以是整数或小数,按常规方式书写:
local JiwoonStats = {
health: 120,
power: 10.5,
};
文本 (字符串)
字符串可以用单引号、双引号或模板字面量书写:
local cute_group = ['Lis', 'Mimi', 'ちぇりー'];
global mimiMood = "Serving";
local playerMessage = `Cherry used a combo move with a power of ${CherryMoveCount}!`
布尔值
NekoScript 使用标准布尔值:true
和 false
global concertLightsOn = false;
Mimi.isHappy = true
数组 (Arrays) 和对象
可以定义数组(列表)和对象(字典或映射),我们将在第8节深入探讨。现在先看一个简单示例:
local cool_group = ["米歇尔", "지운"];
local MichelleStats = { purity: %01101 };
空值 / 未定义
NekoScript 基于 JavaScript,使用 null
和 undefined
。
local pendingQuest = null;
local currentSoundtrack = undefined;
运算符 →
运算符
NekoScript 提供了一系列运算符,用于执行数学、逻辑和比较操作。
算术运算符
用于执行基本数学运算:
+
加法a + b
-
减法a - b
*
乘法a * b
/
除法a / b
%
取模a % b
**
幂运算a ** b
比较运算符
用于比较值
==
相等a == b
!=
不等a != b
>
大于a > b
<
小于a < b
>=
大于等于a >= b
<=
小于等于a < b
逻辑运算符
用于组合条件和布尔值:
&&
与a && b
||
或a || b
!
非!a
赋值运算符
赋值运算符将操作与重新赋值结合起来:
=
简单赋值
+=
相加并赋值
-=
相减并赋值
*=
相乘并赋值
/=
相除并赋值
语法运算符
NekoScript 使用各种符号来组织代码、定义结构和分隔值。以下是它们的主要用途:
自增和自减 ++
& --
· 给数字加1或减1
Lis.cuteness++
Jiwoon.angst--
圆括号 (
& )
· 分组数学表达式
local finalScore = (stagePoints + comboBonus) * crowdCheers;
· 调用函数
teleport(Michelle.x, Michelle.y);
方括号 [
& ]
· 定义数组
local current_idols = ["Lis", "지운"];
· 访问索引
local leader = current_idols[1]
花括号 {
& }
· 分隔代码块(函数、循环、条件语句等)
if Mimi.loyalty > 90 { startQuest("Lost Teacup"); }
· 定义带属性的对象
local outfit = {
name: "Punk Skirt",
color: "black and purple",
}
逗号 ,
· 分隔函数参数
setPartyStats(Mimi.charm, Lis.vocals))
· 分隔数组或对象中的元素
local theSquad = ["Lis", "Mimi"];
local MimiStats = { life: 20, charm: 100 };
· 声明或重新赋值多个变量
jiwoonX, jiwoonY = getSpotlightPosition();
· 分隔循环部分
repeat 5, i { /**/ }
点号 .
· 访问对象属性
Michelle.microphone.isGold
· 调用方法
game.ui.drawStatusText()
分号 ;
· 分隔或界定指令
local Mimi.mood = "Serving"; local Mimi.health = 100;
冒号 :
· 定义对象属性
local stageLights = { theme: "Goth-Metal" }
· 在三元条件中分隔结果
? true : false
三元运算符 ?
· 内联条件
stageLights.color = Jiwoon.mood == "rebellious" ? "red" : "black"
美元符号 $
· 在模板字面量中访问变量
print(`Welcome, everyone, to ${concertName}!`);
条件语句 →
条件语句
条件结构允许仅在满足特定条件时执行代码的某些部分。
if
评估一个条件,如果为真则执行代码块:
if Mimi.charm <= 0 {
endGame("You ran out of charm! Time for a recharge.");
}
elseif
如果第一个条件不满足,允许评估第二个条件:
if Lis.energy > 9000 {
Lis.performSpecialMove("Twinkle Blast");
} else if Lis.energy > 50 {
Lis.sing("Starlight Serenade");
}
else
如果前面的条件都不满足,则执行一个代码块:
if crowd.isCheering {
Cherry.strikePose("confident");
} else {
Cherry.strikePose("pouty");
}
switch
评估同一条件的多种情况。与 case
和 default
一起使用
switch (game.currentZone) {
case "backstage":
Jiwoon.tuneGuitar();
break;
case "mainstage":
Jiwoon.startShredding();
break;
case "vip_lounge":
Jiwoon.stareIntensely();
break;
default:
print("Ji-woon is lost.");
}
循环 →
循环
NekoScript 提供了多种循环结构来多次执行代码块。
经典 for
允许手动控制变量的初始化、条件和更新。用逗号分隔,不用分号,也不带括号:
for local i = 0, i < 10, i++ {
Mimi.cleanSpot(i);
}
for local note = 0, note < 5, note++ {
Lis.singNote(note);
if Lis.pitchAccuracy[note] < 95 {
print("Lis needs more practice on this note!");
}
}
数组 for
...in
遍历数组元素:
for local member in bandMembers {
print(`Energy for ${member}: ${member.energy}`);
}
for local idol, index in performanceLineup {
idol.assignPose(index);
}
for
...of
遍历对象的属性:
for local fanName of Cherry.fanMail {
print(`Reading message from ${fanName}...`);
}
for local statName, statValue of Michelle.stats {
print(`${statName}: ${statValue}`);
}
while
只要条件满足就重复代码块:
while trophy.isDull {
Mimi.polishTrophy();
}
repeat
是 for
和 while
的简单替代方案,语法非常简洁:
repeat 3 {
Lis.performSignatureMove();
}
repeat 10, beatCount {
Jiwoon.playGuitarChord(beatCount);
print(`Shredding on beat ${beatCount}!`);
}
every
这是 NekoScript 特有的特殊循环。自动每隔几秒执行其内容,无需计时器。
every 200 {
Mimi.bringSnacks();
}
every 0.5 {
stageLights.pulse();
}
函数 →
函数
NekoScript 允许以清晰简洁的方式定义和使用函数。函数可以有名称或匿名(无名称),并且可以接受任意数量的参数。
声明函数
在 nekoscript 中使用关键字 func
声明函数
func checkReadiness(idol) {
idol.isReady = true;
print(idol.name + " is ready for the stage!");
}
函数可以随后用其参数调用:
checkReadiness(Michelle)
匿名函数
也可以定义没有名称的函数,适用于将它们赋值给变量或对象:
local calculateCharm = func(baseCharm) {
local bonus = 10;
return baseCharm + bonus;
}
local finalCharm = calculateCharm(Cherry.baseCharm);
箭头函数 (arrow functions)
NekoScript 允许使用箭头式语法 (=>
) 定义快速函数。这种形式非常适合回调或单行函数:
local addScore = score => score + 10;
也适用于多个参数:
local calculateDamage = (attack, defense) => attack - defense;
在像 .each
这样的方法中,你可以这样写:
readyIdols.map(idol => {
print(`${idol.name} is ready to perform!`);
});
async
& await
在 NekoScript 中可以使用关键字 async
定义异步函数。这些函数允许等待需要时间的操作。
只有当你在其中使用 await
时,async
函数才有用。只能在 async
函数内使用 await
。async
函数的结果是一个 promise。
async func fetchOutfitFromServer(outfitName) {
local outfitData = await loadGameAsset(`outfits/${outfitName}`);
Cherry.wear(outfitData);
print(`Cherry is now wearing the ${outfitName}!`);
}
fetchOutfitFromServer("Niupleis-Idol");
return
在函数内部使用,指示该函数应返回的值。当执行 return
时,函数立即停止并返回该值到调用处。
func get_performance(vocalSkill, stagePresence) {
return vocalSkill * 2 + stagePresence;
}
local LisScore = get_performance(Lis.vocalSkill, Lis.stagePresence);
self
在函数内部,引用当前上下文(例如对象)时使用 self
local Michelle = {
fans: 100,
increaseFans: func(newFans) {
self.fans += newFans;
print(`Michelle now has ${self.fans} fans!`);
},
};
Michelle.increaseFans(50);
数据结构 →
数据结构
NekoScript 提供两种主要数据结构:数组(也称为列表)和对象(具有键值属性的结构)。这些结构允许存储和组织信息。
数组 (arrays)
数组是有序的值集合。使用方括号 [
]
定义,元素用逗号 ,
分隔:
local maid_girls = ["Mimi", "Lis", "ちぇりー"];
可以通过索引访问其元素,从0开始:
print(maid_girls[0]);
也可以在固定索引处定义元素:
maid_girls[2] = "米歇尔";
对象
对象是属性的集合,用花括号 {
}
定义,键和值用冒号 :
定义并用逗号 ,
分隔:
local idolProfile = {
name: "지운",
style: "Goth Metal",
songs: ["Bad Luck", "Rebel Heart", "Shadows"],
};
可以使用点号 .
访问属性:
print(idolProfile.style)
也可以修改它:
idolProfile.style = "Kawaii Metal"
这些结构可以嵌套、组合并自由用作函数参数、计算结果或游戏状态的一部分。
常量 →
引擎常量
NekoScript 包含一系列内置的特殊常量。这些常量始终可用,并提供对系统、游戏、场景信息和其他内部实用程序的直接访问。
变量
这些变量表现为实时值。你可以随时读取它们以获取引擎或游戏当前状态的信息。
@dt
Delta Time:自上一帧以来经过的时间。
@fps
每秒帧数 (Frames Per Second)。
@camera
摄像机的当前位置,作为数组 [x, y]
。
@scene
当前场景的名称 (String)。
@time
自游戏启动以来的总时间(秒)。
@sceneTime
自当前场景加载以来的时间(秒)。
@keys
按下的键列表(按名称:"ArrowUp", "z" 等)。
@scancodes
按下的键列表(按键盘物理代码)。
这些变量可以直接在任何表达式中使用。
函数
这些函数允许执行与引擎相关的特殊转换或计算。
@position(x, y)
将固定坐标转换为引擎坐标(根据摄像机、缩放、比例等)。
@size(w, h)
根据系统比例调整大小。
示例:
if @scene == "MainStage" {
Mimi.position.x += 50 * @dt;
if @sceneTime > 30 {
Michelle.launchConfettiCannon();
}
@camera = @position(cherry.x, cherry.y);
local guitarSize = @size(150, 400);
draw.prop("guitar", Jiwoon.position, guitarSize);
print(`FPS: ${@fps} | Total Game Time: ${@time}`);
}
Javascript →
可用的 JavaScript 函数和对象
NekoScript 构建在 JavaScript 基础之上,因此许多标准语言的函数和对象都可用。
标准函数
这些可能是游戏开发中最有用的函数。
Math
用于数学运算的静态属性和方法。Math ↗
String
用于操作文本的对象。String ↗
Number
用于操作数值的常量和方法。Number ↗
JSON
用于将值转换为 Json 和从 Json 转换的静态方法。Json ↗
RegExp
操作文本模式。RegExp ↗
Date
表示时间中的一个时刻。Date ↗
Boolean
表示逻辑运算结果的值Boolean ↗
Array
多个值(变量、对象、其他数组)的集合,使用单一名称Array ↗
Object
具有键名的值或实体(变量、对象、数组、函数、方法)的集合。Object ↗
示例:
local gameSave = {
score: Math.round(99.5),
isComplete: false, // Boolean
name: getMember().replace(/Member\s*/, ""), // RegExp
playerLevel: Number.parseInt("99"),
keyItems: ["Microphone", "Tea Set"], // Array
lastSave: new Date(),
}; // Object
local savedDataString = JSON.stringify(gameSave);
基本全局函数
这些函数在 JavaScript 中可用:
isFinite()
inNaN()
parseFloat()
parseInt()
特殊值
undefined
NaN
Infinity
全局对象
Object
Boolean
Symbol
数字和日期Number
BigInt
Math
Date
文本和正则表达式String
RegExp
结构Array
Map
Set
WeakMap
WeakSet
特殊类型Int8Array
Uint8Array
Uint8ClampedArray
Int16Array
Uint16Array
Int32Array
Uint32Array
BigInt64Array
BigUint64Array
Float32Array
Float64Array
并发Promise
AsyncFunction
实用工具JSON
Reflect
Proxy
Intl
标准错误
Error
TypeError
ReferenceError
SyntaxError
RangeError
URIError
EvalError
AggregateError