From 15bc4d1cef8cbd13282f6631eeb71ad00ff34004 Mon Sep 17 00:00:00 2001 From: oc Date: Thu, 4 Apr 2019 13:00:01 +0800 Subject: [PATCH] script & api docs --- _docs/_sidebar.md | 3 +- _docs/api.md | 278 ++-------------------------------- _docs/element.md | 2 +- _docs/event.md | 4 +- _docs/img/plugin.jpg | Bin 0 -> 32911 bytes _docs/personalization.md | 6 +- _docs/script.md | 313 +++++++++++++++++++++++++++++++++++++++ _docs/start.md | 2 +- libs/ui.js | 4 +- 9 files changed, 336 insertions(+), 276 deletions(-) create mode 100644 _docs/img/plugin.jpg create mode 100644 _docs/script.md diff --git a/_docs/_sidebar.md b/_docs/_sidebar.md index f7c5a0a1..914ee883 100644 --- a/_docs/_sidebar.md +++ b/_docs/_sidebar.md @@ -3,4 +3,5 @@ - [元件说明](element) - [事件](event) - [个性化](personalization) -- [脚本](api) +- [脚本](script) +- [附录:API列表](api) diff --git a/_docs/api.md b/_docs/api.md index 4783aaaa..346f4bd6 100644 --- a/_docs/api.md +++ b/_docs/api.md @@ -1,268 +1,14 @@ -# 脚本 +# 附录:API列表 ?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * -在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。 - -## 控制台的使用 - -在Chrome浏览器中,按(Ctrl+Shift+I)可打开控制台。 - -![](img/console.jpg) - -控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。 - -有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。 - -### Console:命令行 - -Console页为命令行。可以在这里输入一些命令进行调试。 - -比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。 - -更多的API可参见[附录:API列表](#附录:API列表)。 - -除此以外,游戏中的报错等信息也是可以在Console中进行查看的。 - -![](img/console1.jpg) - -### Sources:断点调试 - -Sources页可以查看JS源代码,并进行断点调试等。 - -例如,如果相对脚本编辑中的伤害计算函数进行断点调试: -1. 在左边找到`project/functions.js`,单击打开文件 -2. 并找到对应的行(可以Ctrl+F搜索),比如搜索`getDamageInfo` -3. 在行号上点一下打断点,会出现一个蓝色标签 - -之后,当代码运行到你的断点处时,将自动停止运行。 - -![](img/sources.jpg) - -可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。 - -图中红色框内有几个按钮,从左到右分别是:**继续执行**,**执行到下一行**,**进入当前函数**,**跳出当前函数**,**单步执行**。 - -通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。 - -红圈下方是Call Stack,即当前的函数调用链(从哪些地方调用过来的)。 - -Sources还有更多有趣的功能,在此不做介绍,有兴趣的可自行网上搜索了解。 - -### Elements:网页元素查看 - -Elements页可以查看网页的源代码,调整css布局等。 - -![](img/elements.jpg) - -不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。 - -手机模式下,左边可以对屏幕分辨率进行调整和模拟。 - -这可以很有效的帮我们进行测试样板在手机端的表现。 - -## 整体项目架构 - -``` text -├── /_server/ # 为可视化地图编辑器提供一些支持的目录 -├── /libs/ # ---- 系统库目录 ---- -│ ├─ /thirdparty/ # 游戏所用到的第三方库文件 -│ ├─ actions.js # 用户交互处理 -│ ├─ core.js # 系统核心文件(游戏入口,接口&转发) -│ ├─ control.js # 游戏逻辑控制 -│ ├─ data.js # 全塔属性等 -│ ├─ enemys.js # 怪物相关处理 -│ ├─ events.js # 各个事件的执行 -│ ├─ icons.js # 图标和素材 -│ ├─ items.js # 道具效果 -│ ├─ loader.js # 各个资源加载 -│ ├─ maps.js # 地图数据和绘制 -│ ├─ ui.js # UI窗口绘制 -│ └─ utils.js # 工具类函数 -├── /project/ # ---- 项目目录 ---- -│ ├─ /animates/ # 动画目录 -│ ├─ /floors/ # 楼层文件 -│ ├─ /images/ # 图片素材 -│ ├─ /sounds/ # bgm和音效 -│ ├─ data.js # 全塔属性 -│ ├─ enemys.js # 怪物属性 -│ ├─ events.js # 公共事件 -│ ├─ functions.js # 脚本编辑 -│ ├─ icons.js # 素材和ID的对应关系定义 -│ ├─ items.js # 道具的定义和效果 -│ ├─ maps.js # 地图和数字的对应关系 -│ └─ plugins.js # 自定义插件 -├── /常用工具/ # 辅助造塔的小工具 -├── editor.html # 地图编辑器 -├── editor-mobile.html # 手机版的地图编辑器 -├── index.html # 主程序,游戏的入口 -├── main.js # JS程序的入口,将动态对所需JS进行加载 -├── style.css # 游戏所需要用到的样式表 -└── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 -``` - -`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 - -`libs`为**系统库目录**,里面存放了各个系统核心函数。 - -从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 - -`project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 - -## 函数的转发 - -在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。 - -例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的: - -```js -////// 获得某个自定义变量或flag ////// -control.prototype.getFlag = function(name, defaultValue) { - if (!core.status.hero) return defaultValue; - var value = core.status.hero.flags[name]; - return value != null ? value : defaultValue; -} -``` - -也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。 - -但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`,而不需要中间的control。 - -为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数,转发到core中执行**。 - -上述`getFlag`代码的转发实际上是增加了如下函数: - -```js -////// getFlag函数的转发 ////// -core.getFlag = function (name, defaultValue) { - return core.control.getFlag(name, defaultValue); -} -// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag() -``` - -转发是自动完成的,其满足如下两条规则: -- **在libs中其他文件定义的函数,如果不以下划线`_`开头,就会进行转发。** -- **如果core中已经存在同名函数,则会在控制台中打出一条报错信息,并不转发该函数。** - -具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。 - -!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发! - -例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。 - -## 复写函数 - -样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 - -在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: -- 不容易随着新样板来接档迁移 -- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 -- …… - -好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 - -如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` - -下面是几个例子,从简单到复杂。 - -### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 - -直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 - -```js -// 重写ui.js中的_drawBook_drawBackground函数 -core.ui._drawBook_drawBackground = function () { - // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 - core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); -} -``` - -### 重写点击楼传事件 - -重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 - -```js -// 重写events.js的useFly函数,即点击楼传按钮时的事件 -core.events.useFly = function (fromUserAction) { - if (core.isMoving()) { - core.drawTip("请先停止勇士行动"); - return; - } - if (core.status.lockControl || core.status.event.id != null) return; - - if (core.canUseItem('fly')) core.useItem('fly'); - else core.drawTip("当前无法使用"+core.material.items.fly.name); -} -``` - -其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 - -### 楼层切换时根据flag来播放不同的音效 - -整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。 - -```js -// 复制重写events.js中的_changeFloor_beforeChange,修改音效 -core.events._changeFloor_beforeChange = function (info, callback) { - // 直接替换原始函数中的 core.playSound('floor.mp3'); - if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); - if (core.getFlag("floorSound") == 1) core.playSound('floor1.mp3'); - if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); - // ... - - // 下面是原始函数中的剩余代码,保持不变 - window.setTimeout(function () { - if (info.time == 0) - core.events._changeFloor_changing(info, callback); - else - core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { - core.events._changeFloor_changing(info, callback); - }); - }, 25) -} -``` - -### 每次打开全局商店时播放一个音效 - -打开全局商店是在`events.js`中的`openShop`函数,因此需要对其进行重写。 - -然而,我们只需要在这个函数执行之前插一句音效播放,所以并不需要重写整个函数,而是直接插入一行就行。 - -```js -var openShop = core.events.openShop; // 先把原始函数用一个变量记录下来 -core.events.openShop = function (shopId, needVisited) { - core.playSound("shop.mp3"); // 播放一个音效 - return openShop(shopId, needVisited); // 直接调用原始函数 -} -``` - -### 每次绘制地图前在控制台打出一条信息 - -绘制地图在`maps.js`的`drawMap`函数,因此需要对其进行重写。 - -由于只需要额外在函数执行前增加一句控制台输出,所以直接插入一行即可。 - -但是需要注意的是,`drawMap`中使用了`this._drawMap_drawAll()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。 - -```js -var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来 -core.maps.drawMap = function (floorId, callback) { - console.log("drawMap..."); // 控制台打出一条信息 - drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps -} -``` - -详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 - -## 附录:API列表 - 这里将列出所有被转发到core的API,没有被转发的函数此处不会列出,请自行在代码中查看。 本附录量较大,如有什么需求请自行Ctrl+F进行搜索。 如有任何疑问,请联系小艾寻求帮助。 -### core.js +## core.js core.js中只有很少的几个函数,主要是游戏开始前的初始化等。 @@ -421,7 +167,7 @@ core.doFunc(func, _this) 此函数剩余参数将作为参数被传入func。 ``` -### actions.js +## actions.js actions.js主要是处理一些和用户交互相关的内容。 @@ -513,7 +259,7 @@ core.longClick() 如果全部返回false则将停止本次长按行为,直到手指离开屏幕并重新进行长按为止。 ``` -### control.js +## control.js control.js将负责整个游戏的核心控制系统,分为如下几个部分: - requestAnimationFrame相关 @@ -947,7 +693,7 @@ core.resize() 此函数将根据当前的屏幕分辨率信息,生成一个obj,并传入各个注册好的resize函数中执行。 ``` -### enemys.js +## enemys.js enemys.js中定义了一系列和怪物相关的API函数。 @@ -1033,7 +779,7 @@ core.hasEnemyLeft(floorId) 检查某个楼层是否还有剩余的怪物。等价于 core.getCurrentEnemys(floorId).length > 0 ``` -### events.js +## events.js events.js将处理所有和事件相关的操作,主要分为五个部分: - 游戏的开始和结束 @@ -1370,7 +1116,7 @@ core.afterUseBomb() 使用炸弹或圣锤后的事件。实际被转发到了脚本编辑中。 ``` -### icons.js +## icons.js icons.js主要是负责素材相关信息,比如某个素材在对应的图片上的位置。 @@ -1384,7 +1130,7 @@ core.getTilesetOffset(id) 如果该素材不是tileset,则返回null。 ``` -### items.js +## items.js items.js主要负责一切和道具相关的内容。 @@ -1474,7 +1220,7 @@ core.quickLoadEquip() 读取当前套装。index为读取的套装编号。 ``` -### loader.js +## loader.js loader.js主要负责资源加载相关的内容。 @@ -1509,7 +1255,7 @@ core.freeBgm(name) 释放一个bgm的内存并移出缓存列表。如果该bgm正在播放则也会立刻停止。 ``` -### map.js +## map.js maps.js负责一切和地图相关的处理内容,包括如下几个方面: - 地图的初始化,保存和读取,地图数组的生成 @@ -1842,7 +1588,7 @@ core.stopAnimate(id, doCallback) 如果doCallback为真,则会执行该动画所对应的回调函数。 ``` -### ui.js +## ui.js ui.js负责一切UI界面的绘制。主要包括三个部分: - 设置某个画布的属性的相关API @@ -2104,7 +1850,7 @@ core.ui.deleteAllCanvas() 删除所有的自定义画布。 ``` -### utils.js +## utils.js utils.js是一个工具函数库,里面有各个样板中使用到的工具函数。 diff --git a/_docs/element.md b/_docs/element.md index 6f4551d3..4fed9b92 100644 --- a/_docs/element.md +++ b/_docs/element.md @@ -85,7 +85,7 @@ percentage为该装备是否按比例增加属性。 使用`core.getEquip(equipType)`来获得某个装备类型的当前装备。 -更多相关API详见[附录:API列表](api#附录:API列表)。 +更多相关API详见[附录:API列表](api)。 ### 多重装备 diff --git a/_docs/event.md b/_docs/event.md index fbd0b094..9fd851a4 100644 --- a/_docs/event.md +++ b/_docs/event.md @@ -1746,11 +1746,11 @@ icon是可选的,如果设置则会在选项前绘制图标,其可以是一 `{"type":"function"}`需要有一个`"function"`参数,它是一个JS函数,里面可以写任何自定义的JS脚本;系统将会执行它。 -系统常见可能会被造塔所用到的的API都在[API列表](api#附录:API列表)中给出,请进行参照。 +系统常见可能会被造塔所用到的的API都在[API列表](api)中给出,请进行参照。 **警告:自定义脚本中只能执行同步代码,不可执行任何异步代码,比如直接调用core.changeFloor(...)之类都是不行的。** -[API列表](api#附录:API列表)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 +[API列表](api)中的所有异步API都进行了标记;如果你不确定一个函数是同步的还是异步的,请向小艾咨询。 如果需要异步的代码都需要用事件(insertAction)来执行,这样事件处理过程和录像回放才不会出错。 diff --git a/_docs/img/plugin.jpg b/_docs/img/plugin.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d0d69befdce6b52593064bf9ac7cbca2206a66c8 GIT binary patch literal 32911 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 literal 0 HcmV?d00001 diff --git a/_docs/personalization.md b/_docs/personalization.md index 904b6e45..3341f2dd 100644 --- a/_docs/personalization.md +++ b/_docs/personalization.md @@ -51,7 +51,7 @@ core.fillText('test', '这是一段文字', 10, 30, '#FF0000', '16px Verdana'); `core.deleteAllCanvas()`可以删除所有动态创建的画布,`core.relocateCanvas(name, x, y)`和`core.resizeCanvas(name, x, y)`可以对画布的位置和大小进行改变。 -更多详细API请参见[API列表](api#附录:API列表)。 +更多详细API请参见[API列表](api)。 ## 自定义素材 @@ -339,7 +339,7 @@ function (enemy, hero_hp, hero_atk, hero_def, hero_mdef, x, y, floorId) { 要修改楼传事件,需要进行如下两步: -1. 重写楼传的点击事件。在插件中对`core.control.useFly进行重写`。详细代码参见[重写点击楼传事件](api#重写点击楼传事件)。 +1. 重写楼传的点击事件。在插件中对`core.control.useFly进行重写`。详细代码参见[重写点击楼传事件](script#重写点击楼传事件)。 2. 修改楼传的使用事件。和其他永久道具一样,在地图编辑器的图块属性中修改楼传的useItemEffect和canUseItemEffect两个内容。例如: ``` js "useItemEffect": "core.insertAction([...])" // 执行某段自定义事件,或者其他脚本 @@ -429,7 +429,7 @@ this.myfunc = function(x) { 从V2.6开始,在插件中用`this.xxx`定义的函数将会被转发到core中。例如上述的`myfunc`除了`core.plugin.myfunc`外也可以直接`core.myfunc`调用。 -详见[函数的转发](api#函数的转发)。 +详见[函数的转发](script#函数的转发)。 ## 标题界面事件化 diff --git a/_docs/script.md b/_docs/script.md new file mode 100644 index 00000000..817a68a6 --- /dev/null +++ b/_docs/script.md @@ -0,0 +1,313 @@ +# 脚本 + +?> 目前版本**v2.6**,上次更新时间:* {docsify-updated} * + +在V2.6版本中,基本对整个项目代码进行了重写,更加方便造塔者的使用和复写函数。 + +## 控制台的使用 + +在Chrome浏览器中,按(Ctrl+Shift+I)可打开控制台。 + +![](img/console.jpg) + +控制台中有很多的标签,最常用的是`Console`, `Sources`和`Elements`。 + +有关更详尽的控制台使用可自行搜索[Chrome开发者工具](https://www.baidu.com/s?wd=chrome%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7)了解更多。 + +### Console:命令行 + +Console页为命令行。可以在这里输入一些命令进行调试。 + +比如,进入游戏后,输入`core.status.hero.atk`即可获得勇士的当前攻击力数值。`core.status.hero.atk=100`可以设置攻击力为100。 + +更多的API可参见[附录:API列表](#附录:API列表)。 + +除此以外,游戏中的报错等信息也是可以在Console中进行查看的。 + +![](img/console1.jpg) + +### Sources:断点调试 + +Sources页可以查看JS源代码,并进行断点调试等。 + +例如,如果相对脚本编辑中的伤害计算函数进行断点调试: +1. 在左边找到`project/functions.js`,单击打开文件 +2. 并找到对应的行(可以Ctrl+F搜索),比如搜索`getDamageInfo` +3. 在行号上点一下打断点,会出现一个蓝色标签 + +之后,当代码运行到你的断点处时,将自动停止运行。 + +![](img/sources.jpg) + +可以将鼠标移动到变量上,将弹窗形式显示这个变量的各项数值,从而查看变量值是否符合预期。 + +图中红色框内有几个按钮,从左到右分别是:**继续执行**,**执行到下一行**,**进入当前函数**,**跳出当前函数**,**单步执行**。 + +通过这几个按钮,可以一行一行的对代码进行执行,执行过程中能不断查看各个变量的数值变化,从而定位问题所在。 + +红圈下方是Call Stack,即当前的函数调用链(从哪些地方调用过来的)。 + +Sources还有更多有趣的功能,在此不做介绍,有兴趣的可自行网上搜索了解。 + +### Elements:网页元素查看 + +Elements页可以查看网页的源代码,调整css布局等。 + +![](img/elements.jpg) + +不过对魔塔样板来说,最重要的是红圈中的按钮。点击此按钮可以进入**手机模式**。 + +手机模式下,左边可以对屏幕分辨率进行调整和模拟。 + +这可以很有效的帮我们进行测试样板在手机端的表现。 + +## 整体项目架构 + +``` text +├── /_server/ # 为可视化地图编辑器提供一些支持的目录 +├── /libs/ # ---- 系统库目录 ---- +│ ├─ /thirdparty/ # 游戏所用到的第三方库文件 +│ ├─ actions.js # 用户交互处理 +│ ├─ core.js # 系统核心文件(游戏入口,接口&转发) +│ ├─ control.js # 游戏逻辑控制 +│ ├─ data.js # 全塔属性等 +│ ├─ enemys.js # 怪物相关处理 +│ ├─ events.js # 各个事件的执行 +│ ├─ icons.js # 图标和素材 +│ ├─ items.js # 道具效果 +│ ├─ loader.js # 各个资源加载 +│ ├─ maps.js # 地图数据和绘制 +│ ├─ ui.js # UI窗口绘制 +│ └─ utils.js # 工具类函数 +├── /project/ # ---- 项目目录 ---- +│ ├─ /animates/ # 动画目录 +│ ├─ /floors/ # 楼层文件 +│ ├─ /images/ # 图片素材 +│ ├─ /sounds/ # bgm和音效 +│ ├─ data.js # 全塔属性 +│ ├─ enemys.js # 怪物属性 +│ ├─ events.js # 公共事件 +│ ├─ functions.js # 脚本编辑 +│ ├─ icons.js # 素材和ID的对应关系定义 +│ ├─ items.js # 道具的定义和效果 +│ ├─ maps.js # 地图和数字的对应关系 +│ └─ plugins.js # 自定义插件 +├── /常用工具/ # 辅助造塔的小工具 +├── editor.html # 地图编辑器 +├── editor-mobile.html # 手机版的地图编辑器 +├── index.html # 主程序,游戏的入口 +├── main.js # JS程序的入口,将动态对所需JS进行加载 +├── style.css # 游戏所需要用到的样式表 +└── 启动服务.exe # 一个本地的HTTP服务器,通过它来运行游戏 +``` + +`_server`为**地图编辑器目录**,里面存放了地图编辑器相关的各项内容。 + +`libs`为**系统库目录**,里面存放了各个系统核心函数。 + +从V2.6开始,请勿直接修改libs下的代码,如有需要修改系统库函数请尝试在插件中[复写函数](#复写函数)。 + +`project`为**项目目录**,你所造的塔的数据全部存放在project下。在不同样板之间接档也是直接迁移project目录即可。 + +## 函数的转发 + +在本样板中,`core.js`里面基本是没有定义什么函数的,所有的游戏内函数都在其他几个文件中实现。 + +例如,常见的获得某个变量值`getFlag`是定义在`control.js`中的: + +```js +////// 获得某个自定义变量或flag ////// +control.prototype.getFlag = function(name, defaultValue) { + if (!core.status.hero) return defaultValue; + var value = core.status.hero.flags[name]; + return value != null ? value : defaultValue; +} +``` + +也就是,我们可以通过`core.control.getFlag(name, value)`来调用此函数。 + +但是这样会十分不便,我们希望能直接调用`core.getFlag(name, value)`,而不需要中间的control。 + +为了达到这个目的,样板设置了**函数转发**,即**将其他文件中定义的函数,转发到core中执行**。 + +上述`getFlag`代码的转发实际上是增加了如下函数: + +```js +////// getFlag函数的转发 ////// +core.getFlag = function (name, defaultValue) { + return core.control.getFlag(name, defaultValue); +} +// 转发后,即可通过 core.getFlag() 来实际调用 core.control.getFlag() +``` + +转发是自动完成的,其满足如下两条规则: +- **在libs中其他文件定义的函数,如果不以下划线`_`开头,就会进行转发。** +- **如果core中已经存在同名函数,则会在控制台中打出一条报错信息,并不转发该函数。** + +具体函数的转发实现代码可参见`core.js`的`_forwardFunc`函数。 + +!> 除此以外,插件中以`this.xxx`来定义的函数也会被转发! + +例如,你可以直接调用`core.drawLight()`来实际调用插件中的`core.plugin.drawLight`。 + +## 插件编写 + +插件编写是H5魔塔的一个重大特点,从V2.0.1引入,并逐渐发扬光大。 + +对于有一定脚本经验的人来说,可以编写插件来实现各种各样的功能,包括且不仅限于拓展功能的实现,系统代码的复写等等。 + +在V2.5.5以前,插件位置都在脚本编辑中;从V2.6开始则迁移到了新的下拉框中,并进行了切分。 + +你也可以创建自己的插件。 + +![](img/plugin.jpg) + +新的插件切分和原来的单插件使用方法完全一致,单纯进行了切分而已。可参见已有的`init`和`drawLight`的样例。 + +拆分的意义主要是将各个可能的功能独立出来,避免单个框内内容太长,过大和混杂等。 + +在V2.6中,应当每个独立的额外功能实现都新建一个自己的插件,这样也方便进行拓展,例如打包迁移到别的塔上,或发布在网页插件库中。 + +另外一点需要注意的是,所有插件的初始化都会在系统资源加载之前,此时图片等资源尚未进行加载。 + +在所有资源加载完毕时,将会执行init插件中的_afterLoadResources函数,可以在这里对资源进行一些操作,比如切分图片等。 + +```js +function () { + console.log("插件编写测试"); + + // 可以写一些直接执行的代码 + // 在这里写的代码将会在【资源加载前】被执行,此时图片等资源尚未被加载。 + // 请勿在这里对包括bgm,图片等资源进行操作。 + + + this._afterLoadResources = function () { + // 本函数将在所有资源加载完毕后,游戏开启前被执行 + // 可以在这个函数里面对资源进行一些操作,比如切分图片等。 + + // 这是一个将assets.png拆分成若干个32x32像素的小图片并保存的样例。 + // var arr = core.splitImage("assets.png", 32, 32); + // for (var i = 0; i < arr.length; i++) { + // core.material.images.images["asset"+i+".png"] = arr[i]; + // } + + } + + // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); + // 从V2.6开始,插件中用this.XXX方式定义的函数也会被转发到core中,详见文档-脚本-函数的转发。 +} +``` + +网站上提供了一个插件库,[https://h5mota.com/plugins/](https://h5mota.com/plugins/),上面有一些大家分享的插件,可供使用。 + +可以查看附录中的[API列表](api)来查看所有的系统API内容。 + +## 复写函数 + +样板的功能毕竟是写死的,有时候我们也需要修改样板的一些行为。 + +在V2.6以前,需要直接打开libs目录下的对应文件并进行修改。但是开libs下的文件就会出现各种问题: +- 不容易随着新样板接档进行迁移 +- 也不好找到自己改过什么,从而能整理成新的插件在别的塔使用 +- …… + +好消息的是,从V2.6开始,我们再也不需要开文件了,而是可以直接在插件中对原始函数进行复写。 + +如果我想对xxx文件中的yyy函数进行重写,其模式一般是:`core.xxx.yyy = function (参数列表) { ... }` + +下面是几个例子,从简单到复杂。 + +### 重写怪物手册的背景图绘制,使用winskin而不是默认的黑色 + +直接重写怪物手册的背景图绘制,使用`core.drawBackground`来用winskin绘制一个背景图。 + +```js +// 重写ui.js中的_drawBook_drawBackground函数 +core.ui._drawBook_drawBackground = function () { + // core.__PIXEL__为定义的一个宏,对于13x13的值是416,对于15x15的值是480 + core.drawBackground(0, 0, core.__PIXEL__, core.__PIXEL__); +} +``` + +### 重写点击楼传事件 + +重写点击楼传事件,使得点击楼传按钮时能使用一个道具(比如item:fly)。 + +```js +// 重写events.js的useFly函数,即点击楼传按钮时的事件 +core.events.useFly = function (fromUserAction) { + if (core.isMoving()) { + core.drawTip("请先停止勇士行动"); + return; + } + if (core.status.lockControl || core.status.event.id != null) return; + + if (core.canUseItem('fly')) core.useItem('fly'); + else core.drawTip("当前无法使用"+core.material.items.fly.name); +} +``` + +其他的几个按钮,如快捷商店`openQuickShop`,虚拟键盘`openKeyBoard`的重写也几乎完全一样。 + +### 楼层切换时根据flag来播放不同的音效 + +整体复制并重写整个楼传切换前的函数,将`core.playSound('floor.mp3')`替换成根据flag来判定。 + +```js +// 复制重写events.js中的_changeFloor_beforeChange,修改音效 +core.events._changeFloor_beforeChange = function (info, callback) { + // 直接替换原始函数中的 core.playSound('floor.mp3'); + if (core.getFlag("floorSound") == 0) core.playSound('floor0.mp3'); + if (core.getFlag("floorSound") == 1) core.playSound('floor1.mp3'); + if (core.getFlag("floorSound") == 2) core.playSound('floor2.mp3'); + // ... + + // 下面是原始函数中的剩余代码,保持不变 + window.setTimeout(function () { + if (info.time == 0) + core.events._changeFloor_changing(info, callback); + else + core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { + core.events._changeFloor_changing(info, callback); + }); + }, 25) +} +``` + +### 每次打开全局商店时播放一个音效 + +打开全局商店是在`events.js`中的`openShop`函数,因此需要对其进行重写。 + +然而,我们只需要在这个函数执行之前插一句音效播放,所以并不需要重写整个函数,而是直接插入一行就行。 + +```js +var openShop = core.events.openShop; // 先把原始函数用一个变量记录下来 +core.events.openShop = function (shopId, needVisited) { + core.playSound("shop.mp3"); // 播放一个音效 + return openShop(shopId, needVisited); // 直接调用原始函数 +} +``` + +### 每次绘制地图前在控制台打出一条信息 + +绘制地图在`maps.js`的`drawMap`函数,因此需要对其进行重写。 + +由于只需要额外在函数执行前增加一句控制台输出,所以直接插入一行即可。 + +但是需要注意的是,`drawMap`中使用了`this._drawMap_drawAll()`,因此使用函数时需要用`call`或者`apply`来告知this是什么。 + +```js +var drawMap = core.maps.drawMap; // 先把原始函数用一个变量记录下来 +core.maps.drawMap = function (floorId, callback) { + console.log("drawMap..."); // 控制台打出一条信息 + drawMap.call(core.maps, floorId, callback); // 需要使用`call`来告知this是core.maps +} +``` + +详见[call和apply的用法](https://www.jianshu.com/p/80ea0d1c04f8)。 + +========================================================================================== + +[继续阅读下一章:API列表](api) + + diff --git a/_docs/start.md b/_docs/start.md index f5b4fa12..004ea21f 100644 --- a/_docs/start.md +++ b/_docs/start.md @@ -231,7 +231,7 @@ HTML5的塔都是可以进行控制台调试的。 - `core.resetMap()` 重置当前层地图。 - …… -更多API和详细参数介绍可参见[API列表](api#附录:API列表)。 +更多API和详细参数介绍可参见[API列表](api)。 ## 编辑器的基本操作 diff --git a/libs/ui.js b/libs/ui.js index 6c4501e2..54dbccc1 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -542,7 +542,7 @@ ui.prototype._calTextBoxWidth = function (ctx, content, min_width, max_width, fo // 如果不存在手动换行,尽量调成半行形式 if (allLines.length == 1) { - var w = core.calWidth(ctx, allLines[0]) + 5; + var w = core.calWidth(ctx, allLines[0]) + 10; if (w