【MZ插件】CRT显示器扫描线和随机故障等效果
指挥AI写的,效果还不赖,分享出来,功能文档里写的很明白了,不过自己感觉故障效果没那么完美,勉强可以用,MV 不知道能不能用可以自行测试本来录屏的转gif有点糊,明白我意思就好
JAVASCRIPT 代码下载
/*:
* @target MZ
* @plugindesc CRT 显示器滤镜,包含扫描线、屏幕曲率、暗角、色差和随机故障效果。
* @author enpitsulin
*
* @param scanlineIntensity
* @text 扫描线强度
* @desc 扫描线效果的强度 (0.0 - 1.0)。
* @type number
* @decimals 2
* @min 0
* @max 1
* @default 0.35
*
* @param curvatureAmount
* @text 屏幕曲率
* @desc 桶形畸变的程度 (0.0 - 1.0)。
* @type number
* @decimals 2
* @min 0
* @max 1
* @default 0.25
*
* @param vignetteIntensity
* @text 暗角强度
* @desc 屏幕边缘暗化的程度 (0.0 - 1.0)。
* @type number
* @decimals 2
* @min 0
* @max 1
* @default 0.30
*
* @param chromaticAberration
* @text 色差强度
* @desc RGB 通道分离的程度 (0.0 - 1.0)。
* @type number
* @decimals 2
* @min 0
* @max 1
* @default 0.15
*
* @param colorBleeding
* @text 色溢强度
* @desc 红色通道水平扩散的程度 (0.0 - 1.0)。
* @type number
* @decimals 2
* @min 0
* @max 1
* @default 0.12
*
* @param glitchFrequency
* @text 故障频率
* @desc 故障效果平均间隔秒数。
* @type number
* @decimals 1
* @min 0
* @default 5.0
*
* @param glitchIntensity
* @text 故障强度
* @desc 故障效果的最大强度 (0.0 - 1.0)。
* @type number
* @decimals 2
* @min 0
* @max 1
* @default 0.60
*
* @param glitchEnabled
* @text 启用故障效果
* @desc 是否启用随机故障效果(手动触发仍然可用)。
* @type boolean
* @default true
*
* @param enabled
* @text 默认启用
* @desc 是否默认启用 CRT 滤镜效果。
* @type boolean
* @default true
*
* @command setEnabled
* @text 设置启用状态
* @desc 启用或禁用 CRT 滤镜效果。
*
* @arg enabled
* @text 启用
* @type boolean
* @default true
*
* @command setGlitchEnabled
* @text 设置故障效果启用状态
* @desc 启用或禁用随机故障效果(手动触发仍然可用)。
*
* @arg enabled
* @text 启用
* @type boolean
* @default true
*
* @command triggerGlitch
* @text 触发故障效果
* @desc 手动触发一次故障效果。
*
* @arg duration
* @text 持续时间(帧)
* @desc 故障效果的持续帧数(默认:随机 10-40)。
* @type number
* @min 1
* @default 0
*
* @command setGlitchFrequency
* @text 设置故障频率
* @desc 更改自动故障效果的平均间隔。
*
* @arg frequency
* @text 频率(秒)
* @type number
* @decimals 1
* @min 0
* @default 5.0
*
* @help
* CRTFilter.js
*
* 为游戏画面应用 CRT(阴极射线管)显示器效果,包括扫描线、桶形畸变、
* 暗角、色差和周期性故障效果。
*
* 此滤镜钩入 Spriteset_Base,因此会同时应用于地图场景和战斗场景。
*
* 插件命令:
* setEnabled - 开启/关闭 CRT 效果
* setGlitchEnabled - 开启/关闭自动故障效果
* triggerGlitch - 手动触发故障效果
* setGlitchFrequency- 更改自动故障间隔
*/
void(function(){
"use strict";
var PLUGIN_NAME = "CRTFilter";
var params = PluginManager.parameters(PLUGIN_NAME);
var SETTINGS = {
scanlineIntensity: Number(params["scanlineIntensity"] || 0.35),
curvatureAmount: Number(params["curvatureAmount"] || 0.25),
vignetteIntensity: Number(params["vignetteIntensity"] || 0.30),
chromaticAberration: Number(params["chromaticAberration"] || 0.15),
colorBleeding: Number(params["colorBleeding"] || 0.12),
glitchFrequency: Number(params["glitchFrequency"] || 5.0),
glitchIntensity: Number(params["glitchIntensity"] || 0.60),
glitchEnabled: params["glitchEnabled"] !== "false",
enabled: params["enabled"] !== "false",
};
// =========================================================================
// GlitchStateMachine Class - Manages glitch timing and state
// =========================================================================
function GlitchStateMachine(){
this.initialize.apply(this, arguments);
}
GlitchStateMachine.prototype.initialize = function(options){
this.state = "cooldown"; // cooldown | attack | sustain | release
this.timer = 0;
this.phaseTimer = 0;
this.duration = 0;
this.maxIntensity = options.maxIntensity || 0.6;
this.frequency = options.frequency || 5.0;
this.enabled = options.glitchEnabled !== false;
this.currentIntensity = 0;
this.cooldownTarget = this._randomCooldown();
};
GlitchStateMachine.prototype._randomCooldown = function(){
var base = this.frequency * 60;
return Math.floor(base * 0.5 + Math.random() * base);
};
GlitchStateMachine.prototype._randomDuration = function(){
return Math.floor(10 + Math.random() * 30);
};
GlitchStateMachine.prototype.update = function(){
switch(this.state){
case"cooldown":
this.timer++;
if(this.enabled && this.timer >= this.cooldownTarget){
this.trigger(0);
}
break;
case"attack": {
this.phaseTimer++;
var attackLen = Math.max(1, Math.floor(this.duration * 0.2));
var t = this.phaseTimer / attackLen;
this.currentIntensity = t * this.maxIntensity;
if(this.phaseTimer >= attackLen){
this.state = "sustain";
this.phaseTimer = 0;
}
break;
}
case"sustain": {
this.phaseTimer++;
var sustainLen = Math.max(
1,
Math.floor(this.duration * 0.5)
);
this.currentIntensity = this.maxIntensity;
if(this.phaseTimer >= sustainLen){
this.state = "release";
this.phaseTimer = 0;
}
break;
}
case"release": {
this.phaseTimer++;
var releaseLen = Math.max(
1,
Math.floor(this.duration * 0.3)
);
var rt = 1 - this.phaseTimer / releaseLen;
this.currentIntensity = rt * this.maxIntensity;
if(this.phaseTimer >= releaseLen){
this._end();
}
break;
}
}
};
GlitchStateMachine.prototype.trigger = function(duration){
this.state = "attack";
this.phaseTimer = 0;
this.duration = duration > 0 ? duration : this._randomDuration();
};
GlitchStateMachine.prototype._end = function(){
this.state = "cooldown";
this.timer = 0;
this.phaseTimer = 0;
this.currentIntensity = 0;
this.cooldownTarget = this._randomCooldown();
};
GlitchStateMachine.prototype.setEnabled = function(value){
this.enabled = value;
};
GlitchStateMachine.prototype.setFrequency = function(seconds){
this.frequency = seconds;
};
GlitchStateMachine.prototype.getIntensity = function(){
returnthis.currentIntensity;
};
// =========================================================================
// Fragment Shader (GLSL ES 1.0)
// =========================================================================
var FRAGMENT_SRC =
"precision mediump float;" +
"varying vec2 vTextureCoord;" +
"uniform sampler2D uSampler;" +
"uniform float uTime;" +
"uniform float uScanlineIntensity;" +
"uniform float uCurvature;" +
"uniform float uVignette;" +
"uniform float uChromatic;" +
"uniform float uBleeding;" +
"uniform float uGlitchStrength;" +
"uniform float uGlitchSeed;" +
"" +
"float rand(vec2 co) {" +
"return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);" +
"}" +
"" +
"vec2 curveUV(vec2 uv, float amount) {" +
"vec2 c = uv - 0.5;" +
"float r2 = dot(c, c);" +
"vec2 curved = uv + c * r2 * amount * 0.5;" +
"return curved;" +
"}" +
"" +
"void main() {" +
"vec2 uv = vTextureCoord;" +
"" +
"uv = curveUV(uv, uCurvature);" +
"" +
"if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {" +
" gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);" +
" return;" +
"}" +
"" +
"float bandY = floor(uv.y * 20.0);" +
"float bandRand = rand(vec2(bandY, uGlitchSeed));" +
"float displacement = 0.0;" +
"if (uGlitchStrength > 0.0 && bandRand > 0.7) {" +
" displacement = (bandRand - 0.7) / 0.3 * 0.06 * uGlitchStrength;" +
" displacement *= sign(rand(vec2(bandY + 1.0, uGlitchSeed)) - 0.5);" +
"}" +
"vec2 glitchUV = vec2(uv.x + displacement, uv.y);" +
"" +
"float caBase = uChromatic * 0.004;" +
"float caGlitch = uGlitchStrength * 0.012;" +
"float caAmount = caBase + caGlitch;" +
"" +
"float r = texture2D(uSampler, vec2(glitchUV.x + caAmount, glitchUV.y)).r;" +
"float g = texture2D(uSampler, glitchUV).g;" +
"float b = texture2D(uSampler, vec2(glitchUV.x - caAmount, glitchUV.y)).b;" +
"float a = texture2D(uSampler, glitchUV).a;" +
"" +
"float bleedOffset = uBleeding * 0.003;" +
"float rBleed = texture2D(uSampler, vec2(glitchUV.x + bleedOffset, glitchUV.y)).r;" +
"r = mix(r, rBleed, 0.5);" +
"" +
"float scanline = 1.0 - uScanlineIntensity * 0.5 * (1.0 - cos(vTextureCoord.y * 1000.0 * 3.14159 * 2.0));" +
"r *= scanline;" +
"g *= scanline;" +
"b *= scanline;" +
"" +
"vec2 vig = uv - 0.5;" +
"float vigDist = dot(vig, vig);" +
"float vigFactor = 1.0 - vigDist * uVignette * 2.5;" +
"vigFactor = clamp(vigFactor, 0.0, 1.0);" +
"r *= vigFactor;" +
"g *= vigFactor;" +
"b *= vigFactor;" +
"" +
"if (uGlitchStrength > 0.0) {" +
" float flicker = 1.0 + (rand(vec2(uTime, uGlitchSeed)) - 0.5) * 0.3 * uGlitchStrength;" +
" r *= flicker;" +
" g *= flicker;" +
" b *= flicker;" +
"}" +
"" +
"if (uGlitchStrength > 0.0) {" +
" float noiseLine = rand(vec2(floor(uv.y * 300.0), uGlitchSeed + 7.0));" +
" if (noiseLine > 0.97) {" +
" float noise = rand(vec2(uv.x * 100.0, uGlitchSeed + uv.y)) * uGlitchStrength;" +
" r = mix(r, noise, 0.4 * uGlitchStrength);" +
" g = mix(g, noise, 0.4 * uGlitchStrength);" +
" b = mix(b, noise, 0.4 * uGlitchStrength);" +
" }" +
"}" +
"" +
"gl_FragColor = vec4(clamp(r, 0.0, 1.0), clamp(g, 0.0, 1.0), clamp(b, 0.0, 1.0), a);" +
"}";
// =========================================================================
// CRTFilter Class
// =========================================================================
function CRTFilter(){
this.initialize.apply(this, arguments);
}
CRTFilter.prototype = Object.create(PIXI.Filter.prototype);
CRTFilter.prototype.constructor = CRTFilter;
CRTFilter.prototype.initialize = function(){
PIXI.Filter.call(this, null, FRAGMENT_SRC);
this.uniforms.uTime = 0;
this.uniforms.uScanlineIntensity = SETTINGS.scanlineIntensity;
this.uniforms.uCurvature = SETTINGS.curvatureAmount;
this.uniforms.uVignette = SETTINGS.vignetteIntensity;
this.uniforms.uChromatic = SETTINGS.chromaticAberration;
this.uniforms.uBleeding = SETTINGS.colorBleeding;
this.uniforms.uGlitchStrength = 0;
this.uniforms.uGlitchSeed = 0;
this._enabled = SETTINGS.enabled;
this._time = 0;
this._glitch = new GlitchStateMachine({
maxIntensity: SETTINGS.glitchIntensity,
frequency: SETTINGS.glitchFrequency,
glitchEnabled: SETTINGS.glitchEnabled,
});
};
CRTFilter.prototype.update = function(){
if(!this._enabled){
this.uniforms.uGlitchStrength = 0;
return;
}
this._time++;
this.uniforms.uTime = this._time * 0.01;
this._glitch.update();
this.uniforms.uGlitchStrength = this._glitch.getIntensity();
if(this.uniforms.uGlitchStrength > 0){
this.uniforms.uGlitchSeed = Math.random() * 100;
}
};
CRTFilter.prototype.setEnabled = function(value){
this._enabled = value;
this.enabled = value;
};
CRTFilter.prototype.setGlitchEnabled = function(value){
this._glitch.setEnabled(value);
};
CRTFilter.prototype.triggerGlitch = function(duration){
if(this._enabled){
this._glitch.trigger(duration || 0);
}
};
CRTFilter.prototype.setGlitchFrequency = function(seconds){
this._glitch.setFrequency(seconds);
};
// Make available globally for save/load compatibility
window.CRTFilter = CRTFilter;
window.GlitchStateMachine = GlitchStateMachine;
// =========================================================================
// Engine Hooks
// =========================================================================
var _Spriteset_Base_createOverallFilters =
Spriteset_Base.prototype.createOverallFilters;
Spriteset_Base.prototype.createOverallFilters = function(){
_Spriteset_Base_createOverallFilters.call(this);
this._crtFilter = new CRTFilter();
if(!this._crtFilter._enabled){
this._crtFilter.enabled = false;
}
this.filters.push(this._crtFilter);
};
var _Spriteset_Base_update = Spriteset_Base.prototype.update;
Spriteset_Base.prototype.update = function(){
_Spriteset_Base_update.call(this);
if(this._crtFilter){
this._crtFilter.update();
}
};
// =========================================================================
// Plugin Commands
// =========================================================================
PluginManager.registerCommand(PLUGIN_NAME, "setEnabled", function(args){
var scene = SceneManager._scene;
if(scene && scene._spriteset && scene._spriteset._crtFilter){
var enabled = args.enabled === "true";
scene._spriteset._crtFilter.setEnabled(enabled);
}
});
PluginManager.registerCommand(PLUGIN_NAME, "setGlitchEnabled", function(args){
var scene = SceneManager._scene;
if(scene && scene._spriteset && scene._spriteset._crtFilter){
var enabled = args.enabled === "true";
scene._spriteset._crtFilter.setGlitchEnabled(enabled);
}
});
PluginManager.registerCommand(
PLUGIN_NAME,
"triggerGlitch",
function(args){
var scene = SceneManager._scene;
if(scene && scene._spriteset && scene._spriteset._crtFilter){
var duration = Number(args.duration) || 0;
scene._spriteset._crtFilter.triggerGlitch(duration);
}
}
);
PluginManager.registerCommand(
PLUGIN_NAME,
"setGlitchFrequency",
function(args){
var scene = SceneManager._scene;
if(scene && scene._spriteset && scene._spriteset._crtFilter){
var frequency = Number(args.frequency) || 5.0;
scene._spriteset._crtFilter.setGlitchFrequency(frequency);
}
}
);
})();
本帖来自P1论坛作者铅笔描绘的思念,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=498644若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
页:
[1]