diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 399a4c1b..09bde8b4 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -222,6 +222,7 @@ action | text_1_s | comment_s | autoText_s + | scrollText_s | setText_s | tip_s | setValue_s @@ -365,6 +366,19 @@ var code = '{"type": "autoText", "text": "'+title+EvalString_1+EvalString_2+'", return code; */; +scrollText_s + : '滚动剧情文本:' '时间' Int '不等待执行完毕' Bool? BGNL? EvalString Newline + + +/* scrollText_s +tooltip : scrollText:滚动剧情文本,将从下到上进行滚动显示。 +helpUrl : https://h5mota.com/games/template/docs/#/event?id=scrollText%ef%bc%9a%e6%bb%9a%e5%8a%a8%e5%89%a7%e6%83%85%e6%96%87%e6%9c%ac +default : [5000,false,"时间是总时间,可以使用setText事件来控制字体、颜色、大小、偏移量等"] +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "scrollText", "text": "'+EvalString_0+'"'+Bool_0+', "time" :'+Int_0+'},\n'; +return code; +*/; + setText_s : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' EvalString? BGNL? '标题颜色' EvalString? '正文颜色' EvalString? '背景色' EvalString? '粗体' B_1_List BGNL? '标题字体大小' EvalString? '正文字体大小' EvalString? '打字间隔' EvalString? Newline @@ -1906,8 +1920,12 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['autoText_s'].xmlText([ '','','',data.time||0,this.EvalString(data.text),this.next]); break; + case "scrollText": + this.next = MotaActionBlocks['scrollText_s'].xmlText([ + data.time, data.async||false, this.EvalString(data.text), this.next]); + break; case "comment": // 注释 - this.next = MotaActionBlocks['comment_s'].xmlText([data.text,this.next]); + this.next = MotaActionBlocks['comment_s'].xmlText([this.EvalString(data.text),this.next]); break; case "setText": // 设置剧情文本的属性 var setTextfunc = function(a){return a?JSON.stringify(a).slice(1,-1):null;} diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index c16c0a12..864fb7fd 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -60,6 +60,7 @@ editor_blockly = function () { MotaActionBlocks['text_1_s'].xmlText(), MotaActionBlocks['comment_s'].xmlText(), MotaActionBlocks['autoText_s'].xmlText(), + MotaActionBlocks['scrollText_s'].xmlText(), MotaActionBlocks['setText_s'].xmlText(), MotaActionBlocks['showImage_0_s'].xmlText(), MotaActionBlocks['animateImage_0_s'].xmlText(), @@ -492,6 +493,7 @@ document.getElementById('blocklyDiv').onmousewheel = function(e){ 'text_0_s': 'EvalString_0', 'text_1_s': 'EvalString_2', 'autoText_s': 'EvalString_2', + 'scrollText_s': 'EvalString_0', 'comment_s': 'EvalString_0', 'choices_s': 'EvalString_0', 'function_s': 'RawEvalString_0', diff --git a/docs/event.md b/docs/event.md index 69e2214a..328c1900 100644 --- a/docs/event.md +++ b/docs/event.md @@ -316,15 +316,35 @@ time为可选项,代表该自动文本的时间。可以不指定,不指定 !> 由于用户无法跳过自动剧情文本,因此对于大段剧情文本请自行添加“是否跳过剧情”的提示,否则可能会非常不友好。 +### scrollText:滚动剧情文本 + +使用`{"type": "scrollText"}`可以使用滚动剧情文本,即将一段文字从屏幕最下方滚动到屏幕最上方。 + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "scrollText", "text": "第一排\n第二牌\n\n空行后的一排", "time": 5000, "async": true}, +] +``` + +text为正文文本内容。可以使用`${ }`来计算表达式的值,且使用`\n`手动换行。系统不会对滚动剧情文本进行自动换行。 + +time为可选项,代表总的滚动时间。默认为5000毫秒。 + +async可选,如果为true则会异步执行(即不等待当前事件执行完毕,立刻执行下一个事件)。 + +可以使用下面的[设置剧情文本的属性](event#setText:设置剧情文本的属性)来对文字颜色、文字大小、粗体、距离左边的偏移量进行设置。 + +!> 滚动剧情文本会绘制在UI层(和对话框冲突)!如果是异步处理请注意不要和对话框混用。 + ### setText:设置剧情文本的属性 使用`{"type": "setText"}`可以设置剧情文本的各项属性。 ``` js "x,y": [ // 实际执行的事件列表 - {"type": "setText", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3]}, - {"type": "setText", "position": "up", "bold": true, "titlefont": 26, "textfont": 17, "time": 70}, - "这段话将显示在上方,标题为红色,正文为黄色粗体,背景为透明度0.3的蓝色,标题26px,正文17px,70毫秒速度打字机效果", + {"type": "setText", "title": [255,0,0], "text": [255,255,0], "background": [0,0,255,0.3], "time": 70}, + {"type": "setText", "position": "up", "offset": 15, "bold": true, "titlefont": 26, "textfont": 17}, + "这段话将显示在上方(距离顶端15像素),标题为红色,正文为黄色粗体,背景为透明度0.3的蓝色,标题26px,正文17px,70毫秒速度打字机效果", {"type": "setText", "background": "winskin.png"} // 还可以一张使用WindowSkin作为皮肤。 ] ``` @@ -339,6 +359,8 @@ V2.5.2以后,background也可以为一个WindowSkin的文件名。详见[剧 position为可选项,表示设置文字显示位置。只能为up(上),center(中)和down(下)三者。 默认值: `center` +offset为可选项,如果设置则为代表距离如果显示位置是上/下的话,距离顶端/底端的像素值。也作为滚动剧情文本时距离左边的像素值。 + bold为可选项,如果设置则为true或false,表示正文是否使用粗体。 默认值:`false` titlefont为可选项,表示标题字体大小(px为单位)。默认值:`22` diff --git a/libs/control.js b/libs/control.js index d3f25f7d..f3ae58b9 100644 --- a/libs/control.js +++ b/libs/control.js @@ -2686,7 +2686,7 @@ control.prototype.pauseBgm = function () { core.musicStatus.isPlaying = false; } catch (e) { - console.log("无法暂停BGM "+bgm); + console.log("无法暂停BGM"); console.log(e); } } @@ -2714,7 +2714,7 @@ control.prototype.resumeBgm = function () { } } catch (e) { - console.log("无法恢复BGM "+bgm); + console.log("无法恢复BGM"); console.log(e); } } @@ -3310,7 +3310,8 @@ control.prototype.resize = function(clientWidth, clientHeight) { core.dom.statusCanvas.width = 129; core.dom.statusCanvas.height = 416; } - this.updateStatusBar(); + if (core.isPlaying()) + core.updateStatusBar(); } ////// 渲染DOM ////// diff --git a/libs/events.js b/libs/events.js index 048e3118..e8166ec8 100644 --- a/libs/events.js +++ b/libs/events.js @@ -422,6 +422,23 @@ events.prototype.doAction = function() { }, data.time || 3000); } break; + case "scrollText": // 滚动剧情文本 + if (core.status.replay.replaying) + core.events.doAction(); + else { + var content = core.replaceText(data.text); + var time = data.time || 5000; + if (data.async) { + core.ui.drawScrollText(content, time); + core.events.doAction(); + } + else { + core.ui.drawScrollText(content, time, function() { + core.events.doAction(); + }); + } + } + break; case "comment": this.doAction(); break; diff --git a/libs/ui.js b/libs/ui.js index 27ba3e00..a620dfb6 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -721,6 +721,62 @@ ui.prototype.drawTextBox = function(content, showAll) { } +////// 绘制滚动字幕 ////// +ui.prototype.drawScrollText = function (content, time, callback) { + + content = content || ""; + time = time || 5000; + + clearInterval(core.status.event.interval); + core.status.event.interval = null; + + // 获得颜色的盒子等信息 + var textAttribute = core.status.textAttribute || core.initStatus.textAttribute; + var textfont = textAttribute.textfont || 16; + var offset = textAttribute.offset || 15; + var textColor = core.arrayToRGBA(textAttribute.text); + + var font = textfont+"px "+core.status.globalAttribute.font; + if (textAttribute.bold) font = "bold "+font; + var contents = core.splitLines('ui', content), lines = contents.length; + + // 计算总高度,按1.2倍行距计算 + var width = 416, height = textfont * 1.4 * lines; + var tempCanvas = core.bigmap.tempCanvas; + tempCanvas.canvas.width = width; + tempCanvas.canvas.height = height; + tempCanvas.clearRect(0, 0, width, height); + tempCanvas.font = font; + tempCanvas.fillStyle = textColor; + + // 全部绘制 + var currH = textfont; + for (var i = 0; i < lines; ++i) { + var text = contents[i]; + tempCanvas.fillText(text, offset, currH); + currH += 1.4 * textfont; + } + + // 开始绘制到UI上 + core.clearMap('ui'); + var per_pixel = 1, per_time = time * per_pixel / (416+height); + var currH = 416; + core.canvas.ui.drawImage(tempCanvas.canvas, 0, currH); + var animate = setInterval(function () { + core.clearMap('ui'); + currH -= per_pixel; + if (currH < -height) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (core.isset(callback)) callback(); + return; + } + core.canvas.ui.drawImage(tempCanvas.canvas, 0, currH); + }, per_time); + + core.animateFrame.asyncId[animate] = true; +} + ////// 绘制一个选项界面 ////// ui.prototype.drawChoices = function(content, choices) {