diff --git a/_docs/api.md b/_docs/api.md index cdb7332a..75f925c3 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1013,12 +1013,13 @@ lose: fn(reason?: string) moveEnemyOnPoint: fn(fromX: number, fromY: number, toX: number, toY: number, floorId?: string) 将某个点已经设置的敌人属性移动到其他点 -moveImage: fn(code: number, to?: [number], opacityVal?: number, time?: number, callback?: fn()) +moveImage: fn(code: number, to?: [number], opacityVal?: number, moveMode?: string, time?: number, callback?: fn()) 移动一张图片并/或改变其透明度 例如:core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明 code: 图片编号 to: 新的左上角坐标,省略表示原地改变透明度 opacityVal: 新的透明度,省略表示不变 +moveMode: 移动模式 time: 移动用时,单位为毫秒。不填视为1秒 callback: 图片移动完毕后的回调函数,可选 @@ -2012,6 +2013,9 @@ textImage: fn(content: string, lineHeight?: number) -> image 工具函数库,里面有各个样板中使用到的工具函数。 ```text +applyEasing: fn(mode?: string) -> fn(t: number) -> number +获得变速移动曲线 + arrayToRGB: fn(color: [number]) -> string 颜色数组转字符串 例如:core.arrayToRGB([102, 204, 255]); // "#66ccff" diff --git a/_docs/instruction.md b/_docs/instruction.md index d002cd5c..a19e1659 100644 --- a/_docs/instruction.md +++ b/_docs/instruction.md @@ -119,7 +119,7 @@ data为整个指令对象,x和y为当前点坐标,prefix为独立开关的 * 此指令对应`core.showImage()`函数,编号真正的意义,详见[个性化](personalization) 2. **清除图片:**如题,需要指定要清除的图片编号和淡出时间。 * 此指令对应`core.hideImage(code, time, callback)` -3. **图片移动:**其实还包括了透明度渐变,“终点像素位置”指移动结束后的图片在视野中的左上角像素坐标(不填则表示单纯的透明度渐变),“不透明度”指渐变结束后的新的不透明度(不填表示单纯的移动),移动和透明度渐变都是匀速直线进行的。对应`core.moveImage(code, to, opacityVal, time, callback)` +3. **图片移动:**其实还包括了透明度渐变,“终点像素位置”指移动结束后的图片在视野中的左上角像素坐标(不填则表示单纯的透明度渐变),“不透明度”指渐变结束后的新的不透明度(不填表示单纯的移动)。对应`core.moveImage(code, to, opacityVal, moveMode, time, callback)` 4. **显示或清除动图:**需要填写动图的文件名(带.gif后缀),“起点像素位置”含义如前且必须填写,可以双击指令块来预览第一帧的效果。 * 动图不支持淡入淡出和伸缩移动,如果不填任何参数则清除所有动图。 * 该指令对应`core.showGif(name, x, y)`函数。 @@ -462,7 +462,7 @@ core.insertAction({"type": "changeFloor", "floorId": "MT" + core.rand2(20)}) 3. **设置和移动视角:**设置视角支持双击从地图选点,不填坐标则重置视角。 * 移动视角的动画时间为每动一格的时间,步伐数组只支持“上下左右”。 * 请注意,勇士重绘时(`core.drawHero()`函数)视角也会随之重置。所以视角变化后勇士的坐标、朝向、显隐、行走图(事件和API都提供了不重绘参数)和跟随者情况暂时都不要动。 - * 实际调用`core.setViewport(x, y)`和`core.moveViewport(steps, time, callback)`,其中前者的自变量为【像素坐标】且不必为32的倍数,必要时可作为原生js使用来实现特殊的演出。 + * 实际调用`core.setViewport(x, y)`和`core.moveViewport(x, y, moveMode, time, callback)`,其中前者的自变量为【像素坐标】且不必为32的倍数,必要时可作为原生js使用来实现特殊的演出。 4. **显隐状态栏:**如题,如果隐藏状态栏期间勇士需要恢复行动,则建议不隐藏竖屏工具栏以方便手机玩家。 * 实际调用`core.showStatusBar()`和`core.hideStatusBar(showToolbox)` 5. **显隐勇士:**如题,动画时间为淡入淡出时间,异步勾选框用法如前。 diff --git a/_server/CodeMirror/defs.js b/_server/CodeMirror/defs.js index faabcec7..201ebd6e 100644 --- a/_server/CodeMirror/defs.js +++ b/_server/CodeMirror/defs.js @@ -2530,7 +2530,7 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ }, "moveViewport": { "!doc": "移动视野范围", - "!type": "fn(x: number, y: number, time?: number, callback?: fn())" + "!type": "fn(x: number, y: number, moveMode?: string, time?: number, callback?: fn())" }, "syncLoad": { "!doc": "从服务器加载存档", @@ -2740,6 +2740,10 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ "y": "number" } }, + "applyEasing": { + "!doc": "获得变速移动曲线", + "!type": "fn(mode?: string) -> fn(t: number) -> number" + }, "clamp": { "!doc": "将x限定在[a,b]区间内,注意a和b可交换
例如:core.clamp(1200, 1, 1000); // 1000
x: 原始值,!x为true时x一律视为0
a: 下限值,大于b将导致与b交换
b: 上限值,小于a将导致与a交换", "!type": "fn(x: number, a: number, b: number) -> number" @@ -3802,7 +3806,7 @@ var terndefs_f6783a0a_522d_417e_8407_94c67b692e50 = [ }, "moveImage": { "!doc": "移动一张图片并/或改变其透明度
例如:core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明
code: 图片编号
to: 新的左上角坐标,省略表示原地改变透明度
opacityVal: 新的透明度,省略表示不变
time: 移动用时,单位为毫秒。不填视为1秒
callback: 图片移动完毕后的回调函数,可选", - "!type": "fn(code: number, to?: [number], opacityVal?: number, time?: number, callback?: fn())" + "!type": "fn(code: number, to?: [number], opacityVal?: number, moveMode?: string, time?: number, callback?: fn())" }, "openSettings": { "!doc": "点击设置按钮时的操作", diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 index 8e1c52f2..61e0ff03 100644 --- a/_server/MotaAction.g4 +++ b/_server/MotaAction.g4 @@ -2053,13 +2053,13 @@ return code; */; setViewport_s - : '设置视角' '左上角坐标' 'x' PosString? ',' 'y' PosString? '动画时间' Int '不等待执行完毕' Bool Newline + : '设置视角' '左上角坐标' 'x' PosString? ',' 'y' PosString? '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline /* setViewport_s tooltip : setViewport: 设置视角 helpUrl : /_docs/#/instruction -default : ["","",0,false] +default : ["","","",0,false] selectPoint : ["PosString_0", "PosString_1"] colour : this.soundColor var loc = ''; @@ -2068,18 +2068,19 @@ if (PosString_0 && PosString_1) { } Int_0 = Int_0 ?(', "time": '+Int_0):''; Bool_0 = Bool_0?', "async": true':''; -var code = '{"type": "setViewport"'+loc+Int_0+Bool_0+'},\n'; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var code = '{"type": "setViewport"'+loc+MoveMode_List_0+Int_0+Bool_0+'},\n'; return code; */; setViewport_1_s - : '设置视角' '增量坐标' 'dx' PosString? ',' 'dy' PosString? '动画时间' Int '不等待执行完毕' Bool Newline + : '设置视角' '增量坐标' 'dx' PosString? ',' 'dy' PosString? '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline /* setViewport_1_s tooltip : setViewport: 设置视角 helpUrl : /_docs/#/instruction -default : ["0","0",0,false] +default : ["0","0","",0,false] colour : this.soundColor var loc = ''; if (PosString_0 && PosString_1) { @@ -2087,7 +2088,8 @@ if (PosString_0 && PosString_1) { } Int_0 = Int_0 ?(', "time": '+Int_0):''; Bool_0 = Bool_0?', "async": true':''; -var code = '{"type": "setViewport"'+loc+Int_0+Bool_0+'},\n'; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var code = '{"type": "setViewport"'+loc+MoveMode_List_0+Int_0+Bool_0+'},\n'; return code; */; @@ -2182,19 +2184,20 @@ return code; moveImage_s : '图片移动' '图片编号' NInt '终点像素位置' 'x' PosString? 'y' PosString? BGNL? - '不透明度' EvalString? '移动时间' Int '不等待执行完毕' Bool Newline + '不透明度' EvalString? '移动方式' MoveMode_List '移动时间' Int '不等待执行完毕' Bool Newline /* moveImage_s tooltip : moveImage:图片移动 helpUrl : /_docs/#/instruction -default : [1,'','','',500,false] +default : [1,'','','','',500,false] var toloc = ''; if (PosString_0 && PosString_1) toloc = ', "to": ['+PosString_0+','+PosString_1+']'; EvalString_0 = (EvalString_0!=='') ? (', "opacity": '+EvalString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; var async = Bool_0?', "async": true':''; -var code = '{"type": "moveImage", "code": '+NInt_0+toloc+EvalString_0+',"time": '+Int_0+async+'},\n'; +var code = '{"type": "moveImage", "code": '+NInt_0+toloc+MoveMode_List_0+EvalString_0+',"time": '+Int_0+async+'},\n'; return code; */; @@ -3962,6 +3965,10 @@ Move_List : '上'|'下'|'左'|'右'|'前'|'后'|'左上'|'左下'|'右上'|'右下'|'设置速度' /*Move_List ['up','down','left','right','forward','backward','leftup','leftdown','rightup','rightdown','speed']*/; +MoveMode_List + : '匀速移动'|'缓入快出'|'快入缓出'|'缓入缓出' + /*MoveMode_List ['', 'easeIn', 'easeOut', 'easeInOut']*/; + NameMap_List : '确定'|'取消'|'操作失败'|'光标移动'|'打开界面'|'读档'|'存档'|'获得道具'|'回血'|'炸弹'|'飞行器'|'开关门'|'上下楼'|'跳跃'|'破墙镐'|'阻激夹域'|'穿脱装备' /*NameMap_List ['确定','取消','操作失败','光标移动','打开界面','读档','存档','获得道具','回血','炸弹','飞行器','开关门','上下楼','跳跃','破墙镐','阻激夹域','穿脱装备']*/; diff --git a/_server/MotaActionParser.js b/_server/MotaActionParser.js index 392353c5..7fbe855f 100644 --- a/_server/MotaActionParser.js +++ b/_server/MotaActionParser.js @@ -516,11 +516,11 @@ ActionParser.prototype.parseAction = function() { case "setViewport": // 设置视角 if (data.dxy) { this.next = MotaActionBlocks['setViewport_1_s'].xmlText([ - data.dxy[0],data.dxy[1],data.time||0,data.async||false,this.next]); + data.dxy[0],data.dxy[1],data.moveMode||'', data.time||0,data.async||false,this.next]); } else { data.loc = data.loc||['','']; this.next = MotaActionBlocks['setViewport_s'].xmlText([ - data.loc[0],data.loc[1],data.time||0,data.async||false,this.next]); + data.loc[0],data.loc[1],data.moveMode||'', data.time||0,data.async||false,this.next]); } break; case "vibrate": // 画面震动 @@ -551,7 +551,7 @@ ActionParser.prototype.parseAction = function() { case "moveImage": // 移动图片 data.to=data.to||['',''] this.next = MotaActionBlocks['moveImage_s'].xmlText([ - data.code, data.to[0], data.to[1], data.opacity, data.time||0, data.async||false, this.next]); + data.code, data.to[0], data.to[1], data.opacity, data.moveMode||'', data.time||0, data.async||false, this.next]); break; case "showGif": // 显示动图 data.loc=data.loc||['',''] diff --git a/libs/control.js b/libs/control.js index f064cd1b..f80eb677 100644 --- a/libs/control.js +++ b/libs/control.js @@ -968,23 +968,25 @@ control.prototype.setViewport = function (px, py) { } ////// 移动视野范围 ////// -control.prototype.moveViewport = function (x, y, time, callback) { +control.prototype.moveViewport = function (x, y, moveMode, time, callback) { time = time || 0; time /= Math.max(core.status.replay.speed, 1) - var per_time = 10, step = parseInt(time / per_time); - if (step <= 0) { + var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) { this.setViewport(32 * x, 32 * y); if (callback) callback(); return; - } + } var px = core.clamp(32 * x, 0, 32 * core.bigmap.width - core.__PIXELS__); var py = core.clamp(32 * y, 0, 32 * core.bigmap.height - core.__PIXELS__); - var dx = (px - core.bigmap.offsetX) / step, dy = (py - core.bigmap.offsetY) / step; + var cx = core.bigmap.offsetX; + var cy = core.bigmap.offsetY; + var moveFunc = core.applyEasing(moveMode); var animate=window.setInterval(function() { - core.setViewport(core.bigmap.offsetX + dx, core.bigmap.offsetY + dy); - step--; - if (step <= 0) { + step++; + core.setViewport(cx + moveFunc(step / steps) * (px - cx), cy + moveFunc(step / steps) * (py - cy)); + if (step == steps) { delete core.animateFrame.asyncId[animate]; clearInterval(animate); core.setViewport(px, py); diff --git a/libs/events.js b/libs/events.js index 2cd5cfc3..8b2952c3 100644 --- a/libs/events.js +++ b/libs/events.js @@ -1449,7 +1449,7 @@ events.prototype._action_setViewport = function (data, x, y, prefix) { data.loc = this.__action_getLoc(data.loc, x, y, prefix); } console.log(data.loc); - this.__action_doAsyncFunc(data.async, core.moveViewport, data.loc[0], data.loc[1], data.time); + this.__action_doAsyncFunc(data.async, core.moveViewport, data.loc[0], data.loc[1], data.moveMode, data.time); } events.prototype._action_move = function (data, x, y, prefix) { @@ -1567,7 +1567,7 @@ events.prototype._action_showGif = function (data, x, y, prefix) { events.prototype._action_moveImage = function (data, x, y, prefix) { if (this.__action_checkReplaying()) return; - this.__action_doAsyncFunc(data.async, core.moveImage, data.code, data.to, data.opacity, data.time); + this.__action_doAsyncFunc(data.async, core.moveImage, data.code, data.to, data.opacity, data.moveMode, data.time); } events.prototype._precompile_moveImage = function (data) { @@ -2988,7 +2988,7 @@ events.prototype.showImage = function (code, image, sloc, loc, opacityVal, time, return; } core.setOpacity(name, 0); - this.moveImage(code, null, opacityVal, time, callback); + this.moveImage(code, null, opacityVal, null, time, callback); } ////// 隐藏图片 ////// @@ -3000,14 +3000,14 @@ events.prototype.hideImage = function (code, time, callback) { if (callback) callback(); return; } - this.moveImage(code, null, 0, time, function () { + this.moveImage(code, null, 0, null, time, function () { core.deleteCanvas(name); if (callback) callback(); }); } ////// 移动图片 ////// -events.prototype.moveImage = function (code, to, opacityVal, time, callback) { +events.prototype.moveImage = function (code, to, opacityVal, moveMode, time, callback) { time = time || 1000; to = to || []; var name = "image" + (code + 100); @@ -3028,21 +3028,22 @@ events.prototype.moveImage = function (code, to, opacityVal, time, callback) { this._moveImage_moving(name, { fromX: fromX, fromY: fromY, toX: toX, toY: toY, opacity: opacity, toOpacity: toOpacity, - time: time / Math.max(core.status.replay.speed, 1) + moveMode: moveMode, time: time / Math.max(core.status.replay.speed, 1) }, callback) } events.prototype._moveImage_moving = function (name, moveInfo, callback) { - var per_time = 10, step = 0, steps = parseInt(moveInfo.time / 10); + var per_time = 10, step = 0, steps = parseInt(moveInfo.time / per_time); if (steps <= 0) steps = 1; var fromX = moveInfo.fromX, fromY = moveInfo.fromY, toX = moveInfo.toX, toY = moveInfo.toY, opacity = moveInfo.opacity, toOpacity = moveInfo.toOpacity; var currX = fromX, currY = fromY, currOpacity = opacity; + var moveFunc = core.applyEasing(moveInfo.moveMode); var animate = setInterval(function () { step++; currOpacity = opacity + (toOpacity - opacity) * step / steps; - currX = parseInt(fromX + (toX - fromX) * step / steps); - currY = parseInt(fromY + (toY - fromY) * step / steps); + currX = parseInt(fromX + (toX - fromX) * moveFunc(step / steps)); + currY = parseInt(fromY + (toY - fromY) * moveFunc(step / steps)); core.setOpacity(name, currOpacity); core.relocateCanvas(name, currX, currY); if (step == steps) { diff --git a/libs/utils.js b/libs/utils.js index bc70f8bd..671fcadf 100644 --- a/libs/utils.js +++ b/libs/utils.js @@ -480,6 +480,27 @@ utils.prototype.formatBigNumber = function (x, onMap) { return c + x; } +////// 变速移动 ////// +utils.prototype.applyEasing = function(name) { + var list = { + "easeIn": function(t) { + return Math.pow(t, 3); + }, + "easeOut": function(t) { + return 1 - Math.pow(1 - t, 3); + }, + "easeInOut": function(t) { + // easeInOut试了一下感觉二次方效果明显点 + if (t < 0.5) return Math.pow(t, 2) * 2; + else return 1 - Math.pow(1 - t, 2) * 2; + }, + "linear": function(t) { + return t + } + } + return list[name] || list.linear; +} + ////// 数组转RGB ////// utils.prototype.arrayToRGB = function (color) { if (!(color instanceof Array)) return color; diff --git a/runtime.d.ts b/runtime.d.ts index e0a6c239..941f0eec 100644 --- a/runtime.d.ts +++ b/runtime.d.ts @@ -646,7 +646,7 @@ declare class control { setViewport(px?: number, py?: number): void /** 移动视野范围 */ - moveViewport(x: number, y: number, time?: number, callback?: () => any): void + moveViewport(x: number, y: number, moveMode?: string, time?: number, callback?: () => any): void /** 更新跟随者坐标 */ updateFollowers(): void @@ -976,14 +976,15 @@ declare class events { /** * 移动一张图片并/或改变其透明度 - * @example core.moveImage(1, undefined, 0.5); // 1秒内把1号图片变为50%透明 + * @example core.moveImage(1, null, 0.5); // 1秒内把1号图片变为50%透明 * @param code 图片编号 * @param to 新的左上角坐标,省略表示原地改变透明度 * @param opacityVal 新的透明度,省略表示不变 + * @param moveMode 移动模式 * @param time 移动用时,单位为毫秒。不填视为1秒 * @param callback 图片移动完毕后的回调函数,可选 */ - moveImage(code: number, to?: [number?, number?], opacityVal?: number, time?: number, callback?: () => void): void + moveImage(code: number, to?: [number?, number?], opacityVal?: number, moveMode?: string, time?: number, callback?: () => void): void /** * 绘制一张动图或擦除所有动图 @@ -2381,6 +2382,9 @@ declare class utils { */ formatBigNumber(x: number, onMap?: boolean): string + /** 变速移动 */ + applyEasing(mode?: string): (number) => number; + /** * 颜色数组转十六进制 * @example core.arrayToRGB([102, 204, 255]); // "#66ccff",加载画面的宣传色