设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 2702|回复: 1
打印 上一主题 下一主题

[交流讨论] MV源码解析之对话框绘制文本原理

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1882
在线时间
1552 小时
注册时间
2013-4-13
帖子
917
跳转到指定楼层
1
发表于 2019-10-10 00:06:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 沉滞的剑 于 2019-10-10 00:08 编辑

Window_Message绘制文本原理

好久不见, 随便更点写插件时候遇到的东西
顺带附一个我给项目写的小插件, 可能会对其他项目也有用吧
=======================================================

0
对话框是有打字机效果(还有转义符来控制速度和等待)
这是一个看似不起眼, 但实则很有意思的实现
我们来一步一步揭开它是如何绘制多行的带有打字机特效的文本的
部分代码为了利于理解而进行了修改和删减

1. 准备

1.1 原文本
对话框会在战斗和地图上被触发,
但是无论是战斗中还是地图上, 文本的来源都只有


  1. $gameMessage.allText()
复制代码


1.2 文本状态对象


  1. textState = {
  2.         index, // 当前位置
  3.         x,     // x坐标
  4.         y,     // y坐标
  5.         left,  // 左起点
  6.         height,// 高度
  7.         text,  // 文本
  8. }
复制代码


这个对象代表了当前帧需要绘制的字符(text[index])的各种信息(x, y, height)
换行的时候可以让 x = left, y += height 就可以了

1.3 开始方法

当窗口准备绘制时会执行startMessage()方法(部分略)
读取原文本并且构建文本状态对象
因为原文本可能有一些转移符所以要先行转换(物品名称, 人物名称, 变量信息等等)


  1. startMessage() {
  2.         textState = {};
  3.         textState.index = 0;
  4.         textState.text = convertEscapeCharacters($gameMessage.allText());
  5. }
复制代码


2. 状态更新

2.1 基础

最基础实现绘制所有字符的方法, process每一次循环都会让textState.index递增


  1. updateMessage(){
  2.         while(!isEndOfText(textState)) {
  3.                 processCharacter(textState)
  4.         }
  5. }

  6. processCharacter(textState) {
  7.         drawText(textState.text[textState.index++], textState.x, textState.y, w * 2, textState.height)
  8. }
复制代码


2.2 打字机效果

但是首先我们想要一个打字机的效果,
在RM中设定的是每帧打印1个字符,
只需要在while循环中增加一个break就可以(或者去掉循环)
但是我们又想让玩家按住回车键的话会立刻显示所有文本那么就加入一个判断就可以了
如果按下了回车, 那么就不会进入break, 从而在一帧内绘制完所有文本


  1. if(!this._showFast) {
  2.         break
  3. }

  4. updateShowFast() {
  5.   if (Input.isRepeated("ok")) {
  6.     this._showFast = true;
  7.   }
  8. }
复制代码


2.3 换行

接下来一个问题是如何换行, 其实也很简单
之前提到了, 仅仅是移动了绘制的位置信息而已


  1. processCharacter() {
  2.         text = textState.text[textState.index++]
  3.         if (text === '\n') {
  4.                 processNewLine(textState)
  5.         } else {
  6.                 drawText(...)
  7.         }
  8. }

  9. processNewLine(textState) {
  10.   textState.x = textState.left;
  11.   textState.y += textState.height;
  12.   textState.index++;
  13. }
复制代码


2.4 停顿
其他转义符的处理也是一样, 注意有"."和"|"他们设置了一个等待时间
在update中如果这个计数器大于0, 那么就会递减并且跳过绘制, 达到等待和停蹲的效果


  1. processEscapeCharacter(code, textState) {
  2.   switch (code) {
  3.     case ".":
  4.       startWait(15);
  5.       break;
  6.     case "|":
  7.       startWait(60);
  8.       break;
  9.         }
  10. };

  11. startWait(count){
  12.   waitCount = count;
  13. };

  14. updateWait() {
  15.   if (waitCount > 0) {
  16.     waitCount--;
  17.     return true;
  18.   } else {
  19.     return false;
  20.   }
  21. };

  22. update() {
  23.     if (updateWait()) {
  24.       return;
  25.     }
  26.         updateMessage()
  27. }
复制代码

===========================================

另附一个按文本长度居中对话框的小插件


  1. /*:
  2. * @help
  3. * 开启居中指令 SetCentralMessage
  4. * 关闭居中指令 UnsetCentralMessage
  5. */
  6. {
  7.   const Window_CentralMessage = class extends Window_Message {
  8.     newPage(textState) {
  9.       super.newPage(textState);
  10.       if (!flag) return;
  11.       this.textlines = textState.text
  12.         .split("\n")
  13.         .map(this.convertEscapeCharacters);
  14.       this._lineIndex = 0;
  15.       textState.x =
  16.         this.width / 2 -
  17.         this.textWidth(this.textlines[this._lineIndex]) / 2 -
  18.         this.standardPadding();
  19.     }

  20.     processNewLine(textState) {
  21.       super.processNewLine(textState);
  22.       if (!flag) return;
  23.       this._lineIndex += 1;
  24.       textState.x =
  25.         this.width / 2 -
  26.         this.textWidth(this.textlines[this._lineIndex]) / 2 -
  27.         this.standardPadding();
  28.     }
  29.   };
  30.   Window_Message = Window_CentralMessage;
  31.   const temp = Game_Interpreter.prototype.pluginCommand;
  32.   let flag = false;
  33.   Game_Interpreter.prototype.pluginCommand = function(command, args) {
  34.     temp.call(this, command, args);
  35.     if (command === "SetCentralMessage") {
  36.       flag = true;
  37.     } else if (command === "UnsetCentralMessage") {
  38.       flag = false;
  39.     }
  40.   };
  41. }


复制代码

评分

参与人数 4+4 收起 理由
白嫩白嫩的 + 1 精品文章
康姆图帕帕 + 1 塞糖
qq57271884 + 1 塞糖
mr24970985 + 1 塞糖

查看全部评分

夏普的道具店

塞露提亚-道具屋的经营妙方同人作品
发布帖:点击这里
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-5-13 05:42

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表