Project1
标题:
MV源码解析之对话框绘制文本原理
[打印本页]
作者:
沉滞的剑
时间:
2019-10-10 00:06
标题:
MV源码解析之对话框绘制文本原理
本帖最后由 沉滞的剑 于 2019-10-10 00:08 编辑
Window_Message绘制文本原理
好久不见, 随便更点写插件时候遇到的东西
顺带附一个我给项目写的小插件, 可能会对其他项目也有用吧
=======================================================
0
对话框是有打字机效果(还有转义符来控制速度和等待)
这是一个看似不起眼, 但实则很有意思的实现
我们来一步一步揭开它是如何绘制多行的带有打字机特效的文本的
部分代码为了利于理解而进行了修改和删减
1. 准备
1.1 原文本
对话框会在战斗和地图上被触发,
但是无论是战斗中还是地图上, 文本的来源都只有
$gameMessage.allText()
复制代码
1.2 文本状态对象
textState = {
index, // 当前位置
x, // x坐标
y, // y坐标
left, // 左起点
height,// 高度
text, // 文本
}
复制代码
这个对象代表了当前帧需要绘制的字符(text[index])的各种信息(x, y, height)
换行的时候可以让 x = left, y += height 就可以了
1.3 开始方法
当窗口准备绘制时会执行startMessage()方法(部分略)
读取原文本并且构建文本状态对象
因为原文本可能有一些转移符所以要先行转换(物品名称, 人物名称, 变量信息等等)
startMessage() {
textState = {};
textState.index = 0;
textState.text = convertEscapeCharacters($gameMessage.allText());
}
复制代码
2. 状态更新
2.1 基础
最基础实现绘制所有字符的方法, process每一次循环都会让textState.index递增
updateMessage(){
while(!isEndOfText(textState)) {
processCharacter(textState)
}
}
processCharacter(textState) {
drawText(textState.text[textState.index++], textState.x, textState.y, w * 2, textState.height)
}
复制代码
2.2 打字机效果
但是首先我们想要一个打字机的效果,
在RM中设定的是每帧打印1个字符,
只需要在while循环中增加一个break就可以(或者去掉循环)
但是我们又想让玩家按住回车键的话会立刻显示所有文本那么就加入一个判断就可以了
如果按下了回车, 那么就不会进入break, 从而在一帧内绘制完所有文本
if(!this._showFast) {
break
}
updateShowFast() {
if (Input.isRepeated("ok")) {
this._showFast = true;
}
}
复制代码
2.3 换行
接下来一个问题是如何换行, 其实也很简单
之前提到了, 仅仅是移动了绘制的位置信息而已
processCharacter() {
text = textState.text[textState.index++]
if (text === '\n') {
processNewLine(textState)
} else {
drawText(...)
}
}
processNewLine(textState) {
textState.x = textState.left;
textState.y += textState.height;
textState.index++;
}
复制代码
2.4 停顿
其他转义符的处理也是一样, 注意有"."和"|"他们设置了一个等待时间
在update中如果这个计数器大于0, 那么就会递减并且跳过绘制, 达到等待和停蹲的效果
processEscapeCharacter(code, textState) {
switch (code) {
case ".":
startWait(15);
break;
case "|":
startWait(60);
break;
}
};
startWait(count){
waitCount = count;
};
updateWait() {
if (waitCount > 0) {
waitCount--;
return true;
} else {
return false;
}
};
update() {
if (updateWait()) {
return;
}
updateMessage()
}
复制代码
===========================================
另附一个按文本长度居中对话框的小插件
/*:
* @help
* 开启居中指令 SetCentralMessage
* 关闭居中指令 UnsetCentralMessage
*/
{
const Window_CentralMessage = class extends Window_Message {
newPage(textState) {
super.newPage(textState);
if (!flag) return;
this.textlines = textState.text
.split("\n")
.map(this.convertEscapeCharacters);
this._lineIndex = 0;
textState.x =
this.width / 2 -
this.textWidth(this.textlines[this._lineIndex]) / 2 -
this.standardPadding();
}
processNewLine(textState) {
super.processNewLine(textState);
if (!flag) return;
this._lineIndex += 1;
textState.x =
this.width / 2 -
this.textWidth(this.textlines[this._lineIndex]) / 2 -
this.standardPadding();
}
};
Window_Message = Window_CentralMessage;
const temp = Game_Interpreter.prototype.pluginCommand;
let flag = false;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
temp.call(this, command, args);
if (command === "SetCentralMessage") {
flag = true;
} else if (command === "UnsetCentralMessage") {
flag = false;
}
};
}
复制代码
欢迎光临 Project1 (https://rpg.blue/)
Powered by Discuz! X3.1