/*:
* @plugindesc 立绘眨眼插件 - 终极无闪烁版
* @author Doubao
*
* @param eyeOpenSuffix
* @text 睁眼图片后缀
* @desc 睁眼状态图片的文件名后缀
* @default _open
*
* @param eyeCloseSuffix
* @text 闭眼图片后缀
* @desc 闭眼状态图片的文件名后缀
* @default _close
*
* @param blinkInterval
* @text 眨眼间隔(帧)
* @desc 平均眨眼间隔时间(游戏帧,60帧=1秒)
* @default 180
*
* @param blinkSpeed
* @text 眨眼速度(帧)
* @desc 眨眼动画持续帧数
* @default 12
*
* @help
* 使用方法:
* 1. 将睁眼和闭眼的立绘图片放在img/pictures文件夹中
* 2. 确保两张图片文件名相同,仅后缀不同(默认为_open和_close)
* 3. 在事件中使用"显示图片"命令显示立绘
* 4. 使用插件命令控制眨眼效果
*
* 插件命令:
* EyeBlink init [图片ID] [基础名称] - 初始化立绘
* EyeBlink blink [图片ID] - 触发眨眼
* EyeBlink auto [图片ID] - 启用自动眨眼
* EyeBlink stop [图片ID] - 停止自动眨眼
*/
(function() {
// 获取插件参数
var parameters = PluginManager.parameters('EyeBlink');
var eyeOpenSuffix = String(parameters['eyeOpenSuffix'] || '_open');
var eyeCloseSuffix = String(parameters['eyeCloseSuffix'] || '_close');
var blinkInterval = Number(parameters['blinkInterval'] || 180);
var blinkSpeed = Number(parameters['blinkSpeed'] || 12);
// 存储立绘数据
var _portraitData = {};
var _bitmapCache = {};
// 预加载图片并确保已加载
function preloadBitmap(name) {
if (!_bitmapCache[name]) {
_bitmapCache[name] = ImageManager.loadPicture(name);
}
return _bitmapCache[name];
}
// 确保两张图片都已加载完成
function ensureBitmapsLoaded(baseName) {
var openBitmap = preloadBitmap(baseName + eyeOpenSuffix);
var closeBitmap = preloadBitmap(baseName + eyeCloseSuffix);
return openBitmap.isReady() && closeBitmap.isReady();
}
// 创建睁眼和闭眼精灵层
function createPortraitSprites(pictureId, baseName) {
var originalSprite = SceneManager._scene._spriteset._pictureContainer.children[pictureId - 1];
if (!(originalSprite instanceof Sprite_Picture)) {
console.error(`[EyeBlink] 图片ID ${pictureId} 不存在或不是立绘`);
return null;
}
// 创建睁眼精灵(底层)
var openSprite = new Sprite();
openSprite.bitmap = preloadBitmap(baseName + eyeOpenSuffix);
// 创建闭眼精灵(上层)
var closeSprite = new Sprite();
closeSprite.bitmap = preloadBitmap(baseName + eyeCloseSuffix);
// 设置精灵属性(使用更安全的方法)
function setupSprite(sprite) {
sprite.x = originalSprite.x;
sprite.y = originalSprite.y;
sprite.z = originalSprite.z;
sprite.anchor.x = originalSprite.anchor.x;
sprite.anchor.y = originalSprite.anchor.y;
sprite.scale.x = originalSprite.scale.x;
sprite.scale.y = originalSprite.scale.y;
sprite.rotation = originalSprite.rotation;
sprite.opacity = 0; // 初始都不可见
}
setupSprite(openSprite);
setupSprite(closeSprite);
// 添加到容器
var parent = originalSprite.parent;
parent.addChild(openSprite);
parent.addChild(closeSprite);
// 隐藏原始精灵
originalSprite.visible = false;
return { openSprite, closeSprite };
}
// 清理立绘精灵
function cleanupPortraitSprites(pictureId) {
var data = _portraitData[pictureId];
if (!data) return;
// 从父容器中移除精灵
if (data.openSprite && data.openSprite.parent) {
data.openSprite.parent.removeChild(data.openSprite);
}
if (data.closeSprite && data.closeSprite.parent) {
data.closeSprite.parent.removeChild(data.closeSprite);
}
// 显示原始精灵
if (data.originalSprite) {
data.originalSprite.visible = true;
}
// 删除数据
delete _portraitData[pictureId];
console.log(`[EyeBlink] 清理立绘 ID${pictureId}`);
}
// 初始化立绘(增强版)
function initPortrait(pictureId, baseName) {
// 先清理可能存在的旧精灵
cleanupPortraitSprites(pictureId);
// 预加载两张图片
if (!ensureBitmapsLoaded(baseName)) {
console.log(`[EyeBlink] 图片ID ${pictureId} 资源未完全加载,延迟初始化`);
// 延迟检查
setTimeout(() => {
if (ensureBitmapsLoaded(baseName)) {
initPortrait(pictureId, baseName);
} else {
console.error(`[EyeBlink] 图片ID ${pictureId} 资源加载超时`);
}
}, 300);
return false;
}
// 创建精灵
var sprites = createPortraitSprites(pictureId, baseName);
if (!sprites) return false;
// 初始化数据
_portraitData[pictureId] = {
baseName: baseName,
isBlinking: false,
blinkPhase: 0, // 0:睁眼, 1:闭眼过渡, 2:闭眼, 3:睁眼过渡
phaseCounter: 0,
autoBlink: false,
intervalCounter: Math.floor(Math.random() * blinkInterval),
openSprite: sprites.openSprite,
closeSprite: sprites.closeSprite,
originalSprite: SceneManager._scene._spriteset._pictureContainer.children[pictureId - 1],
// 存储原始精灵的可见性和透明度
originalVisible: true,
originalOpacity: 255
};
// 默认显示睁眼状态
sprites.openSprite.opacity = 255;
sprites.closeSprite.opacity = 0;
console.log(`[EyeBlink] 初始化立绘 ID${pictureId}: ${baseName}`);
return true;
}
// 执行眨眼
function blinkPortrait(pictureId) {
var data = _portraitData[pictureId];
if (!data || data.isBlinking) return;
// 只在图片可见且不透明时眨眼
if (!data.originalVisible || data.originalOpacity < 10) return;
data.isBlinking = true;
data.blinkPhase = 1; // 开始闭眼过渡
data.phaseCounter = 0;
console.log(`[EyeBlink] 立绘 ID${pictureId} 开始眨眼`);
}
// 更新眨眼动画(平滑过渡版)
function updateBlinkAnimation() {
for (var pictureId in _portraitData) {
var data = _portraitData[pictureId];
if (!data.isBlinking) continue;
// 如果图片不可见或完全透明,停止眨眼动画
if (!data.originalVisible || data.originalOpacity < 10) {
data.isBlinking = false;
data.blinkPhase = 0;
data.openSprite.opacity = data.originalOpacity;
data.closeSprite.opacity = 0;
continue;
}
data.phaseCounter++;
// 眨眼阶段控制
switch (data.blinkPhase) {
case 1: // 闭眼过渡
var closeProgress = data.phaseCounter / (blinkSpeed / 2);
if (closeProgress >= 1) {
data.blinkPhase = 2;
data.phaseCounter = 0;
data.openSprite.opacity = 0;
data.closeSprite.opacity = data.originalOpacity;
} else {
data.openSprite.opacity = data.originalOpacity * (1 - closeProgress);
data.closeSprite.opacity = data.originalOpacity * closeProgress;
}
break;
case 2: // 闭眼状态
if (data.phaseCounter >= 2) { // 保持2帧
data.blinkPhase = 3;
data.phaseCounter = 0;
}
break;
case 3: // 睁眼过渡
var openProgress = data.phaseCounter / (blinkSpeed / 2);
if (openProgress >= 1) {
data.blinkPhase = 0;
data.isBlinking = false;
data.openSprite.opacity = data.originalOpacity;
data.closeSprite.opacity = 0;
console.log(`[EyeBlink] 立绘 ID${pictureId} 眨眼结束`);
} else {
data.openSprite.opacity = data.originalOpacity * openProgress;
data.closeSprite.opacity = data.originalOpacity * (1 - openProgress);
}
break;
}
}
}
// 更新自动眨眼
function updateAutoBlink() {
for (var pictureId in _portraitData) {
var data = _portraitData[pictureId];
if (!data.autoBlink || data.isBlinking) continue;
// 只在图片可见且不透明时自动眨眼
if (!data.originalVisible || data.originalOpacity < 10) continue;
data.intervalCounter--;
if (data.intervalCounter <= 0) {
blinkPortrait(parseInt(pictureId));
data.intervalCounter = blinkInterval + Math.floor(Math.random() * blinkInterval/2);
}
}
}
// 同步原始精灵的属性到新精灵
function syncSpriteProperties() {
for (var pictureId in _portraitData) {
var data = _portraitData[pictureId];
var originalSprite = data.originalSprite;
// 如果原始精灵已被移除(如地图切换),清理立绘
if (!originalSprite || !originalSprite.parent) {
cleanupPortraitSprites(pictureId);
continue;
}
// 存储原始精灵的可见性和透明度
data.originalVisible = originalSprite.visible;
data.originalOpacity = originalSprite.opacity;
// 如果图片不可见或完全透明,直接隐藏所有精灵
if (!originalSprite.visible || originalSprite.opacity === 0) {
data.openSprite.visible = false;
data.closeSprite.visible = false;
continue;
}
// 显示精灵
data.openSprite.visible = true;
data.closeSprite.visible = true;
// 如果没有在眨眼,根据当前状态设置透明度
if (!data.isBlinking) {
data.openSprite.opacity = originalSprite.opacity;
data.closeSprite.opacity = 0;
}
}
}
// 处理插件命令
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
_Game_Interpreter_pluginCommand.call(this, command, args);
if (command === 'EyeBlink') {
var pictureId = Number(args[1] || 1);
switch (args[0]) {
case 'init':
var baseName = String(args[2] || '');
if (baseName) {
initPortrait(pictureId, baseName);
}
break;
case 'blink':
blinkPortrait(pictureId);
break;
case 'auto':
if (_portraitData[pictureId]) {
_portraitData[pictureId].autoBlink = true;
console.log(`[EyeBlink] 立绘 ID${pictureId} 启用自动眨眼`);
}
break;
case 'stop':
if (_portraitData[pictureId]) {
_portraitData[pictureId].autoBlink = false;
console.log(`[EyeBlink] 立绘 ID${pictureId} 停止自动眨眼`);
}
break;
case 'cleanup':
cleanupPortraitSprites(pictureId);
break;
}
}
};
// 地图切换时清理所有立绘
var _Scene_Map_terminate = Scene_Map.prototype.terminate;
Scene_Map.prototype.terminate = function() {
_Scene_Map_terminate.call(this);
// 清理所有立绘
for (var pictureId in _portraitData) {
cleanupPortraitSprites(pictureId);
}
};
// 游戏主循环更新
var _Scene_Base_update = Scene_Base.prototype.update;
Scene_Base.prototype.update = function() {
_Scene_Base_update.call(this);
updateBlinkAnimation();
updateAutoBlink();
syncSpriteProperties(); // 同步精灵属性
};
})();
使用方法
自动清理(推荐):
使用修改后的插件,地图切换时会自动清理所有立绘,无需额外操作。
手动清理:
在事件中使用EyeBlink cleanup [图片ID]命令手动清理特定立绘。
插件命令:EyeBlink cleanup 1 # 清理图片ID为1的立绘
确保地图切换前消除立绘:
在跳转地图前,使用原生的「消失图片」命令消除立绘。
消失图片:1
转移玩家:目标地图ID, X坐标, Y坐标
显示图片:1, character_open.png, 画面中央, 0, 100%, 100%, 255, 通常合成
插件命令:EyeBlink init 1 character
插件命令:EyeBlink auto 1
等待:180帧
消失图片:1
图片 ID 必须一致
初始化立绘(EyeBlink init)、启用眨眼(EyeBlink auto)和消除立绘(消失图片)时使用的 图片 ID 必须相同。例如:
显示图片时用 图片 ID=1。
插件命令用 EyeBlink init 1 character。
消除立绘时用 消失图片:1。