TJS2 深入引擎:Layer、Image、Wave 实战
为什么需要直接调用原生类
KAG 标签覆盖了 80% 的常用场景。当你需要做这些事时,就需要下沉到 TJS2 + 原生类:
- 精确控制图层的位置、大小、不透明度(KAG 的
chara position=不够灵活) - 动态生成图片(比如文字贴图、渐变背景)
- 同时播放多个 BGM 层并独立控制音量
- 自定义存档格式
- 检测用户输入并做响应(键盘、鼠标位置)
- 跨 scenario 文件传递复杂对象(比如 Character 实例)
Layer 图层操作
获取和创建图层
// 获取当前活动图层(通常是文字框所在图层)
var current = Layer.getCurrent();
// 通过编号获取
var bgLayer = Layer.get(0);
var charaLayer = Layer.get(1);
// 创建新图层(返回图层对象)
var customLayer = Layer.create();
customLayer.number = 10; // 设置图层编号
customLayer.show();
图层属性
layer.x = 100; // 左上角 X 坐标
layer.y = 200; // 左上角 Y 坐标
layer.width = 800; // 宽度
layer.height = 600; // 高度
layer.opacity = 128; // 不透明度 0~255
layer.visible = true; // 是否可见
layer.z = 5; // Z 轴(用于 3D 层次,罕见)
图层操作方法
// 加载图片到图层
layer.loadImage("image/bg/school.tlg");
// 在图层上绘制(和上一层合成)
layer.blit(targetLayer, sourceX, sourceY, destX, destY, width, height);
// 清空图层
layer.clear();
// 移动动画
layer.moveTo(500, 300, 1000); // 1000ms 内移动到目标位置
// 缩放动画
layer.scaleTo(1.5, 1.5, 500); // 500ms 内放大到 150%
// 淡出
layer.fadeOut(500);
// 淡入
layer.fadeIn(500);
// 销毁图层
layer.destroy();
Image 图片操作
// 加载图片(返回 Image 对象)
var bg = Image.load("image/bg/sky.tlg");
var hero = Image.load("image/chara/hero.png");
// 图片属性
bg.width; // 宽度
bg.height; // 高度
hero.hasAlpha; // 是否有透明通道
// 图片绘制到图层
bgLayer.drawImage(bg, 0, 0);
charaLayer.drawImage(hero, 100, 200);
// 裁剪绘制(只画图片的一部分)
charaLayer.drawImage(hero, 0, 0, 0, 0, 300, 400);
// 参数:图片对象, destX, destY, srcX, srcY, srcWidth, srcHeight
动态生成图片
// 创建空白图片
var canvas = new Image(800, 600);
// 画渐变
for (var i = 0; i < 800; i++) {
canvas.fillRect(i, 0, 1, 600, RGB(i / 800 * 255, 0, 255 - i / 800 * 255));
}
// 绘制文字
canvas.drawText("标题文字", 400, 300, font, RGB(255, 255, 255));
Wave 音频操作
播放 BGM
// 播放 BGM(循环)
Wave.playBGM("sound/bgm/title.ogg", true);
// 播放 SE(一次)
Wave.playSE("sound/se/click.wav");
// 播放语音
Wave.playVoice("sound/voice/hero_001.wav");
控制播放
// 停止 BGM
Wave.stopBGM();
// 淡出停止
Wave.fadeBGM(500);
// 暂停/恢复
Wave.pauseBGM();
Wave.resumeBGM();
// 设置音量
Wave.setBGMVolume(0.5); // 0.0 ~ 1.0
Wave.setSEVolume(0.8);
多音频通道
吉里吉里2 支持同时播放多个 BGM:
Wave.playBGM("sound/bgm/main.ogg", true); // BGM 通道 0
Wave.playBGM2("sound/bgm/sub.ogg", true); // BGM 通道 1(混音播放)
Wave.setBGMVolume(0.7);
Wave.setBGM2Volume(0.3);
Storage 文件操作
// 检查文件是否存在
Storage.exists("scenario/main.ks");
// 读取文本文件
var content = Storage.readTextFile("scenario/main.ks");
// 写入文本(注意权限和路径)
Storage.writeTextFile("save/custom.txt", "hello");
// 打开 XP3 包
Storage.openArchive("data.xp3");
// 之后直接用 Storage.readTextFile() 就能读包里的文件了
// 关闭归档
Storage.closeArchive();
// 列出目录文件
var files = Storage.listFiles("image/bg/");
Script 动态加载
// 运行时加载额外脚本
Script.load("script/ExtraContent.tjs");
// 从 XP3 包里加载(先 openArchive)
Script.load("data.xp3/script/extra.tjs");
// 动态执行一段代码字符串
Script.eval("var x = 42; Layer.current.message = x;");
用户输入检测
// 键盘检测
Script.onKeyDown = function(key) {
if (key == 0x0D) { // Enter 键
Layer.current.message = "按了回车";
}
};
// 鼠标位置
Script.onMouseMove = function(x, y) {
// Layer.current.message = "鼠标: " + x + "," + y;
};
// 鼠标点击
Script.onMouseDown = function(x, y, button) {
if (button == 0) { // 左键
Layer.current.message = "左键点击 @ " + x + "," + y;
}
};
实战:自定义对话框
// 创建一个带边框和滚动条的对话框
function createMessageBox(x, y, w, h) {
var box = Layer.create();
box.number = 20;
box.x = x; box.y = y;
box.width = w; box.height = h;
// 画半透明背景
box.fillRect(0, 0, w, h, RGBA(0, 0, 0, 180));
// 画边框
box.frameRect(0, 0, w, h, 2, RGB(255, 255, 255));
box.show();
return box;
}
var msgBox = createMessageBox(50, 400, 700, 150);
注意事项
- 直接操作原生类绕过了 KAG 的安全检查,写错参数可能导致崩溃
- 图层编号如果和 KAG 内部的冲突(KAG 通常用 0~10),注意用更大的编号(比如 100+)
- 引擎是单线程的,所有动画和脚本在主线程顺序执行——大图片加载会卡界面,用异步加载