diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index b3ec9d71..2467d19c 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -801,7 +801,7 @@ setBgFgBlock_s /* setBgFgBlock_s tooltip : setBgFgBlock:设置某个图层块,忽略坐标楼层则为当前点 helpUrl : https://h5mota.com/games/template/docs/#/event?id=setblock%EF%BC%9A%E8%AE%BE%E7%BD%AE%E6%9F%90%E4%B8%AA%E5%9B%BE%E5%9D%97 -colour : this.eventColor +colour : this.dataColor default : ["bg",0,"","",""] var floorstr = ''; if (PosString_0 && PosString_1) { diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index f5dc2315..020fab43 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -93,6 +93,7 @@ editor_blockly = function () { MotaActionBlocks['useItem_s'].xmlText(), MotaActionBlocks['openShop_s'].xmlText(), MotaActionBlocks['setBlock_s'].xmlText(), + MotaActionBlocks['setBgFgBlock_s'].xmlText(), MotaActionBlocks['setHeroIcon_s'].xmlText(), MotaActionBlocks['follow_s'].xmlText(), MotaActionBlocks['unfollow_s'].xmlText(), @@ -115,7 +116,6 @@ editor_blockly = function () { MotaActionBlocks['hideFloorImg_s'].xmlText(), MotaActionBlocks['showBgFgMap_s'].xmlText(), MotaActionBlocks['hideBgFgMap_s'].xmlText(), - MotaActionBlocks['setBgFgBlock_s'].xmlText(), MotaActionBlocks['trigger_s'].xmlText(), MotaActionBlocks['insert_1_s'].xmlText(), MotaActionBlocks['insert_2_s'].xmlText(), diff --git a/docs/personalization.md b/docs/personalization.md index cf2709a0..d59331bb 100644 --- a/docs/personalization.md +++ b/docs/personalization.md @@ -84,13 +84,13 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); 由于HTML5功能(素材)有限,导致了对很多比较复杂的素材(比如房子内)等无法有着较好的绘图方式。 -为了解决这个问题,我们允许用户自己放置一张或多张图片作为某一层的背景素材。 +为了解决这个问题,我们允许用户自己放置一张或多张图片作为某一层的背景/前景素材。 要启用这个功能,我们首先需要在`data.js`中将可能的图片进行加载。 ``` js "images": [ // 在此存放所有可能使用的图片 - // 图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 + // 图片可以被作为背景/前景图,也可以直接用自定义事件进行显示。 // 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 // 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 "bg.jpg", "house.png", "bed.png"// 依次向后添加 @@ -99,18 +99,21 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); !> 请使用网上的一些[在线图片压缩工具](http://compresspng.com/zh/)对图片进行压缩,以节省流量。 -之后,我们可以在每层剧本的`"images"`里来定义该层的默认背景图片素材。 +之后,我们可以在每层剧本的`"images"`里来定义该层的默认背景/前景层的图片素材。 + +从V2.5.4开始,贴图也允许进行帧动画,只要设置第五项的数值。 ``` js -"images": [[3,5,"bg.jpg",0]], // 背景图;你可以选择一张或多张图片来作为背景/前景素材。 +"images": [[96,120,"bg.jpg",0]], // 背景图;你可以选择一张或多张图片来作为背景/前景素材。 "images": [], // 无任何背景图 -"images": [[1,1,"house.png",0], [6,7,"bed.png",1]] // 在(1,1)放一个house.png在背景层,且(6,7)放bed.png在前景层 -"images": [[3,5,"tree.png",2]] // 如果写2,则会自动调节遮挡效果 +"images": [[32,32,"house.png",0], [160,170,"bed.png",1]] // 在(32,32)放一个house.png在背景层,且(160,170)放bed.png在前景层 +"images": [[96,120,"tree.png",2]] // 如果写2,则会自动调节遮挡效果 +"images": [[64,0,"x.png",1,4]] // 这是一个前景层的4帧动画贴图 ``` -images为一个数组,代表当前层所有作为背景素材的图片信息。 +images为一个数组,代表当前层所有作为背景素材的图片信息。每一项为一个五元组,分别为该背景素材的x,y,图片名,遮挡方式和帧数。 -每一项为一个四元组,分别为该背景素材的x,y,图片名和遮挡方式。其中x和y分别为横纵坐标,在0-12之间;图片名则必须在上面的images中定义过。 +其中x和y分别为左上角的像素坐标;图片名则必须在全塔属性的images中定义过。 第四项为遮挡方式,定义如下: @@ -118,17 +121,31 @@ images为一个数组,代表当前层所有作为背景素材的图片信息 - 1:该图片将全部画在前景层,可以遮挡勇士。举例:云彩等效果。 - 2:该图片将上部分画在前景层,下部分画在背景层。从而可以达到一个“自动调节遮挡的效果”。举例:树、房子等等。 -!> 如果写2的话,请确保图片高度是32的倍数! +!> 如果写2的话,最好让x,y和图片高度都是32的倍数! + +第五项为图片的帧数,可选。如果进行了设置,则会将该贴图视为帧动画,并切分成对应的帧数。 + +例如,假设图片是100x100的,且帧数设为4,则视为四帧帧动画,每次绘制的图片大小实际上是25x100。 + +关于楼层贴图和前景、背景层的层叠覆盖关系,默认是:**地板 - 背景贴图 - 背景图块 - 事件 - 勇士 - 前景贴图 - 前景图块**。 + +可以通过修改`libs/maps.js`的`drawMap`函数中下面三行的顺序来改变其覆盖关系。 + +``` js +// ----- 可以调整这三行的顺序来修改覆盖关系;同层画布上,后绘制的覆盖先绘制的 +// ----- ui.js的drawThumbnail函数也需要对应进行修改。 + +// 绘制楼层贴图 +core.maps.drawFloorImages(floorId, images); +// 绘制背景层图块 +core.maps.drawBgFgMap(floorId, core.canvas.bg, "bg", true); +// 绘制前景层图块 +core.maps.drawBgFgMap(floorId, core.canvas.fg, "fg", true); +``` 楼层贴图可以被事件隐藏和显示,详见[隐藏贴图](event#hideFloorImg:隐藏贴图)的写法。 -**如果你需要让某些点不可通行(比如你建了个房子,墙壁和家具等位置不让通行),则需在`events`中指定`{"noPass": false}`,参见[自定义事件](event#自定义事件)的写法。** - -``` js -"events": { - "x,y": {"noPass": true} // (x,y)点不可通行 -} -``` +**如果要让贴图的某些点不可通行,则可以使用noPass或者空气墙。** ### 使用便捷PS工具生成素材 diff --git a/libs/control.js b/libs/control.js index fb49cf22..109de0e3 100644 --- a/libs/control.js +++ b/libs/control.js @@ -70,6 +70,9 @@ control.prototype.setRequestAnimationFrame = function () { core.drawBlock(block, core.status.globalAnimateStatus % (block.event.animate||1)); }); + // Global floor images + core.maps.drawFloorImages(core.status.floorId, core.status.floorAnimateObjs||[], core.status.globalAnimateStatus); + // Global Autotile Animate core.status.autotileAnimateObjs.blocks.forEach(function (block) { // ------ 界面外的动画不绘制 diff --git a/libs/core.js b/libs/core.js index 47eb16c9..99119bc3 100644 --- a/libs/core.js +++ b/libs/core.js @@ -197,6 +197,7 @@ function core() { // 动画 'globalAnimateObjs': [], + 'floorAnimateObjs': [], 'boxAnimateObjs': [], 'autotileAnimateObjs': {"blocks": [], "map": null, "bgmap": null, "fgmap": null}, "globalAnimateStatus": 0, diff --git a/libs/maps.js b/libs/maps.js index 325821d3..3d43610b 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -481,6 +481,61 @@ maps.prototype.generateGroundPattern = function (floorId) { // core.material.groundPattern = '#000000'; } +maps.prototype.drawFloorImages = function (floorId, images, animate) { + var redraw = core.isset(animate); + if (!redraw) { + core.status.floorAnimateObjs = core.clone(images); + } + + animate = animate || 0; + images.forEach(function (t) { + if (typeof t == 'string') t = [0,0,t]; + var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2], frame = core.clamp(parseInt(t[4]), 1, 8); + if (redraw && frame == 1) return; // 不重绘 + + if (core.isset(dx) && core.isset(dy) && + !core.hasFlag("floorimg_"+floorId+"_"+dx+"_"+dy) && + core.isset(core.material.images.images[p])) { + var image = core.material.images.images[p]; + var width = parseInt(image.width / frame), height = image.height; + var offsetX = animate%frame*width; + + if (!t[3]) { + if (/.*\.gif/i.test(p) && main.mode=='play') { + + if (redraw) return; // 忽略gif + + core.dom.gif.innerHTML = ""; + var gif = new Image(); + gif.src = image.src; + gif.style.position = 'absolute'; + gif.style.left = (dx*core.domStyle.scale)+"px"; + gif.style.top = (dy*core.domStyle.scale)+"px"; + gif.style.width = image.width*core.domStyle.scale+"px"; + gif.style.height = image.height*core.domStyle.scale+"px"; + core.dom.gif.appendChild(gif); + } + else { + if (redraw) core.clearMap('bg', dx, dy, width, height); + core.drawImage('bg', image, offsetX, 0, width, height, dx, dy, width, height); + } + } + else if (t[3]==1) { + if (redraw) core.clearMap('fg', dx, dy, width, height); + core.drawImage('fg', image, offsetX, 0, width, height, dx, dy, width, height); + } + else if (t[3]==2) { + if (redraw) { + core.clearMap('bg', dx, dy + height - 32, width, 32); + core.clearMap('fg', dx, dy, width, height-32); + } + core.drawImage('bg', image, offsetX, height-32, width, 32, dx, dy + height - 32, width, 32); + core.drawImage('fg', image, offsetX, 0, width, height-32, dx, dy, width, height-32); + } + } + }); +} + ////// 绘制某张地图 ////// maps.prototype.drawMap = function (floorId, callback) { floorId = floorId || core.status.floorId; @@ -511,41 +566,15 @@ maps.prototype.drawMap = function (floorId, callback) { images = [[0, 0, images]]; } } - images.forEach(function (t) { - if (typeof t == 'string') t = [0,0,t]; - var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2]; - if (core.isset(dx) && core.isset(dy) && - !core.hasFlag("floorimg_"+floorId+"_"+dx+"_"+dy) && - core.isset(core.material.images.images[p])) { - var image = core.material.images.images[p]; - if (!t[3]) { - if (/.*\.gif/i.test(p) && main.mode=='play') { - core.dom.gif.innerHTML = ""; - var gif = new Image(); - gif.src = image.src; - gif.style.position = 'absolute'; - gif.style.left = (32*dx*core.domStyle.scale)+"px"; - gif.style.top = (32*dy*core.domStyle.scale)+"px"; - gif.style.width = image.width*core.domStyle.scale+"px"; - gif.style.height = image.height*core.domStyle.scale+"px"; - core.dom.gif.appendChild(gif); - } - else { - core.drawImage('bg', image, 32*dx, 32*dy, image.width, image.height); - } - } - else if (t[3]==1) - core.drawImage('fg', image, 32*dx, 32*dy, image.width, image.height); - else if (t[3]==2) { - core.drawImage('fg', image, 0, 0, image.width, image.height-32, - 32*dx, 32*dy, image.width, image.height-32); - core.drawImage('bg', image, 0, image.height-32, image.width, 32, - 32*dx, 32*dy + image.height - 32, image.width, 32); - } - } - }); + // ----- 可以调整这三行的顺序来修改覆盖关系;同层画布上,后绘制的覆盖先绘制的 + // ----- ui.js的drawThumbnail函数也需要对应进行修改。 + + // 绘制楼层贴图 + core.maps.drawFloorImages(floorId, images); + // 绘制背景层图块 core.maps.drawBgFgMap(floorId, core.canvas.bg, "bg", true); + // 绘制前景层图块 core.maps.drawBgFgMap(floorId, core.canvas.fg, "fg", true); } @@ -1352,6 +1381,7 @@ maps.prototype.removeGlobalAnimate = function (x, y, all, name) { core.status.globalAnimateStatus = 0; core.status.globalAnimateObjs = []; core.status.autotileAnimateObjs = {"blocks": [], "map": null, "bgmap": null, "fgmap": null}; + core.status.floorAnimateObjs = []; return; } diff --git a/libs/ui.js b/libs/ui.js index a589431e..d2392b9d 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -2423,6 +2423,7 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, cente tempCanvas.canvas.height = tempHeight; tempCanvas.clearRect(0, 0, tempWidth, tempHeight); + // -------- 1. 绘制地板 var groundId = (core.status.maps||core.floors)[floorId].defaultGround || "ground"; var blockIcon = core.material.icons.terrains[groundId]; for (var i = 0; i < mw; i++) { @@ -2431,7 +2432,6 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, cente } } - // background image var images = []; if (core.isset((core.status.maps||core.floors)[floorId].images)) { images = (core.status.maps||core.floors)[floorId].images; @@ -2439,25 +2439,27 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, cente images = [[0, 0, images]]; } } + + // -------- 2. 绘制背景贴图 images.forEach(function (t) { if (typeof t == 'string') t = [0,0,t]; - var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2]; + var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2], frame = core.clamp(parseInt(t[4]), 1, 8); if (core.isset(dx) && core.isset(dy) && !core.hasFlag("floorimg_"+floorId+"_"+dx+"_"+dy) && core.isset(core.material.images.images[p])) { var image = core.material.images.images[p]; + var width = image.width / frame, height = image.height; if (!t[3]) - tempCanvas.drawImage(image, 32 * dx, 32 * dy, image.width, image.height); + tempCanvas.drawImage(image, 0, 0, width, height, dx, dy, width, height); else if (t[3]==2) - tempCanvas.drawImage(image, 0, image.height-32, image.width, 32, - 32 * dx, 32 * dy + image.height - 32, image.width, 32); + tempCanvas.drawImage(image, 0, height-32, width, 32, dx, dy + height - 32, width, 32); } }) - // background map + // -------- 3. 绘制背景图块 core.maps.drawBgFgMap(floorId, tempCanvas, "bg"); - // draw block + // -------- 4. 绘制事件层 var mapArray = core.maps.getMapArray(blocks,mw,mh); for (var b in blocks) { var block = blocks[b]; @@ -2484,7 +2486,7 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, cente } } } - // draw hero + // -------- 5. 绘制勇士 if (core.isset(heroLoc)) { if (!core.isset(core.material.images.images[heroIcon])) heroIcon = "hero.png"; @@ -2493,25 +2495,25 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, cente tempCanvas.drawImage(core.material.images.images[heroIcon], icon.stop * 32, icon.loc * height, 32, height, 32*heroLoc.x, 32*heroLoc.y+32-height, 32, height); } - // draw fg + // -------- 6. 绘制前景贴图 images.forEach(function (t) { - var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2]; + var dx=parseInt(t[0]), dy=parseInt(t[1]), p=t[2], frame = core.clamp(parseInt(t[3]), 1, 8); if (core.isset(dx) && core.isset(dy) && !core.hasFlag("floorimg_"+floorId+"_"+dx+"_"+dy) && core.isset(core.material.images.images[p])) { var image = core.material.images.images[p]; + var width = image.width / frame, height = image.height; if (t[3]==1) - tempCanvas.drawImage(image, 32*dx, 32*dy, image.width, image.height); + tempCanvas.drawImage(image, 0, 0, width, height, dx, dy, width, height); else if (t[3]==2) - tempCanvas.drawImage(image, 0, 0, image.width, image.height-32, - 32*dx, 32*dy, image.width, image.height-32); + tempCanvas.drawImage(image, 0, 0, width, height-32, dx, dy, width, height-32); } }) - // foreground map + // -------- 7. 绘制前景图块 core.maps.drawBgFgMap(floorId, tempCanvas, "fg"); - // draw damage + // -------- 8. 绘制显伤 if (core.status.event.id=='viewMaps' && (core.status.event.data||{}).damage) core.control.updateDamage(floorId, tempCanvas); diff --git a/project/data.js b/project/data.js index fd064f6c..5880029d 100644 --- a/project/data.js +++ b/project/data.js @@ -354,7 +354,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "purify": 3, "hatred": 2, "moveSpeed": 100, - "animateSpeed": 300, + "animateSpeed": 400, "floorChangeTime": 800 }, "flags": { diff --git a/project/floors/sample1.js b/project/floors/sample1.js index 33a60ed3..e73c63fe 100644 --- a/project/floors/sample1.js +++ b/project/floors/sample1.js @@ -11,7 +11,7 @@ main.floors.sample1= 0, 0, "bg.jpg", - false + 0 ] ], "weather": [