From 3aac956f7600bc0244eb232d5af3366298e610c9 Mon Sep 17 00:00:00 2001 From: BaoErjie <408537296@qq.com> Date: Tue, 9 Sep 2025 00:04:26 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=A0=A1=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh/faq/faq.md | 75 +- docs/zh/manuals/2dgraphics.md | 8 +- docs/zh/manuals/3dgraphics.md | 8 +- .../adapting-graphics-to-screen-size.md | 40 +- docs/zh/manuals/addressing.md | 120 +- docs/zh/manuals/ads.md | 54 +- docs/zh/manuals/android.md | 61 +- docs/zh/manuals/animation.md | 18 +- docs/zh/manuals/app-manifest.md | 79 +- docs/zh/manuals/application-lifecycle.md | 76 +- docs/zh/manuals/application-security.md | 95 +- docs/zh/manuals/atlas.md | 290 ++-- docs/zh/manuals/bob.md | 62 +- docs/zh/manuals/buffer.md | 12 +- docs/zh/manuals/building-blocks.md | 72 +- docs/zh/manuals/bundling.md | 74 +- docs/zh/manuals/caching-assets.md | 32 +- docs/zh/manuals/camera.md | 249 ++- docs/zh/manuals/collection-factory.md | 62 +- docs/zh/manuals/collection-proxy.md | 127 +- docs/zh/manuals/components.md | 93 +- docs/zh/manuals/compute.md | 234 +++ .../manuals/debugging-game-and-system-logs.md | 61 +- docs/zh/manuals/debugging-game-logic.md | 96 +- .../manuals/debugging-native-code-android.md | 50 +- docs/zh/manuals/debugging-native-code-ios.md | 123 +- docs/zh/manuals/debugging-native-code.md | 122 +- docs/zh/manuals/debugging.md | 8 +- docs/zh/manuals/design.md | 34 +- docs/zh/manuals/dev-app.md | 70 +- docs/zh/manuals/editor-preferences.md | 82 +- docs/zh/manuals/editor-scripts-ui.md | 370 +++++ docs/zh/manuals/editor-scripts.md | 489 +++++- docs/zh/manuals/editor-templates.md | 26 +- docs/zh/manuals/editor.md | 81 +- docs/zh/manuals/extender-docker-images.md | 46 + docs/zh/manuals/extender-local-setup.md | 133 ++ docs/zh/manuals/extension-facebook.md | 130 +- docs/zh/manuals/extension-fbinstant.md | 42 +- .../zh/manuals/extension-googleplayinstant.md | 58 +- docs/zh/manuals/extension-gpgs.md | 10 +- docs/zh/manuals/extension-iap.md | 113 +- docs/zh/manuals/extension-push.md | 68 +- docs/zh/manuals/extension-websocket.md | 4 +- docs/zh/manuals/extension-webview.md | 144 +- docs/zh/manuals/extensions-best-practices.md | 95 +- docs/zh/manuals/extensions-build-variants.md | 26 +- docs/zh/manuals/extensions-cocoapods.md | 26 + .../manuals/extensions-debugging-android.md | 4 +- docs/zh/manuals/extensions-debugging-ios.md | 4 +- docs/zh/manuals/extensions-debugging.md | 4 +- docs/zh/manuals/extensions-defold-sdk.md | 8 +- docs/zh/manuals/extensions-details.md | 30 +- docs/zh/manuals/extensions-ext-manifests.md | 75 + docs/zh/manuals/extensions-gradle.md | 30 + .../manuals/extensions-manifest-merge-tool.md | 60 +- docs/zh/manuals/extensions-script-api.md | 22 +- docs/zh/manuals/extensions.md | 157 +- docs/zh/manuals/facebook.md | 6 +- docs/zh/manuals/factory.md | 128 +- docs/zh/manuals/file-access.md | 168 ++- docs/zh/manuals/flash.md | 76 +- docs/zh/manuals/flipbook-animation.md | 56 +- docs/zh/manuals/font.md | 223 ++- docs/zh/manuals/getting-help.md | 102 +- docs/zh/manuals/glossary.md | 94 +- docs/zh/manuals/gpgs.md | 4 +- docs/zh/manuals/graphics.md | 12 +- docs/zh/manuals/gui-box.md | 14 +- docs/zh/manuals/gui-clipping.md | 64 +- docs/zh/manuals/gui-layouts.md | 58 +- docs/zh/manuals/gui-particlefx.md | 20 +- docs/zh/manuals/gui-pie.md | 30 +- docs/zh/manuals/gui-script.md | 46 +- docs/zh/manuals/gui-spine.md | 6 +- docs/zh/manuals/gui-template.md | 32 +- docs/zh/manuals/gui-text.md | 38 +- docs/zh/manuals/gui.md | 201 ++- docs/zh/manuals/hot-reload.md | 54 +- docs/zh/manuals/html5.md | 197 +-- docs/zh/manuals/http-requests.md | 26 +- docs/zh/manuals/iac.md | 30 +- docs/zh/manuals/iap.md | 6 +- docs/zh/manuals/importing-assets.md | 34 +- docs/zh/manuals/importing-graphics.md | 56 +- docs/zh/manuals/importing-models.md | 56 +- docs/zh/manuals/input-gamepads.md | 94 +- docs/zh/manuals/input-key-and-text.md | 32 +- docs/zh/manuals/input-mouse-and-touch.md | 64 +- docs/zh/manuals/input.md | 138 +- docs/zh/manuals/install.md | 4 +- docs/zh/manuals/instant-games.md | 4 +- docs/zh/manuals/introduction.md | 32 +- docs/zh/manuals/ios.md | 196 ++- docs/zh/manuals/label.md | 77 +- docs/zh/manuals/libraries.md | 112 +- docs/zh/manuals/linux.md | 14 +- docs/zh/manuals/live-update-aws.md | 50 +- docs/zh/manuals/live-update-scripting.md | 78 +- docs/zh/manuals/live-update.md | 123 +- docs/zh/manuals/lua.md | 239 +-- docs/zh/manuals/macos.md | 56 +- docs/zh/manuals/material.md | 271 ++-- docs/zh/manuals/mesh.md | 144 +- docs/zh/manuals/message-passing.md | 111 +- docs/zh/manuals/microsoft-xbox.md | 4 +- docs/zh/manuals/model-animation.md | 61 +- docs/zh/manuals/model.md | 88 +- docs/zh/manuals/models.md | 12 +- docs/zh/manuals/modules.md | 72 +- docs/zh/manuals/networking.md | 16 +- docs/zh/manuals/nintendo-switch.md | 25 +- docs/zh/manuals/online-services.md | 31 +- docs/zh/manuals/optimization-battery.md | 18 + docs/zh/manuals/optimization-memory.md | 27 + docs/zh/manuals/optimization-size.md | 100 ++ docs/zh/manuals/optimization-speed.md | 89 ++ docs/zh/manuals/optimization.md | 182 +-- docs/zh/manuals/particlefx.md | 176 +-- docs/zh/manuals/physics-events.md | 121 +- docs/zh/manuals/physics-groups.md | 14 +- docs/zh/manuals/physics-joints.md | 45 +- docs/zh/manuals/physics-messages.md | 103 +- docs/zh/manuals/physics-objects.md | 91 +- docs/zh/manuals/physics-ray-casts.md | 17 +- .../manuals/physics-resolving-collisions.md | 55 +- docs/zh/manuals/physics-shapes.md | 95 +- docs/zh/manuals/physics.md | 40 +- docs/zh/manuals/porting-guidelines.md | 92 +- docs/zh/manuals/profiling.md | 105 +- docs/zh/manuals/project-defignore.md | 24 +- docs/zh/manuals/project-settings.md | 667 ++++---- docs/zh/manuals/project-setup.md | 36 +- docs/zh/manuals/properties.md | 154 +- docs/zh/manuals/property-animation.md | 130 +- docs/zh/manuals/push.md | 4 +- docs/zh/manuals/refactoring.md | 14 +- docs/zh/manuals/release-checklist.md | 16 +- docs/zh/manuals/render.md | 271 ++-- docs/zh/manuals/resource.md | 58 +- docs/zh/manuals/scene-editing.md | 4 +- docs/zh/manuals/script-properties.md | 66 +- docs/zh/manuals/script.md | 177 ++- docs/zh/manuals/shader.md | 386 +++-- docs/zh/manuals/socket-connections.md | 10 +- docs/zh/manuals/sony-playstation.md | 27 +- docs/zh/manuals/sound-streaming.md | 119 ++ docs/zh/manuals/sound.md | 121 +- docs/zh/manuals/spine.md | 4 +- docs/zh/manuals/spinemodel.md | 4 +- docs/zh/manuals/sprite.md | 89 +- docs/zh/manuals/test.md | 123 +- docs/zh/manuals/texture-filtering.md | 20 +- docs/zh/manuals/texture-profiles.md | 300 ++-- docs/zh/manuals/tilemap.md | 104 +- docs/zh/manuals/tilesource.md | 94 +- docs/zh/manuals/version-control.md | 28 +- docs/zh/manuals/websocket-connections.md | 4 +- docs/zh/manuals/webview.md | 5 +- docs/zh/manuals/windows.md | 34 +- docs/zh/manuals/working-offline.md | 44 +- docs/zh/manuals/writing-code.md | 62 +- docs/zh/manuals/zerobrane.md | 58 +- docs/zh/shared/android-adb.md | 14 +- docs/zh/shared/android-faq.md | 22 +- docs/zh/shared/apple-privacy-manifest.md | 6 +- docs/zh/shared/blend-modes.md | 10 +- docs/zh/shared/build-variants.md | 28 +- docs/zh/shared/bundle-resources.md | 22 + .../component-max-count-optimizations.md | 14 +- docs/zh/shared/components.md | 22 +- docs/zh/shared/consoles-faq.md | 8 +- docs/zh/shared/custom-resources.md | 5 + docs/zh/shared/editor-faq.md | 28 +- docs/zh/shared/editor-versions.md | 10 +- docs/zh/shared/graphics-api.md | 12 +- docs/zh/shared/html5-faq.md | 8 +- docs/zh/shared/install.md | 32 +- docs/zh/shared/ios-faq.md | 20 +- docs/zh/shared/linux-faq.md | 71 +- docs/zh/shared/material-constants.md | 2 +- docs/zh/shared/optimization-memory-html5.md | 21 + docs/zh/shared/platforms.md | 4 +- docs/zh/shared/slice-9-texturing.md | 38 +- docs/zh/shared/test.md | 6 +- docs/zh/shared/url-shorthands.md | 12 +- docs/zh/shared/windows-faq.md | 26 +- docs/zh/tutorials/15-puzzle.md | 394 +++++ docs/zh/tutorials/astronaut.md | 27 + docs/zh/tutorials/car.md | 438 ++++++ docs/zh/tutorials/colorslide.md | 27 + docs/zh/tutorials/getting-started.md | 35 + docs/zh/tutorials/grading.md | 461 ++++++ docs/zh/tutorials/hud.md | 164 ++ docs/zh/tutorials/level-complete.md | 226 +++ docs/zh/tutorials/magic-link.md | 1339 +++++++++++++++++ docs/zh/tutorials/main-menu.md | 92 ++ docs/zh/tutorials/movement.md | 25 + docs/zh/tutorials/parallax.md | 81 + docs/zh/tutorials/platformer.md | 406 +++++ docs/zh/tutorials/rpgmap.md | 81 + docs/zh/tutorials/runner.md | 663 ++++++++ docs/zh/tutorials/shadertoy.md | 258 ++++ docs/zh/tutorials/side-scroller.md | 26 + docs/zh/tutorials/snake.md | 481 ++++++ docs/zh/tutorials/texture-scrolling.md | 110 ++ docs/zh/tutorials/war-battles.md | 28 + 207 files changed, 13566 insertions(+), 5691 deletions(-) create mode 100644 docs/zh/manuals/compute.md create mode 100644 docs/zh/manuals/editor-scripts-ui.md create mode 100644 docs/zh/manuals/extender-docker-images.md create mode 100644 docs/zh/manuals/extender-local-setup.md create mode 100644 docs/zh/manuals/extensions-cocoapods.md create mode 100644 docs/zh/manuals/extensions-ext-manifests.md create mode 100644 docs/zh/manuals/extensions-gradle.md create mode 100644 docs/zh/manuals/optimization-battery.md create mode 100644 docs/zh/manuals/optimization-memory.md create mode 100644 docs/zh/manuals/optimization-size.md create mode 100644 docs/zh/manuals/optimization-speed.md create mode 100644 docs/zh/manuals/sound-streaming.md create mode 100644 docs/zh/shared/bundle-resources.md create mode 100644 docs/zh/shared/custom-resources.md create mode 100644 docs/zh/shared/optimization-memory-html5.md create mode 100644 docs/zh/tutorials/15-puzzle.md create mode 100644 docs/zh/tutorials/astronaut.md create mode 100644 docs/zh/tutorials/car.md create mode 100644 docs/zh/tutorials/colorslide.md create mode 100644 docs/zh/tutorials/getting-started.md create mode 100644 docs/zh/tutorials/grading.md create mode 100644 docs/zh/tutorials/hud.md create mode 100644 docs/zh/tutorials/level-complete.md create mode 100644 docs/zh/tutorials/magic-link.md create mode 100644 docs/zh/tutorials/main-menu.md create mode 100644 docs/zh/tutorials/movement.md create mode 100644 docs/zh/tutorials/parallax.md create mode 100644 docs/zh/tutorials/platformer.md create mode 100644 docs/zh/tutorials/rpgmap.md create mode 100644 docs/zh/tutorials/runner.md create mode 100644 docs/zh/tutorials/shadertoy.md create mode 100644 docs/zh/tutorials/side-scroller.md create mode 100644 docs/zh/tutorials/snake.md create mode 100644 docs/zh/tutorials/texture-scrolling.md create mode 100644 docs/zh/tutorials/war-battles.md diff --git a/docs/zh/faq/faq.md b/docs/zh/faq/faq.md index 35e788d8..c5475c00 100644 --- a/docs/zh/faq/faq.md +++ b/docs/zh/faq/faq.md @@ -42,9 +42,48 @@ brief: 有关 Defold 游戏引擎和编辑器及平台的常见问题和解答. 答: 可以! 游戏引擎纯 3D. 然而, 工具都是针对 2D 游戏设计的, 所以 3D 游戏工具你得自己开发. 提升 3D 支持的计划进行中. -#### 问: Defold 游戏开发用什么语言? +## 编程语言相关问题 -答: Defold 项目游戏逻辑基本使用 Lua 语言 (特指 Lua 5.1/LuaJIT, 详情请见 [Lua 教程](/manuals/lua)). Lua 是一种强大快速的动态语言. 同时也支持使用原生 (C/C++, Objective-C, Java 和 JavaScript等) 语言来扩展 Defold 引擎功能. 自定义材质, 使用 OpenGL ES SL 语言编写的顶点和片元着色程序. +#### 问: 在 Defold 中使用什么编程语言? + +答: Defold 项目中的游戏逻辑主要使用 Lua 语言编写(特别是 Lua 5.1/LuaJIT,详情请参考 [Lua 手册](/manuals/lua))。Lua 是一种轻量级的动态语言,快速且非常强大。从 1.8.1 版本开始,Defold 支持使用能生成 Lua 代码的转译器。安装转译器扩展后,你可以使用替代语言——如 [Teal](https://github.com/defold/extension-teal)——来编写静态类型检查的 Lua。你也可以使用原生代码(根据平台不同,可以是 C/C++、Objective-C、Java 和 JavaScript)来[为 Defold 引擎扩展新功能](/manuals/extensions/)。在构建[自定义材质](/manuals/material/)时,使用 OpenGL ES SL 着色器语言来编写顶点和片段着色器。 + + +#### 问: 我可以使用 C++ 编写游戏逻辑吗? + +答: Defold 中的 C++ 支持主要用于编写与第三方 SDK 或平台特定 API 交互的原生扩展。[dmSDK](https://defold.com/ref/stable/dmGameObject/)(用于原生扩展的 Defold C++ API)将逐步扩展更多功能,使开发者可以选择完全使用 C++ 编写所有游戏逻辑。Lua 仍将是游戏逻辑的主要语言,但通过扩展的 C++ API,也可以使用 C++ 编写游戏逻辑。扩展 C++ API 的工作主要是将现有的私有头文件移至公共部分,并清理 API 以供公共使用。 + + +#### 问: 我可以在 Defold 中使用 TypeScript 吗? + +答: TypeScript 不是官方支持的。社区维护了一个工具包 [ts-defold](https://ts-defold.dev/),用于编写 TypeScript 并直接从 VSCode 将其转译为 Lua。 + + +#### 问: 我可以在 Defold 中使用 Haxe 吗? + +答: Haxe 不是官方支持的。社区维护了 [hxdefold](https://github.com/hxdefold/hxdefold) 用于编写 Haxe 并将其转译为 Lua。 + + +#### 问: 我可以在 Defold 中使用 C# 吗? + +答: Defold 基金会将添加 C# 支持并将其作为库依赖项提供。C# 是一种广泛采用的编程语言,它将帮助那些大量投资于 C# 的工作室和开发者过渡到 Defold。 + + +#### 问: 我担心添加 C# 支持会对 Defold 产生负面影响。我应该担心吗? + +答: Defold 不会放弃将 Lua 作为主要脚本语言。C# 支持将作为扩展的新语言添加。除非你在项目中选择使用 C# 扩展,否则它不会影响引擎。 + +C# 支持是有代价的(可执行文件大小、运行时性能等),但这由个别开发者/工作室来决定。 + +至于 C# 本身,这是一个相对较小的变化,因为扩展系统已经支持多种语言(C/C++/Java/Objective-C/Zig)。SDK 将通过生成 C# 绑定来保持同步。这将使绑定以最小的努力保持最新。 + +Defold 基金会以前一直反对在 Defold 中添加 C# 支持,但由于多种原因改变了看法: + +* 工作室和开发者继续要求 C# 支持。 +* C# 支持的范围已缩小到仅扩展(即工作量小)。 +* 核心引擎不会受到影响。 +* 如果生成 C# API,可以以最小的努力保持同步。 +* C# 支持将基于带有 NativeAOT 的 DotNet 9,从而生成现有构建管道可以链接的静态库(就像任何其他 Defold 扩展一样)。 ## 平台相关 @@ -58,38 +97,38 @@ brief: 有关 Defold 游戏引擎和编辑器及平台的常见问题和解答. | macOS | 11 Big Sur | `x86-64`, `arm-64` | Editor | | macOS | 10.15 | `x86-64`, `arm-64` | Engine | | Windows | Vista | `x86-32`, `x86-64` | Editor and Engine | - | Ubuntu (1) | 18.04 | `x86-64` | Editor | + | Ubuntu (1) | 22.04 LTS | `x86-64` | Editor | | Linux (2) | Any | `x86-64`, `arm-64` | Engine | | iOS | 11.0 | `arm-64` | Engine | | Android | 4.4 (API level 19) | `arm-32`, `arm-64` | Engine | | HTML5 | | `asm.js`, `wasm` | Engine | - (1 编辑器在 64-bit Ubuntu 18.04 平台上通过编译和测试. 其他版本应该同样可以运行但是未经过测试.) + (1 编辑器在 64-bit Ubuntu 22.04 LTS 平台上通过编译和测试. 其他版本应该同样可以运行但是未经过测试.) (2 游戏引擎在大多数 64-bit Linux 版本上只要更新显卡驱动支持 OpenGL ES 2.0 的基本都能运行.) #### 问: Defold 能输出哪些平台的游戏? -答: 可以一键发布到 PS4™, 任天堂 Switch, iOS, Android 和 HTML5 游戏, 外加 macOS, Windows 和 Linux 游戏. 真正的一套代码平台通用. +答: 可以一键发布到 PS4™, PS5™, 任天堂 Switch, iOS (64位), Android (32位和64位) 和 HTML5 游戏, 外加 macOS (x86-64和arm64), Windows (32位和64位) 和 Linux (x86-64和arm64) 游戏. 真正的一套代码平台通用. #### 问: Defold 基于何种渲染引擎? -A: 作爲開發者只需要關心可編程渲染管綫所支持的一種渲染 API [完全可程式化渲染管线](/manuals/render/). Defold 渲染脚本 API 会把渲染操作转换为如下图形 API: +答: 作为开发者只需要关心可编程渲染管线所支持的一种渲染 API [完全可程式化渲染管线](/manuals/render/). Defold 渲染脚本 API 会把渲染操作转换为如下图形 API: :[图形 API](../shared/graphics-api.md) #### 问: 如何获取 Defold 版本信息? -答: 点击菜单栏 "About" 项. 弹出窗口详细列出了 Defold 版本号, 和文件哈希 SHA1. 对于游戏引擎版本, 调用 [`sys.get_engine_info()`](/ref/sys/#sys.get_engine_info) 获取. +答: 点击帮助菜单 "About" 项. 弹出窗口详细列出了 Defold 版本号, 和文件哈希 SHA1. 对于游戏引擎版本, 调用 [`sys.get_engine_info()`](/ref/sys/#sys.get_engine_info) 获取. 最新测试版位于 http://d.defold.com/beta 可以查看 http://d.defold.com/beta/info.json (正式版同样也有: http://d.defold.com/stable/info.json) 文件获取最新版本信息. #### 问: 运行时如何获取系统信息? -答: 调用 [`sys.get_sys_info()`](/ref/sys#sys.get_sys_info) 获取. +答: 调用 [`sys.get_sys_info()`](/ref/sys/#sys.get_sys_info) 获取. ## 編輯器相關 @@ -129,7 +168,7 @@ A: 作爲開發者只需要關心可編程渲染管綫所支持的一種渲染 A 2. 用广告提升安装量 3. 用广告提升点击量 - 如果选择第一个, 编辑会在你的游戏里找广告. 如果没找到, 游戏很可能会被拒. Defold 本身不使用广告商id. + 如果选择第一个, 应用审核人员会在你的游戏里找广告. 如果没找到, 游戏很可能会被拒. Defold 本身不使用广告商id. #### 问: 怎么用游戏获利? @@ -169,7 +208,7 @@ A: 作爲開發者只需要關心可編程渲染管綫所支持的一種渲染 A #### 问: 精灵变色, 透明效果如何制作? -A: 精灵默认着色程序包含 "tint" 属性: +答: 精灵默认着色程序包含 "tint" 属性: ```lua local red = 1 @@ -197,12 +236,12 @@ A: 精灵默认着色程序包含 "tint" 属性: #### 问: 只有颜色没有纹理的GUI节点如何渲染? -答: 作为顶点着色形状渲染, 注意其性能消耗. +答: 它只是一个顶点着色的形状. 请记住它仍然会消耗填充率. #### 问: 如何释放资源所占用的内存? -答: 引擎对所有资源都有引用计数. 一旦引用为0则资源自动被释放. +答: 所有资源在内部都有引用计数. 一旦引用计数为0, 资源就会被释放. #### 问: 不用游戏对象能播放声音吗? @@ -259,14 +298,16 @@ A: 精灵默认着色程序包含 "tint" 属性: #### 问: 我能在论坛上打广告吗? -答: 可以! 论坛有一个 ["Work for hire" 类目](https://forum.defold.com/c/work-for-hire). 能充实社区论坛的所有信息都欢迎, 这里可以发布求职广告. +答: 当然可以! 我们有一个专门的 ["Work for hire" 类目](https://forum.defold.com/c/work-for-hire). 我们总是鼓励任何有益于社区的事情, 向社区提供你的服务---无论是否收费---就是一个很好的例子. + +#### 问: 我发布了一个帖子并添加了我的作品,我可以添加更多吗? -#### 问: 可以无限打广告? +答: 为了减少 "Work for hire" 帖子的刷屏, 你在自己的帖子中每14天只能发布一次 (除非是对帖子中评论的直接回复, 这种情况下你可以回复). 如果你想在14天内向你的帖子添加更多作品, 你必须编辑现有帖子来添加内容. -答: 为避免 "Work for hire" 类目论坛被爆, 至少14天内不许再加广告 (除非有人回复找你, 此时你可以立即回复这个人). 14天内, 只能通过更新帖子的方式加入新内容. +#### 问: 我可以使用 "Work for hire" 类目来发布招聘信息吗? -#### 问: 我能发布招聘广告吗? +答: 当然可以, 随意使用! 它可以用于发布招聘信息, 也可以用于求职, 例如 "程序员寻找2D像素艺术家; 我很有钱, 会给你很好的报酬". -答: 可以, 自己找! 招聘也行求职也行, 比如 “程序员找美工; 高价求不差钱”. +``` diff --git a/docs/zh/manuals/2dgraphics.md b/docs/zh/manuals/2dgraphics.md index eb4ad803..8e4a5fb3 100644 --- a/docs/zh/manuals/2dgraphics.md +++ b/docs/zh/manuals/2dgraphics.md @@ -1,9 +1,9 @@ --- -title: Defold 2D 图像教程 -brief: 本教程已过时 +title: Defold 2D 图形手册 +brief: 本手册已过时 --- -# 2D Graphics +# 2D 图形 -详见 [图像教程](/manuals/graphics). +本手册已被 [图形概览手册](/manuals/graphics) 取代。 diff --git a/docs/zh/manuals/3dgraphics.md b/docs/zh/manuals/3dgraphics.md index 0a47176d..fd47b319 100644 --- a/docs/zh/manuals/3dgraphics.md +++ b/docs/zh/manuals/3dgraphics.md @@ -1,8 +1,8 @@ --- -title: Defold 3D 图像教程 -brief: 本教程已过时 +title: Defold 3D 图形手册 +brief: 本手册已过时 --- -# 3D graphics +# 3D 图形 -详见 [图像教程](/manuals/graphics). \ No newline at end of file +本手册已被 [图形概览手册](/manuals/graphics) 取代。 \ No newline at end of file diff --git a/docs/zh/manuals/adapting-graphics-to-screen-size.md b/docs/zh/manuals/adapting-graphics-to-screen-size.md index 0faa369d..645e23b9 100644 --- a/docs/zh/manuals/adapting-graphics-to-screen-size.md +++ b/docs/zh/manuals/adapting-graphics-to-screen-size.md @@ -20,12 +20,12 @@ brief: 本教程解释了如何适配游戏和图像到不同的屏幕尺寸. ## 内容渲染的方法如何改变 -Defold 渲染脚本提供整个渲染流程的控制. 渲染脚本控制着显示什么, 怎么显示以及显示的顺序. 默认渲染脚本总是显示 *game.project* 文件里定义的长, 宽的一块区域, 不管窗口缩放和屏幕大小匹配. 如果窗口大小, 比例改变, 将会造成内容的拉伸. 有些游戏可能能接受, 但通常当屏幕分辨率或比例改变, 应该适当让游戏内容显示的多些或少些, 或者至少在不改变窗口比例的条件下缩放游戏内容. 要改变内容拉伸的现象详情请见 [渲染教程](https://www.defold.com/manuals/render/#默认视口映射). +Defold 渲染脚本提供整个渲染流程的控制. 渲染脚本控制着显示什么, 怎么显示以及显示的顺序. 默认渲染脚本总是显示 *game.project* 文件里定义的长, 宽的一块区域, 不管窗口是否缩放或实际屏幕分辨率是否匹配。 如果窗口大小, 比例改变, 将会造成内容的拉伸. 有些游戏可能能接受, 但通常当屏幕分辨率或比例改变, 应该适当让游戏内容显示的多些或少些, 或者至少在不改变窗口比例的条件下缩放游戏内容. 要改变内容拉伸的现象详情请见 [渲染手册](/manuals/render/#default-view-projection)。 ## 复古/8比特图像 -复古/8比特图像游戏模拟了老式游戏机或者低分辨率低调色盘的电脑游戏. 比如任天堂红白机 (NES) 就是分辨率 256x240 的, Commodore 64 是 320x200 的, Gameboy 是 160x144 的, 这些屏幕分辨率赶不上当前屏幕的零头. 为了在当今高分辨率屏幕上模拟这种风格的游戏需要把屏幕缩放好几倍. 一个简单的方法是先显示低分辨率图像然后再在渲染时放大. 这在Defold中使用渲染脚本就能做到, [固定映射](/manuals/render/#Fixed) 可以设置一个合适的放大值. +复古/8比特图像游戏模拟了老式游戏机或者低分辨率低调色盘的电脑游戏. 比如任天堂红白机 (NES) 就是分辨率 256x240 的, Commodore 64 是 320x200 的, Gameboy 是 160x144 的, 这些屏幕分辨率赶不上当前屏幕的零头. 为了在当今高分辨率屏幕上模拟这种风格的游戏需要把屏幕缩放好几倍. 一个简单的方法是先显示低分辨率图像然后再在渲染时放大. 这在Defold中使用渲染脚本就能做到, [固定投影](/manuals/render/#fixed-projection) 可以设置一个合适的放大值。 比如使用这个瓷砖图源和角色 ([source](https://ansimuz.itch.io/grotto-escape-game-art-pack)) 来模拟一个8位 320x200 分辨率游戏: @@ -47,7 +47,13 @@ Defold 渲染脚本提供整个渲染流程的控制. 渲染脚本控制着显 msg.post("@render:", "use_fixed_projection", { zoom = 4 }) ``` -结果会变成这样: +::: sidenote +同样的结果也可以通过将[相机组件](/manuals/camera/)附加到游戏对象上,勾选*正交投影*并将*正交缩放*设置为4.0来实现: + +![](images/screen_size/retro-camera_zoom.png) +::: + +这将产生以下结果: ![](images/screen_size/retro-zoomed_1280x800.png) @@ -65,11 +71,11 @@ msg.post("@render:", "use_fixed_projection", { zoom = 4 }) ![](images/screen_size/retro-zoomed_nearest.png) -现在我们的游戏图像是像素对齐的了. 还可以想想其他办法, 比如在*game.project*的sprite里关闭 sub-pixels: +现在我们的游戏图像是像素对齐的了. 还可以想想其他办法, 比如在*game.project*的精灵(sprite)里关闭子像素(Subpixels): ![](images/screen_size/retro-subpixels.png) -当Subpixels选项关闭后所有 sprites 就不会渲染在半个像素上而是永远像素对齐. +当子像素(Subpixels)选项关闭后所有精灵(sprites)就不会渲染在半个像素上而是永远像素对齐。 ## 高分辨率图像 @@ -85,36 +91,36 @@ msg.post("@render:", "use_fixed_fit_projection") 在 *game.project* 文件中可以设置设计时屏幕宽和高. -### 高分辨率视网膜屏幕 +### 高DPI设置和视网膜屏幕 -想要支持高分辨率视网膜屏幕可以在 *game.project* 文件里打开这个选项: +如果你还希望支持高分辨率的视网膜屏幕,可以在*game.project*文件的Display部分启用此功能: ![](images/screen_size/highdpi-enabled.png) -选中这个选项就打开了高分辨率后台缓冲. 游戏会以设置好的宽高双倍比例渲染, 但是游戏分辨率不变. 也就是说游戏内容是1倍大小的就照常显示. 但是如果内容是双倍大小再在游戏里缩小为1倍的话就是高清渲染了. +这将在支持高DPI的显示器上创建一个高DPI的后台缓冲区。游戏将以设置好的宽高双倍分辨率渲染,但宽度和高度设置仍将是脚本和属性中使用的逻辑分辨率。这意味着所有测量值保持不变,任何以1x比例渲染的内容看起来都一样。但是,如果你导入高分辨率图像并将它们缩放到0.5x,它们在屏幕上将是高DPI的。 -## 创建可适配性 GUI +## 创建自适应GUI -系统的 GUI 组件由各种元素组成, 或称作 [节点](/manuals/gui/#节点类型), 虽然看起来很简单却能创造出从按钮到复杂的菜单和弹框等各种界面. GUI 可以设计成自动适配屏幕尺寸和方向的. 比如让节点保持在屏幕顶部, 底部或边上而不改变自身的大小. 也可以设置成对于不同屏幕尺寸和方向自动调整节点关系大小与形态. +创建GUI组件的系统由许多基本构建块或[节点](/manuals/gui/#节点类型)组成,虽然看起来非常简单,但可以用来创建从按钮到复杂菜单和弹出窗口的任何内容。您创建的GUI可以配置为自动适应屏幕大小和方向的变化。例如,您可以让节点锚定在屏幕的顶部、底部或侧面,节点可以保持其大小或拉伸。节点之间的关系以及它们的大小和外观也可以配置为在屏幕大小或方向变化时发生变化。 ### 节点属性 -gui中的节点包含锚点, 横轴纵轴以及一个调整模式. +GUI中的每个节点都有一个枢轴点、水平和垂直锚点以及调整模式。 -* 锚点定义了节点的中心. -* 锚点模式控制着当屏幕边界或者其父节点边界在适配物理屏幕时拉伸的话, 节点自身在水平和垂直方向位置如何修改. -* 调整模式控制着当屏幕边界或者其父节点边界在适配物理屏幕时, 节点自身该怎样做. +* 枢轴点定义节点的中心点。 +* 锚点模式控制当场景边界或父节点边界被拉伸以适应物理屏幕大小时,节点的垂直和水平位置如何改变。 +* 调整模式设置控制当场景边界或父节点边界被调整以适应物理屏幕大小时,节点会发生什么变化。 -详情请见 [GUI教程](/manuals/gui/#节点属性). +您可以在[GUI手册](/manuals/gui/#节点属性)中了解更多关于这些属性的信息。 ### 布局 -Defold支持GUI在手机上自动适配屏幕方向. 此功能让你能把GUI设计成适配各种各样屏幕比例和方向的. 也可以用来创建特定设备上的界面布局. 详情请见 [GUI 布局教程](/manuals/gui-layouts/) +Defold支持GUI在移动设备上自动适应屏幕方向变化。通过使用此功能,您可以设计一个能够适应各种屏幕尺寸和方向以及宽高比的GUI。也可以创建匹配特定设备型号的布局。您可以在[GUI布局手册](/manuals/gui-layouts/)中了解更多关于此系统的信息。 ## 测试不同的屏幕尺寸 -Debug 菜单有用来模拟特定设备分辨率或者自定义分辨率的选项. 当应用运行时你可以通过选择 Debug->Simulate Resolution 然后从列表中选择一个模拟设备. 运行中的应用会自动缩放来测试游戏运行在不同分辨率和屏幕比例的设备上的样子. +调试菜单包含一个选项,用于模拟特定设备型号分辨率或自定义分辨率。当应用程序运行时,您可以选择Debug->Simulate Resolution并从列表中选择一个设备型号。运行的应用程序窗口将调整大小,您将能够看到您的游戏在不同分辨率或不同宽高比下的外观。 ![](images/screen_size/simulate-resolution.png) diff --git a/docs/zh/manuals/addressing.md b/docs/zh/manuals/addressing.md index 4ecafe80..87a96aa4 100644 --- a/docs/zh/manuals/addressing.md +++ b/docs/zh/manuals/addressing.md @@ -1,15 +1,15 @@ --- -title: Defold 定位 -brief: 本教程解释了 Defold 如何实现地址定位功能. +title: Defold 中的寻址 +brief: 本手册解释了 Defold 如何解决寻址问题. --- -# 定位 +# 寻址 -为了让代码能够控制每个对象和组件的移动, 缩放, 播放动画或者添加删除各种视听元素, Defold 提供了地址定位机制. +控制运行游戏的代码必须能够访问每个对象和组件,以便移动、缩放、制作动画、删除和操作玩家看到和听到的内容。Defold的寻址机制使这成为可能。 -## 标志 +## 标识符 -Defold 使用地址 (称为 URL, 暂且不表) 来引用游戏对象和组件. 地址里包含各种标志. 下面列举了 Defold 使用地址的例子. 本教程将详述地址的用法: +Defold使用地址(或URL,但我们暂时忽略这一点)来引用游戏对象和组件。这些地址由标识符组成。以下是Defold如何使用地址的所有示例。在本手册中,我们将详细研究它们的工作原理: ```lua local id = factory.create("#enemy_factory") @@ -33,21 +33,21 @@ function init(self) msg.post("#body", "disable") -- <1> end ``` -1. 不知道 '#' 是什么意思没关系, 一会儿就谈到. +<1> 如果你对'#'字符感到困惑,不用担心,我们很快会讲到它。 -就像设计的那样, 游戏一开始脚本组件 *定位* 到了sprite组件 "body" 对其地址发出了一个 "disable" 的 *消息* . 这个消息的结果就是把sprite隐藏了. 也就是说, 整个流程是这样的: +这将按预期工作。当游戏启动时,脚本组件通过其标识符"body"*寻址*到精灵组件,并使用该地址向其发送一条带有"disable"的*消息*。这个特殊引擎消息的效果是精灵组件隐藏精灵图形。从示意图上看,设置如下: ![bean](images/addressing/bean.png) -id可以随意设置. 当前我们对游戏对象设置了一个id "bean", sprite组件叫做 "body", 控制这个对象的脚本组件叫做 "controller". +设置中的标识符是任意的。在这里,我们选择给游戏对象命名为"bean",其精灵组件命名为"body",而控制角色的脚本组件命名为"controller"。 ::: sidenote -如果你不手动命名, 编辑器会自动设置一个命名. 每当新建一个游戏对象或组件, 系统会将唯一 *Id* 赋值给它. +如果你不选择名称,编辑器会为你选择。每当你在编辑器中创建新的游戏对象或组件时,会自动设置一个唯一的*Id*属性。 -- 游戏对象就是go后面跟一个数字 ("go2", "go3" 以此类推). -- 组件就是组件名后面跟一个数字 ("sprite", "sprite2" 以此类推). +- 游戏对象自动获得一个名为"go"的id,后面跟着枚举器("go2"、"go3"等)。 +- 组件获得一个对应于组件类型的id("sprite"、"sprite2"等)。 -自动命名虽然能用, 但是我们鼓励你自己将命名设计的更好, 更有意义. +自动命名虽然能用,但是我们鼓励你自己将命名设计的更好,更有意义。 ::: 现在, 再增加一个sprite来给豆子先生添加一个盾牌: @@ -80,7 +80,7 @@ id可以随意设置. 当前我们对游戏对象设置了一个id "bean", sprit 回过头来看上个例子我们没有指定对象的id, 系统默认对象就是脚本所在的 *当前游戏对象*. -比如, `"#body"` 就是在当前游戏对象里找 "body" 组件. 这就很方便因为脚本可以在 *任何* 游戏对象上运行, 只要它有 "body" 组件. +在上述示例中,脚本组件使用相对地址"#body"来寻址精灵组件。该地址是相对的,因为它是从脚本组件所在位置开始解析的。地址中的"#"字符表示"当前游戏对象中的组件"。因此,整个地址"#body"应解释为"当前游戏对象中标识符为'body'的组件"。 ## 集合 @@ -145,7 +145,7 @@ Defold 提供两种简化写法用来简化消息传递时需要输入的完整 然后每个豆子先生负责向管理器发送状态消息: "contact" 表明碰到了敌人, 或者 "ouch!" 表明受到了袭击. 为了这项工作, 豆子控制器脚本使用相对地址向 "manager" 里的 "controller" 组件发送消息. -任何以 '/' 开头的地址都从游戏世界的根上进行索引. 这对应了游戏启动时载入的 *bootstrap collection* 的根. +绝对地址是从游戏世界的根(即"集合")开始解析的。绝对地址以"/"字符开头,表示"从根开始"。 控制器脚本的绝对地址是`"/manager#controller"` 而且不管组件用在哪里该绝对地址总能定位到该组件. @@ -153,9 +153,9 @@ Defold 提供两种简化写法用来简化消息传递时需要输入的完整 ![absolute addressing](images/addressing/absolute.png) -## 哈希标记 +## 哈希ID -引擎把每个 id 都存为哈希值. 所有以组件或游戏对象为参数的方法可以接受字符串, 哈希或者 URL 对象. 我们已经在上面看到如何使用字符串进行定位了. +Defold引擎将每个标识符转换为64位哈希值,用于内部寻址。这比使用字符串更节省内存和CPU。然而,有时你需要手动计算哈希值,例如在将游戏状态保存到文件时。所有以组件或游戏对象为参数的方法可以接受字符串, 哈希或者 URL 对象. 我们已经在上面看到如何使用字符串进行定位了. 当你获取游戏对象的 id , 引擎总是返回一个绝对路径 id 的哈希值: @@ -186,13 +186,20 @@ local relative_id = hash("my_object") go.set_position(pos, relative_id) ``` -## URLs +## URL格式 -最后我们来看 Defold 定位的完全体: URL. +Defold中的URL遵循以下格式: -URL 是一个对象, 通常用特定格式的字符串表示. 一般一个 URL 包含三个部分: +``` +[socket:][path][#fragment] +``` + +其中: +- `socket` - 用于网络通信(可选) +- `path` - 游戏对象和集合的路径 +- `fragment` - 组件的标识符(以"#"开头) -`[socket:][path][#fragment]` +URL是一个对象,通常用特定格式的字符串表示。一般一个URL包含三个部分: socket : 代表目标的游戏世界. 使用 [集合代理](/manuals/collection-proxy) 时, 它用来表示 _动态加载的集合_. @@ -243,4 +250,75 @@ my_url.fragment = "controller" -- specify as string or hash -- Post to target specified by URL msg.post(my_url, "hello_manager!") + +``` + +### URL示例 + +| URL | 描述 | +|-----|------| +| `"#"` | 当前游戏对象 | +| `"#body"` | 当前游戏对象中标识符为"body"的组件 | +| `"/level1/main/bean#body"` | "level1"集合中"main"集合里"bean"游戏对象的"body"组件 | +| `"main:/bean#body"` | 在"main"socket中,"bean"游戏对象的"body"组件 | + +### URL对象 + +你可以使用`msg.url()`函数创建URL对象: + +```lua +local my_url = msg.url("#body") +``` + +这将返回一个URL对象,可以用于寻址或存储。URL对象也可以从字符串、哈希值或其他URL对象构造: + +```lua +local my_url1 = msg.url("#body") -- 从字符串构造 +local my_url2 = msg.url(my_url1) -- 从另一个URL对象构造 +local my_url3 = msg.url(nil, "/level1/main/bean", hash("body")) -- 从路径和哈希值构造 +``` + +### 发送消息 + +你可以使用`msg.post()`函数向URL发送消息: + +```lua +msg.post("#body", "disable") +``` + +这将向当前游戏对象中标识符为"body"的组件发送一条"disable"消息。你也可以使用URL对象: + +```lua +local my_url = msg.url("#body") +msg.post(my_url, "disable") +``` + +### URL字符串 + +你可以使用`tostring()`函数将URL对象转换为字符串: + +```lua +local my_url = msg.url("#body") +print(tostring(my_url)) -- 输出: "#body" +``` + +### URL哈希 + +你可以使用`msg.url()`函数将URL对象转换为哈希值: + +```lua +local my_url = msg.url("#body") +print(my_url) -- 输出: 哈希值 +``` + +### URL比较 + +你可以使用`==`运算符比较两个URL对象: + +```lua +local my_url1 = msg.url("#body") +local my_url2 = msg.url("#body") +if my_url1 == my_url2 then + print("URLs are equal") +end ``` diff --git a/docs/zh/manuals/ads.md b/docs/zh/manuals/ads.md index 48cf455c..e097680d 100644 --- a/docs/zh/manuals/ads.md +++ b/docs/zh/manuals/ads.md @@ -1,61 +1,61 @@ --- -title: 在 Defold 中播放广告 -brief: 广告是网页游戏和手机游戏的基本盈利方式. 本教程介绍了在 Defold 中如何通过广告盈利. +title: 在 Defold 中展示广告 +brief: 展示各种类型的广告是网页和手机游戏盈利的常见方式。本手册介绍了使用广告为游戏盈利的多种方法。 --- # 广告 -广告作为网页游戏和手机游戏的一种普遍盈利方式日趋壮大. 玩家观看游戏里的广告, 开发者有机会获利. 一般看的越多获利越多, 但是最终受益还要取决于下面几项: +广告已成为网页和手机游戏盈利的非常普遍的方式,并已发展成为一个价值数十亿美元的产业。作为开发者,你的收入基于观看游戏中广告的人数。通常情况很简单,观看者越多收入越多,但其他因素也会影响你的收入: -* 广告质量 - 游戏内容相关的广告容易获得玩家点击与关注. -* 广告类型 - 广告条的利润低而从头看到尾的全屏广告利润高. -* 广告商 - 不同广告供应商的广告利润有高有低. +* 广告质量 - 相关广告更容易获得玩家的互动和关注。 +* 广告格式 - 横幅广告通常收入较低,而从头到尾观看的全屏广告收入更高。 +* 广告网络 - 你获得的收入因广告网络而异。 ::: sidenote -CPM = 千人成本. 广告商为一千个浏览量支付的报酬. 不同广告商不同广告类型费用也不同. +CPM = 千人成本。广告商为每千次观看支付的金额。CPM因广告网络和广告格式而异。 ::: -## 类型 +## 格式 -游戏中可以放各种广告. 常见的有广告条, 插播广告和奖励广告: +游戏中可以使用许多不同类型的广告格式。一些更常见的格式是横幅广告、插页广告和奖励广告: -### 广告条 +### 横幅广告 -广告条可以是文字的, 图片的或者视频的并且占据一部分屏幕, 一般不是屏幕上边就是底边. 广告条很容易植入, 休闲小游戏也容易空出广告条的位置来. 曝光率高, 玩家反感较少. +横幅广告基于文本、图像或视频,覆盖屏幕的相对较小部分,通常位于屏幕的顶部或底部。横幅广告非常容易实现,并且非常适合休闲单屏游戏,因为很容易为广告预留屏幕区域。横幅广告最大化了曝光率,因为用户可以在不中断游戏的情况下玩你的游戏。 -### 插播广告 +### 插页广告 -插播广告是一种带动画有的还有多媒体交互的全屏广告. 插播广告通常出现在游戏关与关之间或者游戏暂停等时候. 插播广告曝光不高, 但是付费较高, 可以带来不菲收入. +插页广告是带有动画的大型全屏体验,有时还包含交互式富媒体内容。插页广告通常在关卡之间或游戏会话之间展示,因为这是游戏体验中的自然中断。插页广告通常产生的观看次数少于横幅广告,但其成本(CPM)远高于横幅广告,从而带来可观的总体广告收入。 ### 奖励广告 -奖励广告 (也叫激励广告) 是一种玩家最不反感的广告. 样子通常跟插播广告类似. 玩家可以通过观看广告获得游戏虚拟奖励 - 比如财产, 金币, 命, 游戏延时等等. 奖励广告通常付费最高, 但是观看率取决于玩家. 在关键时刻给予玩家渴望的奖励才能取得激励的效果. +奖励广告(也称为激励广告)是可选的,因此比许多其他形式的广告侵入性更小。奖励广告通常是像插页广告一样的全屏体验。用户可以选择观看广告以换取奖励 - 例如战利品、硬币、生命、时间或其他游戏内货币或福利。奖励广告通常具有最高的成本(CPM),但观看次数直接与用户选择率相关。只有当奖励足够有价值并在适当时机提供时,奖励广告才能产生良好的效果。 -## 广告商 +## 广告网络 -在 [Defold 资源中心](/tags/stars/ads/) 提供了一些广告商的支持程序: +[Defold 资源门户](/tags/stars/ads/)包含几个与广告提供商集成的资源: -* [AdMob](https://defold.com/assets/admob-defold/) - 谷歌 AdMob 广告. -* [Enhance](https://defold.com/assets/enhance/) - 广告商大集合. 编译后需要额外的安装步骤. -* [Facebook Instant Games](https://defold.com/assets/facebookinstantgames/) - 脸书广告. -* [IronSource](https://defold.com/assets/ironsource/) - IronSource 广告. -* [Unity Ads](https://defold.com/assets/defvideoads/) - Unity 广告. +* [AdMob](https://defold.com/assets/admob-defold/) - 使用 Google AdMob 网络展示广告。 +* [Enhance](https://defold.com/assets/enhance/) - 支持多种不同的广告网络。需要额外的构建后步骤。 +* [Facebook Instant Games](https://defold.com/assets/facebookinstantgames/) - 在你的 Facebook Instant Game 中展示广告。 +* [IronSource](https://defold.com/assets/ironsource/) - 使用 IronSource 广告网络展示广告。 +* [Unity Ads](https://defold.com/assets/defvideoads/) - 使用 Unity Ads 网络展示广告。 -# 广告游戏整合 +# 如何在游戏中整合广告 -决定好合作广告商就可以按步骤地植入广告资源了. 一般都要首先加入 [项目依赖](/manuals/libraries/#设置库依赖). 之后载入广告资源, 展示广告内容. +当你决定要在游戏中整合广告网络后,你需要遵循该特定资源的安装和使用说明。通常你要做的是首先将该扩展添加为[项目依赖](/manuals/libraries/#设置库依赖)。一旦将资源添加到项目中,你就可以继续进行集成,并调用特定于该资源的函数来加载和展示广告。 -# 广告与内支付 +# 结合广告和应用内购买 -手机游戏通常含有 [应用内支付](/manuals/iap) 功能以便获得更大收益. +在手机游戏中,提供[应用内购买](/manuals/iap)以永久移除广告是相当常见的。 -## 更多资源 +## 了解更多 -网上也有很多关于优化广告的教程: +有许多在线资源可供学习优化广告收入: * Google AdMob [Monetize mobile games with ads](https://admob.google.com/home/resources/monetize-mobile-game-with-ads/) * Game Analytics [Popular ad formats and how to use them](https://gameanalytics.com/blog/popular-mobile-game-ad-formats.html) diff --git a/docs/zh/manuals/android.md b/docs/zh/manuals/android.md index 1583353b..70ed3555 100644 --- a/docs/zh/manuals/android.md +++ b/docs/zh/manuals/android.md @@ -1,58 +1,58 @@ --- title: Defold 的 Android 平台开发 -brief: 本教程介绍了如何在 Defold 中进行 Android 设备应用的开发 +brief: 本手册描述了如何在 Android 设备上构建和运行 Defold 应用程序 --- # Android 开发 -Android 设备允许自由允许你开发的应用. 可以很容易地编译好游戏拷贝到 Android 设备上. 本手册介绍了对于 Android 游戏的打包步骤. 推荐开发时, 从 [开发应用](/manuals/dev-app) 上运行游戏因为可以通过无线连接设备进行代码和内容的热重载. +Android 设备允许你自由运行自己的应用程序。构建游戏版本并将其复制到 Android 设备上非常容易。本手册解释了为 Android 打包游戏所涉及的步骤。在开发过程中,通过[开发应用](/manuals/dev-app)运行游戏通常是首选,因为它允许你直接将内容和代码热重载到设备上。 -## Android 和 Google Play 签名 +## Android 和 Google Play 签名流程 -Android 要求每个 APK 文件在被安装到设备上或者在设备上更新之前必须进行数字签名. 如果你是安卓开发者, 只需要在把程序包上传到 Play Console 之前, 经过 [Play App Signing](https://developer.android.com/studio/publish/app-signing#app-signing-google-play) 的自动处理即可. 然而, 你还可以选择手动对程序包进行签名以便上传到 Google Play, 其他应用商店以及在整个互联网上传播. +Android 要求所有 APK 在安装到设备上或更新之前都必须使用证书进行数字签名。如果你使用 Android App Bundles,只需在上传到 Play Console 之前对你的应用包进行签名,[Play App Signing](https://developer.android.com/studio/publish/app-signing#app-signing-google-play)会处理其余部分。但是,你也可以手动为应用签名,以便上传到 Google Play、其他应用商店以及在任何商店之外分发。 -从 Defold 编辑器或者 [命令行工具](/manuals/bob) 打包安卓包需要提供一个 keystore (包括证书和公匙), 然后对应用签名时还要用到私匙. 没有的话, Defold 会自动生成一个临时调试用 keystore 用于打包和签名. +当你从 Defold 编辑器或[命令行工具](/manuals/bob)创建 Android 应用包时,你可以提供一个密钥库(包含你的证书和密钥)和密钥库密码,这些将在签名你的应用程序时使用。如果你不提供,Defold 会生成一个调试密钥库并在签名应用程序包时使用它。 -::: sidenote -千万 **不要** 带着调试签名就上传到 Google Play 上去. 开发者必须自己制作属于自己的签名. +::: important +你**绝对不应该**将使用调试密钥库签名的应用程序上传到 Google Play。始终使用你自己创建的专用密钥库。 ::: -## 制作 keystore +## 创建密钥库 ::: sidenote -Defold 应对安卓应用包签名的改变是从 1.2.173 版开始的, 就是使用单独的证书和密码来合成 keystore. [详见论坛帖子](https://forum.defold.com/t/upcoming-change-to-the-android-build-pipeline/66084). +Defold 中的 Android 签名流程在版本 1.2.173 中发生了变化,从使用独立的密钥和证书改为使用密钥库。[更多信息请参阅此论坛帖子](https://forum.defold.com/t/upcoming-change-to-the-android-build-pipeline/66084)。 ::: -也可以 [使用 Android Studio](https://developer.android.com/studio/publish/app-signing#generate-key) 或者通过使用控制台命令来生成签名: +你可以[使用 Android Studio](https://developer.android.com/studio/publish/app-signing#generate-key)或从终端/命令提示符创建密钥库: ```bash keytool -genkey -v -noprompt -dname "CN=John Smith, OU=Area 51, O=US Air Force, L=Unknown, ST=Nevada, C=US" -keystore mykeystore.keystore -storepass 5Up3r_53cR3t -alias myAlias -keyalg RSA -validity 9125 ``` -这个命令会生成一个叫做 `mykeystore.keystore` 的签名, 其中包含了证书和密码. 密匙 `5Up3r_53cR3t` 保护其不备破解. 这个签名有效期为 25 年 (9125 天). 这个签名的id叫做 `myAlias`. +这将创建一个名为 `mykeystore.keystore` 的密钥库文件,其中包含一个密钥和证书。对密钥和证书的访问将受到密码 `5Up3r_53cR3t` 的保护。密钥和证书将在 25 年(9125 天)内有效。生成的密钥和证书将通过别名 `myAlias` 进行标识。 -::: sidenote -要把签名和密匙保存好. 如果要手动上传 Google Play 但是签名密码丢失的话就没办法使用 Google Play 来更新你的应用了. 图省事的话就用 Google Play App Signing 搞定签名吧. +::: important +确保将密钥库和相关密码存储在安全的地方。如果你自己签名并将应用程序上传到 Google Play,而密钥库或密钥库密码丢失,你将无法在 Google Play 上更新应用程序。你可以通过使用 Google Play App Signing 并让 Google 为你签名应用程序来避免这种情况。 ::: -## 安卓应用打包 +## 创建 Android 应用包 -编辑器打包安卓包十分方便. 打包之前可以为应用指定图标, 设置版本号等等, 都在 *game.project* [项目配置文件](/manuals/project-settings/#Android) 里设置. +编辑器允许你轻松地为游戏创建独立的应用包。在打包之前,你可以在 *game.project* [项目设置文件](/manuals/project-settings/#android) 中指定要使用的图标、设置版本代码等。 -选择菜单栏 Project ▸ Bundle... ▸ Android Application... 就可以打包了. +要从菜单中选择打包,请选择 Project ▸ Bundle... ▸ Android Application...。 -要让编辑器自动生成调试用签名, 只需把 *Keystore* 和 *Keystore password* 字段留空即可: +如果你希望编辑器自动创建随机调试证书,请将 *Keystore* 和 *Keystore password* 字段留空: ![Signing Android bundle](images/android/sign_bundle.png) -要让编辑器使用你自己指定的签名打包, 就要设置好 *Keystore* 和 *Keystore password* 字段. *Keystore* 的扩展名是 `.keystore`, 而密码要保存成文本 `.txt` 文件. 如果 keystore 里的 key 使用了自己的密码, 也可在 *Key password* 里指定: +如果你想使用特定的密钥库为你的包签名,请指定 *Keystore* 和 *Keystore password*。*Keystore* 预期具有 `.keystore` 文件扩展名,而密码预期存储在具有 `.txt` 扩展名的文本文件中。如果密钥库中的密钥使用与密钥库本身不同的密码,也可以指定 *Key password*: ![Signing Android bundle](images/android/sign_bundle2.png) -Defold 支持创建 APK 和 AAB 文件. 从打包格式下拉菜单中选择. +Defold 支持创建 APK 和 AAB 文件。从 Bundle Format 下拉菜单中选择 APK 或 AAB。 -点击 Create Bundle 会提示选择打包文件存放位置. +配置好应用包设置后,按 Create Bundle。然后系统会提示你指定在计算机上创建包的位置。 ![Android Application Package file](images/android/apk_file.png) @@ -62,7 +62,9 @@ Defold 支持创建 APK 和 AAB 文件. 从打包格式下拉菜单中选择. #### 安装 APK -编辑器生成 Android 应用包 *.apk* 文件. 应用包可以通过 `adb` 工具安装到设备上, 或者通过 [Google Play 开发者控制台](https://play.google.com/apps/publish/) 发布到 Google Play 上. +一个 *`.apk`* 文件可以使用 `adb` 工具复制到你的设备上,或者通过 [Google Play 开发者控制台](https://play.google.com/apps/publish/) 复制到 Google Play。 + +:[Android ADB](../shared/android-adb.md) ``` $ adb install Defold\ examples.apk @@ -73,29 +75,32 @@ Success #### 使用编辑器安装 APK -你可以在编辑器的打包对话框中勾选 "Install on connected device" 和 "Launch installed app" 安装并启动 *.apk*: +你可以使用编辑器打包对话框中的"在连接的设备上安装"和"启动已安装的应用"复选框来安装和启动 *`.apk`* 文件: ![Install and Launch APK](images/android/install_and_launch.png) -为了顺利运行, 你要安装好 ADB 并打开连接设备的 *USB debugging*. 如果编辑器无法找到 ADB 命令行工具的地址, 你要在 [Preferences](/manuals/editor-preferences/#tools) 中指定好. +要使此功能正常工作,你需要安装 ADB 并在连接的设备上启用 *USB 调试*。如果编辑器无法检测到 ADB 命令行工具的安装位置,你需要在[首选项](/manuals/editor-preferences/#tools)中指定它。 #### 安装 AAB -对于 *.aab* 文件可以通过 [Google Play 开发者控制台](https://play.google.com/apps/publish/) 上传给 Google Play. 也可以使用 *.aab* 文件制作 *.apk* 以便使用 [Android 打包工具](https://developer.android.com/studio/command-line/bundletool) 在本地安装. +一个 *`.aab`* 文件可以通过 [Google Play 开发者控制台](https://play.google.com/apps/publish/) 上传到 Google Play。也可以使用 *`.aab`* 文件制作 *`.apk`* 以便使用 [Android 打包工具](https://developer.android.com/studio/command-line/bundletool) 在本地安装。 ## 权限 -Defold 引擎需要一些权限来运行各种功能. 权限在 `AndroidManifest.xml` 文件中定义, 并在 *game.project* [项目配置文件](/manuals/project-settings/#Android) 中配置. 关于 Android 权限详见 [官方文档](https://developer.android.com/guide/topics/permissions/overview). 默认配置需要如下权限: +Defold 引擎需要许多不同的权限才能使所有引擎功能正常工作。权限在 `AndroidManifest.xml` 中定义,在 *game.project* [项目设置文件](/manuals/project-settings/#android) 中指定。你可以在[官方文档](https://developer.android.com/guide/topics/permissions/overview)中阅读更多关于 Android 权限的信息。默认清单中请求以下权限: ### android.permission.INTERNET and android.permission.ACCESS_NETWORK_STATE (Protection level: normal) -允许应用打开网络连接访问互联网. 需要上网时需要此权限. 见 ([Android 官方文档-网络](https://developer.android.com/reference/android/Manifest.permission#INTERNET)) 和 ([Android 官方文档-网络状态](https://developer.android.com/reference/android/Manifest.permission#ACCESS_NETWORK_STATE)). + +允许应用程序打开网络套接字并访问网络信息。这些权限是访问互联网所必需的。([Android 官方文档](https://developer.android.com/reference/android/Manifest.permission#INTERNET))和([Android 官方文档](https://developer.android.com/reference/android/Manifest.permission#ACCESS_NETWORK_STATE))。 ### android.permission.WAKE_LOCK (Protection level: normal) -允许应用阻止屏幕息屏和调光. 接收通知保持亮屏时需要此权限. ([[Android 官方文档-亮屏锁定](https://developer.android.com/reference/android/Manifest.permission#WAKE_LOCK)) + +允许使用 PowerManager WakeLocks 来防止处理器休眠或屏幕变暗。在接收推送通知时暂时防止设备休眠需要此权限。([Android 官方文档](https://developer.android.com/reference/android/Manifest.permission#WAKE_LOCK)) ## 使用 AndroidX -AndroidX 一個較大改動就是, 不再維護 Android Support Library 了. AndroidX 應用使用雲計算功能和新庫完整取代了 Support Library. [Asset Portal](/assets) 裏的绝大多数擴展包已經支持 AndroidX. 如果希望使用旧版安卓库而不是 AndroidX, 可以创建新的 app manifest 文件或在 Defold Manifest 文件里勾選 "Use Android Support lib" 選項. + +AndroidX 是对原始 Android 支持库的重大改进,该支持库已不再维护。AndroidX 包通过提供功能对等和新库完全取代了支持库。[资源门户](/assets) 中的大多数 Android 扩展都支持 AndroidX。如果你不想使用 AndroidX,可以通过在[应用程序清单](https://defold.com/manuals/app-manifest/)中勾选 `Use Android Support Lib` 来明确禁用它,转而使用旧的 Android 支持库。 ![](images/android/enable_supportlibrary.png) diff --git a/docs/zh/manuals/animation.md b/docs/zh/manuals/animation.md index 629a7eee..a4aec574 100644 --- a/docs/zh/manuals/animation.md +++ b/docs/zh/manuals/animation.md @@ -1,17 +1,17 @@ --- -title: Defold 动画教程 -brief: 本教程介绍了 Defold 的动画支持. +title: Defold 动画手册 +brief: 本手册介绍了 Defold 的动画支持。 --- # 动画 -Defold 内置组件支持多种动画: +Defold 内置支持多种类型的动画,你可以将其用作组件的图形源: -* [逐帧动画](/manuals/flipbook-animation) - 按顺序显示图片而形成的动画 -* [3D 模型动画](/manuals/model-animation) - 3D 蒙皮动画 -* [属性动画](/manuals/property-animation) - 以position, scale, rotation 等属性插值变换而形成的动画 +* [逐帧动画](/manuals/flipbook-animation) - 按顺序播放一系列静态图像 +* [模型动画](/manuals/model-animation) - 播放 3D 蒙皮动画 +* [属性动画](/manuals/property-animation) - 对位置、缩放、旋转等许多属性进行动画处理 -其他动画格式要使用扩展支持: +可以通过扩展添加其他动画格式: -* [Rive animation](/extension-rive) - 播放基于矢量的 2D 骨骼动画 -* [Spine animation](/extension-spine) - 播放贴图 2D 骨骼动画 +* [Rive 动画](/extension-rive) - 播放基于矢量的 2D 骨骼动画 +* [Spine 动画](/extension-spine) - 播放带纹理的 2D 骨骼动画 diff --git a/docs/zh/manuals/app-manifest.md b/docs/zh/manuals/app-manifest.md index fcb17c29..e21d6b18 100644 --- a/docs/zh/manuals/app-manifest.md +++ b/docs/zh/manuals/app-manifest.md @@ -1,60 +1,95 @@ --- -title: App manifest -brief: 本教程介绍了如何使用应用清单来去掉引擎的特性. +title: 应用清单 +brief: 本手册介绍了如何使用应用清单来排除引擎中的功能。 --- -# App manifest +# 应用清单 -应用清单控制为引擎加入或去掉功能特性. 推荐为引擎去掉不用的特性因为可以减小游戏包体. +应用清单用于排除或控制要在引擎中包含哪些功能。排除引擎中未使用的功能是推荐的最佳实践,因为它会减小游戏的最终二进制文件大小。 +此外,应用清单还包含一些用于控制 HTML5 平台代码编译的选项,如最低支持的浏览器版本/内存设置,这些也会影响结果二进制文件的大小。 ![](images/app_manifest/create-app-manifest.png) ![](images/app_manifest/app-manifest.png) +# 应用清单 + +在 `game.project` 中,将清单分配给 `Native Extensions` -> `App Manifest`。 + ## Physics -控制使用哪个物理引擎, 或者选择 None 来完全去掉物理引擎. +控制使用哪个物理引擎,或选择 None 来完全排除物理功能。 +## Physics 2d -## Exclude Record +选择使用哪个版本的 Box2D。 -从引擎中去掉视频录制功能 (参见手册 [`start_record`](https://defold.com/ref/stable/sys/#start_record) 消息). +## Rig + Model +控制骨骼和模型功能,或选择 None 来完全排除模型和骨骼功能。(参见[`模型`](https://defold.com/manuals/model/#model-component)文档)。 -## Exclude Profiler +## Exclude Record -从引擎中去掉分析器. 分析器用来收集性能和使用计数器. 参见 [分析器教程](/manuals/profiling/). +从引擎中排除视频录制功能(参见[`start_record`](https://defold.com/ref/stable/sys/#start_record)消息文档)。 +## Exclude Profiler -## Exclude Sound +从引擎中排除分析器。分析器用于收集性能和使用计数器。在[分析手册](/manuals/profiling/)中学习如何使用分析器。 -从引擎中去掉所有声音播放功能. +## Exclude Sound +从引擎中排除所有声音播放功能。 ## Exclude Input -从引擎中去掉所有输入处理. - +从引擎中排除所有输入处理功能。 ## Exclude Live Update -从引擎中去掉 [热更新功能](/manuals/live-update). +从引擎中排除[热更新功能](/manuals/live-update)。 +## Exclude Image -## Exclude Basis Universal +从引擎中排除`image`脚本模块[链接](https://defold.com/ref/stable/image/)。 -从引擎中去掉基础通用 [纹理压缩库](/manuals/texture-profiles). +## Exclude Types +从引擎中排除`types`脚本模块[链接](https://defold.com/ref/stable/types/)。 -## Use Android Support Lib +## Exclude Basis Universal + +从引擎中排除 Basis Universal[纹理压缩库](/manuals/texture-profiles)。 -使用安卓支持库而不使用 Android X. [更多详情参见这里](https://defold.com/manuals/android/#using-androidx). +## Use Android Support Lib +使用已弃用的 Android 支持库而不是 Android X。[更多信息](https://defold.com/manuals/android/#using-androidx)。 ## Graphics -选择使用的图形后端. +选择使用哪个图形后端。 + +* OpenGL - 仅包含 OpenGL。 +* Vulkan - 仅包含 Vulkan。 +* OpenGL and Vulkan - 同时包含 OpenGL 和 Vulkan。Vulkan 将是默认选项,如果 Vulkan 不可用则回退到 OpenGL。 + +## HTML5 平台设置 + +### 浏览器版本 + +设置 HTML5 构建要支持的最低浏览器版本。这会影响最终二进制文件的大小,因为引擎会包含所有必要的 polyfill 来支持指定的浏览器版本。 + +* 默认值 - 包含所有 polyfill,支持所有浏览器。 +* ES2015 - 包含支持 ES2015(或更高版本)浏览器所需的 polyfill。 +* ES2017 - 包含支持 ES2017(或更高版本)浏览器所需的 polyfill。 +* ES2019 - 包含支持 ES2019(或更高版本)浏览器所需的 polyfill。 +* ES2021 - 包含支持 ES2021(或更高版本)浏览器所需的 polyfill。 + +### 内存设置 + +控制 HTML5 构建的内存设置。这会影响最终二进制文件的大小,因为引擎会包含所有必要的 polyfill 来支持指定的内存设置。 -* OpenGL - 只包含 OpenGL. -* Vulkan - 只包含 Vulkan. -* OpenGL and Vulkan - 同时包含 OpenGL 和 Vulkan. Vulkan 是默认的, Vulkan 不可以时使用 OpenGL. +* 默认值 - 包含所有 polyfill,支持所有内存设置。 +* 128MB - 包含支持 128MB 内存设置所需的 polyfill。 +* 256MB - 包含支持 256MB 内存设置所需的 polyfill。 +* 512MB - 包含支持 512MB 内存设置所需的 polyfill。 +* 1GB - 包含支持 1GB 内存设置所需的 polyfill。 diff --git a/docs/zh/manuals/application-lifecycle.md b/docs/zh/manuals/application-lifecycle.md index 87fe9191..358cfa42 100644 --- a/docs/zh/manuals/application-lifecycle.md +++ b/docs/zh/manuals/application-lifecycle.md @@ -1,41 +1,41 @@ --- -title: Defold 应用生命周期教程 -brief: 本教程详述了 Defold 游戏的生命周期. +title: Defold 应用生命周期手册 +brief: 本手册详述了 Defold 游戏和应用程序的生命周期。 --- # 应用生命周期 -Defold 应用或者游戏的生命周期相当简单. 引擎切换运行三种状态: 初始化, 更新循环 (游戏主要耗时状态) 以及析构. +Defold 应用程序或游戏的生命周期总体上很简单。引擎会经历三个执行阶段:初始化、更新循环(应用程序和游戏大部分时间都在这里度过)和最终化。 ![Lifecycle overview](images/application_lifecycle/application_lifecycle_overview.png) -通常对于 Defold 的内部运作机制稍作了解就足够了. 然而, 有时了解 Defold 内部的运行顺序还是有必要的. 本教程介绍了 Defold 应用从始至终是按什么顺序运行的. +在许多情况下,只需要对 Defold 的内部工作原理有一个基本的了解就足够了。然而,你可能会遇到一些边缘情况,此时 Defold 执行任务的精确顺序变得至关重要。本文档描述了引擎如何从头到尾运行应用程序。 -引擎一开始进行初始化操作. 载入启动集合然后在所有组件上调用 [`init()`](/ref/go#init) 函数 (包括脚本组件和GUI脚本). 这个函数用于进行用户自定义初始化操作. +应用程序首先初始化运行引擎所需的一切。它加载主集合并调用所有具有 `init()` Lua 函数的已加载组件(脚本组件和带有 GUI 脚本的 GUI 组件)的 [`init()`](/ref/go#init) 函数。这允许你进行自定义初始化。 -接下来就是应用主要耗时环节更新循环. 每一帧, 每个游戏对象及其组件都会进行更新操作. 脚本和GUI脚本的 [`update()`](/ref/go#update) 函数被调用. 与此同时消息机制开始运作, 播放声音和渲染图像的程序开始运行. +然后应用程序进入更新循环,应用程序将在此度过其生命周期的大部分时间。每一帧,游戏对象及其包含的组件都会被更新。任何脚本和 GUI 脚本的 [`update()`](/ref/go#update) 函数都会被调用。在更新循环期间,消息被分发给它们的接收者,声音被播放,所有图形都被渲染。 -应用最后会结束运行. 应用退出之前引擎会退出更新循环进入析构阶段. 准备删除所有被加载的游戏对象. 所有对象的 [`final()`](/ref/go#final) 函数被调用, 用于进行用户自定义析构操作. 然后删除所有游戏对象以及启动集合. +在某个时刻,应用程序的生命周期将结束。在应用程序退出之前,引擎会退出更新循环并进入最终化阶段。它准备删除所有已加载的游戏对象。所有对象组件的 [`final()`](/ref/go#final) 函数都会被调用,这允许进行自定义清理。然后对象被删除,主集合被卸载。 ## 初始化 -下图包含了初始化的分解步骤. 其中 "dispatch messages" 阶段 (在 "spawn dynamic objects" 上方) 单独在右侧图表进行详细分解. +下图包含了初始化步骤的更详细分解。"dispatch messages"传递中涉及的步骤(在"spawn dynamic objects"之前)为了清晰起见已单独放在右侧的块中。 ![Lifecycle overview](images/application_lifecycle/application_lifecycle_init.png) -其实初始化阶段启动集合被加载之前引擎还做了许多别的工作. 内存分析, 接口, 图像, HID (输入设备), 声音, 物理等等的初始化. 应用配置文件 (*game.project*) 也在此时被加载. +实际上,在主集合加载之前,引擎在初始化过程中会采取更多步骤。内存分析器、套接字、图形、HID(输入设备)、声音、物理等等都被设置。应用程序配置(*game.project*)也被加载和设置。 -最开始的用户可控的操作, 是在引擎初始化结束后, 对于渲染脚本 `init()` 函数的调用. +在引擎初始化结束时,第一个用户可控制的入口点是对当前渲染脚本的 `init()` 函数的调用。 -然后启动集合被加载和初始化. 对其中所有游戏对象及其子对象设置位移 (位置, 旋转和缩放). 所有组件的 `init()` 函数被调用. +然后加载并初始化主集合。集合中的所有游戏对象将其变换(平移(位置变化)、旋转和缩放)应用到它们的子对象。然后调用所有存在的组件 `init()` 函数。 ::: sidenote -每个 `init()` 函数调用顺序不确定. 调用顺序和集合里所处位置无关. +游戏对象组件 `init()` 函数的调用顺序是未指定的。你不应该假设引擎以特定顺序初始化属于同一集合的对象。 ::: -因为 `init()` 代码里可以有消息发布, 工厂创建对象, 删除对象等等操作, 引擎接下来会进入 "post-update" 阶段. 此时消息被发布出去, 工厂实际创建对象, 需要删除的对象进行删除操作. 注意 post-update 阶段包含的 "dispatch messages" 序列不仅发送消息队列还会处理发送给集合代理的消息. 代理子队列 (开启关闭, 载入和标记卸载) 等步骤在此时进行处理. +由于你的 `init()` 代码可以发布新消息,告诉工厂生成新对象,标记对象以供删除以及执行各种操作,引擎接下来会执行完整的"post-update"传递。此传递执行消息传递、实际的工厂游戏对象生成和对象删除。请注意,post-update 传递包含一个"dispatch messages"序列,它不仅发送任何排队的消息,还处理发送到集合代理的消息。代理的任何后续更新(启用和禁用、加载和标记为卸载)都在这些步骤中执行。 -也就是说在 `init()` 里进行 [集合代理](/manuals/collection-proxy) 加载操作是完全可行的, 集合初始化, 从代理卸载集合---这些都能在第一个 `update()` 被调用之前进行, 也就是在引擎退出初始化进入更新循环之前进行: +研究上图可以发现,在 `init()` 期间加载[集合代理](/manuals/collection-proxy),确保其包含的所有对象都被初始化,然后通过代理卸载集合是完全可能的——所有这些都在第一个组件 `update()` 被调用之前,即在引擎离开初始化阶段并进入更新循环之前: ```lua function init(self) @@ -44,7 +44,7 @@ function init(self) end function update(self, dt) - -- 运行到这里时代理集合已经被卸载了. + -- 在到达此代码之前,代理集合已被卸载。 print("update()") end @@ -54,71 +54,71 @@ function on_message(self, message_id, message, sender) msg.post("#collectionproxy", "init") msg.post("#collectionproxy", "enable") msg.post("#collectionproxy", "unload") - -- 代理集合 init() 和 final() 函数 - -- 会在 update() 调用之前完成 + -- 代理集合对象的 init() 和 final() 函数 + -- 在我们到达此对象的 update() 之前被调用 end end ``` ## 更新循环 -每帧都会循环更新长长的序列. 下图展示了更新循环的详细步骤. "Dispatch messages" 阶段还是单独在右侧独立详细展示: +更新循环每帧运行一次长序列。下图中的更新序列为了清晰起见分为逻辑序列块。"Dispatch messages"出于同样的原因也被单独分解出来: ![Update loop](images/application_lifecycle/application_lifecycle_update.png) ## 输入 -从可用设备中读取输入数据, 通过映射 [输入绑定表](/manuals/input) 把输入派发出去. 获得输入焦点的游戏对象里每个组件的 `on_input()` 函数都会获得输入数据. 不管是脚本组件还是GUI脚本 `on_input()` 函数都会被调用---只要它们被定义了而且获得了输入焦点. +从可用设备读取输入,根据[输入绑定](/manuals/input)进行映射,然后分发。任何获得输入焦点的游戏对象都会将输入发送到其所有组件的 `on_input()` 函数。具有脚本组件和带有 GUI 脚本的 GUI 组件的游戏对象将获得输入到两个组件的 `on_input()` 函数——前提是它们已被定义并且已获得输入焦点。 -获得输入焦点并且包含集合代理组件的游戏对象会把输入发送到代理集合内部去. 这个过程在被开启的代理间递归进行下去. +任何获得输入焦点并包含集合代理组件的游戏对象会将输入分发到代理集合内的组件。这个过程在启用的集合代理内的启用的集合代理中递归进行下去。 ## 更新 -启动集合每个游戏对象组件都会被遍历到. 只要它们定义了 `update()` 函数, 就会在更新时被调用. 对于集合代理组件, 其内部 "update" 过程会递归进行下去. +遍历主集合中的每个游戏对象组件。如果这些组件中的任何一个具有脚本 `update()` 函数,那么该函数将被调用。如果组件是集合代理,则代理集合中的每个组件都会递归地更新,执行上图"update"序列中的所有步骤。 ::: sidenote -如果 [物理模拟使用了固定时间步](/manuals/physics/#physics-updates) 则每个脚本里的 `fixed_update()` 也可能会被自动调用. 这有助于物理模拟使用固定时间间隔而产生稳定模拟效果. +如果[物理模拟使用固定时间步](/manuals/physics/#physics-updates),则所有脚本组件中也可能会有对 `fixed_update()` 函数的调用。在基于物理的游戏中,当你希望以固定间隔操作物理对象以实现稳定的物理模拟时,此函数很有用。 ::: ::: sidenote -每个 `update()` 函数调用顺序不确定. 调用顺序和集合里所处位置无关. +游戏对象组件 `update()` 函数的调用顺序是未指定的。你不应该假设引擎以特定顺序更新属于同一集合的对象。 ::: -下个阶段所有消息被发送出去. 接收者的 `on_message()` 代码也可能包含消息发送, 所以消息会持续发送直至全部发送完成. 但是, 有一个发送消息最大轮数的限制. 详见 [消息传递教程](/manuals/message-passing). +在下一步中,所有已发布的消息都被分发。由于任何接收者组件的 `on_message()` 代码可以发布额外的消息,消息分发器将继续分发已发布的消息,直到消息队列为空。然而,消息分发器通过消息队列运行的次数是有限制的。有关详细信息,请参阅[消息链](/manuals/message-passing#message-chains)。 -对于碰撞对象组件, 物理消息 (collisions, triggers, ray_cast 响应等) 会被发送给所有含有 `on_message()` 函数的组件. +对于碰撞对象组件,物理消息(碰撞、触发器、ray_cast 响应等)被分发到包含具有 `on_message()` 函数的脚本的所有组件的整个游戏对象。 -然后进行位移操作, 对每个游戏对象及其子对象应用位置移动, 旋转和缩放. +然后进行变换,应用任何游戏对象移动、旋转和缩放到每个游戏对象组件以及任何子游戏对象组件。 ## 渲染更新 -渲染更新阶段发送消息到 `@render` 接口 (摄像机组件 `set_view_projection` 消息, `set_clear_color` 消息等). 渲染脚本的 `update()` 函数被调用. +渲染更新块向 `@render` 套接字分发消息(摄像机组件 `set_view_projection` 消息、`set_clear_color` 消息等)。然后调用渲染脚本 `update()`。 ## 后更新 -更新过程结束后, 一个后更新过程开始启动. 此时被标记为卸载的集合代理会从内存中卸载 (在 "dispatch messages" 阶段). 被标记为删除的游戏对象调用 `final()` 函数. 通常 `final()` 还包含消息发送代码所以接下来又是一轮 "dispatch messages" 阶段. +更新之后,运行后更新序列。它从内存中卸载标记为卸载的集合代理(这发生在"dispatch messages"序列期间)。任何标记为删除的游戏对象将调用其所有组件的 `final()` 函数(如果有的话)。`final()` 函数中的代码通常会向队列发布新消息,因此之后会运行"dispatch messages"传递。 -然后工厂实际创建对象. 最后, 被标记为删除的游戏对象被删除. +任何被告知生成游戏对象的工厂组件将在此执行此操作。最后,标记为删除的游戏对象实际上被删除。 -更新循环的最后一步是向 `@system` 发送消息 (`exit`, `reboot` 消息, 开关分析器, 开始结束视频捕获等等). 图像开始渲染. 与此同时, 视频捕获完成, 可视分析器渲染完成 (见 [调试教程](/manuals/debugging).) +更新循环中的最后一步涉及分发 `@system` 消息(`exit`、`reboot` 消息,切换分析器,启动和停止视频捕获等)。然后渲染图形。在图形渲染期间,进行视频捕获,以及视觉分析器的任何渲染(参见[调试文档](/manuals/debugging))。 ## 帧率和集合时间步 -每秒帧数 (即每秒更新循环运行次数) 可以在项目配置里设置, 或者通过发送 `set_update_frequency` 消息到 `@system` 接口手动设置. 而且, 可以通过发送 `set_time_step` 消息给代理来为集合单独设置 _时间步_. 修改集合时间步不影响帧率. 它影响的是物理时间步与发送到 `update()` 函数中的 `dt` 值. 注意修改时间步不影响 `update()` 调用的频率---永远是每帧调用一次. +每秒帧更新数(等于每秒更新循环运行次数)可以在项目设置中设置,或者通过向 `@system` 套接字发送 `set_update_frequency` 消息以编程方式设置。此外,可以通过向代理发送 `set_time_step` 消息来为集合代理单独设置_时间步_。更改集合的时间步不会影响帧率。它确实会影响物理更新时间步以及传递给 `update()` 的 `dt` 变量。还要注意,更改时间步不会改变每帧调用 `update()` 的次数——它总是恰好一次。 -(See the [Collection proxy manual](/manuals/collection-proxy) and [`set_time_step`](/ref/collectionproxy#set-time-step) for details) +(有关详细信息,请参阅[集合代理手册](/manuals/collection-proxy)和[`set_time_step`](/ref/collectionproxy#set-time-step)) -## 析构 +## 最终化 -应用退出时, 完成最后一次更新循环, 卸载所有集合代理: 析构和删除集合代理里的所有游戏对象. +当应用程序退出时,首先它完成最后的更新循环序列,这将卸载任何集合代理:最终化并删除每个代理集合中的所有游戏对象。 -然后引擎进入析构阶段处理启动集合里的内容: +当完成后,引擎进入处理主集合及其对象的最终化序列: ![Finalization](images/application_lifecycle/application_lifecycle_final.png) -组件的 `final()` 函数被调用. 后跟一轮消息传递. 最后, 所有游戏对象被删除, 启动集合被卸载. +首先调用组件 `final()` 函数。随后进行消息分发。最后,所有游戏对象都被删除,主集合被卸载。 -引擎还会继续做后续子系统处理: 删除项目配置, 关闭内存分析, 等等. +引擎随后在幕后进行子系统的关闭:项目配置被删除,内存分析器被关闭等等。 -至此应用正式退出. +现在应用程序已完全关闭。 diff --git a/docs/zh/manuals/application-security.md b/docs/zh/manuals/application-security.md index b15e5e1e..a39f55d2 100644 --- a/docs/zh/manuals/application-security.md +++ b/docs/zh/manuals/application-security.md @@ -1,35 +1,35 @@ --- -title: Defold 应用加密教程 -brief: 本教程涵盖了安全开发实践的若干方面. +title: 应用安全手册 +brief: 本手册涵盖了安全开发实践的若干方面。 --- -# 应用加密 +# 应用安全 -应用加密是一个很大的主题, 内容包括从安全开发实践到应用上线后的数据保护. 本教程涵盖了安全与加密的部分内容, 讨论在使用 Defold 引擎, 工具和服务时与安全加密相关的技术: +应用安全是一个广泛的主题,涵盖了从安全开发实践到游戏发布后保护游戏内容的各个方面。本手册将涵盖多个领域,并将其放在使用Defold引擎、工具和服务时的应用安全背景下: * 知识产权保护 * 反作弊解决方案 -* 网络通信安全 +* 安全网络通信 * 使用第三方软件 -* 使用云编译服务器 -* 可下载资源 +* 使用云构建服务器 +* 可下载内容 ## 保护您的知识产权免遭盗窃 -大多数开发人员关心的问题是如何保护他们的作品不被盗. 从法律的角度来看, 版权, 专利和商标有助于保护视频游戏知识产权的方方面面. 版权赋予开发者发表创意作品的专项权力, 专利保护创造发明, 商标保护名字, 符号和标志. +大多数开发者关心的问题是如何保护他们的创作不被盗用。从法律角度来看,版权、专利和商标可以用来保护视频游戏知识产权的不同方面。版权赋予其所有者分发创意作品的专有权,专利保护任何发明,而商标保护名称、符号和标志。 -为了保护游戏凝聚的创意和劳动, 技术手段也是有必要的. 要知道您的游戏一旦上市, 就无法阻止玩家想方设法提取游戏资源. 针对游戏文件的反向工程和各种动态提取工具都能搞到游戏资源, 因为无论如何, 贴图, 模型以及各种资源最终都要解压发送给 GPU 或者载入内存的. +采取技术预防措施来保护游戏的创意作品也是可取的。然而,重要的是要记住,一旦游戏到了玩家手中,就有可能找到提取资源的方法。这可以通过对游戏应用程序和文件进行逆向工程来实现,也可以通过使用工具在资源发送到GPU时或加载到内存时提取纹理和模型。 -出于这个原因, 我们的看法是, 不怕贼偷, 就怕贼惦记. +因此,我们的总体立场是,如果用户决心提取游戏的资源,他们将能够做到这一点。 -开发者可以提升游戏资源窃取的难度, __但永远不是不可能__. 这通常包括用各种手段加密, 混淆以达到保护和隐藏游戏资源的目的. +开发者可以添加自己的保护措施,使提取资源变得更加困难,__但并非不可能__。这通常包括各种加密和混淆手段,以保护和隐藏游戏资源。 ### 源代码混淆 -应用源代码混淆是一个故意降低代码可读性, 而不影响游戏输出的自动化过程. 目的不仅是为了防止被盗, 还能提高作弊的难度. +应用源代码混淆是一个自动化过程,其中源代码被故意设计为难以被人理解,同时不影响程序的输出。目的通常是为了防止被盗,同时也使作弊变得更加困难。 -可以在 Defold 编辑器里进行代码混淆, 这一步要么作为预编译步骤要么整合成为 Defold 编译工作中的一部分. 预编译混淆就是在 Defold 编译开始之前用某个工具给代码进行混淆操作. +在Defold中,可以应用源代码混淆,既可以作为预构建步骤,也可以作为Defold构建过程的集成部分。使用预构建混淆时,源代码在Defold构建过程开始之前使用混淆工具进行混淆。 -编译工作中混淆就是使用 Lua 编译插件把代码混淆作为编译工作的一部分. Lua 编译插件可以输入原始代码, 输出混淆后的代码. 具体示例可以参考 [Prometheus 扩展](https://github.com/defold/extension-prometheus), 它基于 GitHub 上的 Prometheus Lua 混淆器. 下面展示了使用 Prometheus 主动混淆一小段代码的例子 (注意这种高强度混淆会对 Lua 代码的运行效率产生影响): +另一方面,构建时混淆通过使用Lua构建器插件集成到构建过程中。Lua构建器插件将原始源代码作为输入,并返回混淆后的源代码作为输出。构建时混淆的一个示例在[Prometheus扩展](https://github.com/defold/extension-prometheus)中展示,该扩展基于GitHub上可用的Prometheus Lua混淆器。下面您将找到一个使用Prometheus积极混淆代码片段的示例(请注意,这种重度混淆会影响Lua代码的运行时性能): 示例: @@ -47,60 +47,63 @@ local v={"+qdW","ZK0tEKf=";"XP/IX3+="}for o,J in ipairs({{1;3};{1,1},{2,3}})do w ``` ### 资源加密 -在 Defold 编译过程中, 游戏资源会被处理为 Defold 引擎理解的格式. 贴图被编译成为 Basis Universal 格式, 碰撞器, 游戏对象和组件从原始可读文本被转化成为相应的二进制格式, Lua 源代码被编译为字节码. 其他资源比如音效则保持不变. +在Defold构建过程中,游戏资源被处理并转换为适合Defold引擎运行时使用的格式。纹理被编译为Basis Universal格式,集合、游戏对象和组件从人类可读的文本表示转换为对应的二进制格式,Lua源代码被处理并编译为字节码。其他资源(如声音文件)则按原样使用。 -处理完成后游戏资源会被一个一个地打入游戏包. 游戏包是个大号二进制文件, 其中每个资源的位置被存储在一个包索引文件中. 格式文档参见 [这里](https://github.com/defold/defold/blob/dev/engine/docs/ARCHIVE_FORMAT.md). +当这个过程完成后,资源会逐个添加到游戏存档中。游戏存档是一个大型二进制文件,存档中每个资源的位置存储在存档索引文件中。该格式在[这里](https://github.com/defold/defold/blob/dev/engine/docs/ARCHIVE_FORMAT.md)有文档说明。 -Lua 代码被打包前可以指定是否进行加密. Defold 提供了一种简单的分组加密算法用于保护代码中的字符串不至于被一些二进制预览工具打开就能看到. 这也不是很安全因为 Defold 已在 GitHub 上开源, 包括加密密钥. +在Lua源文件被添加到存档之前,它们也可以选择性地进行加密。Defold中提供的默认加密是一种简单的分组加密,用于防止在使用二进制文件查看器工具检查游戏存档时,代码中的字符串立即可见。由于Defold源代码在GitHub上可用,且加密密钥在源代码中可见,因此不应将其视为密码学安全的。 -允许通过实现一个资源加密插件来对 Lua 源代码实施自定义加密手段. 资源加密插件由两部分组成, 一个编译时对资源进行加密的部分和一个运行时对资源进行解密的部分. 作为基本应用的开端, 有一个简单的资源加密插件实现 [在 GitHub 上](https://github.com/defold/extension-resource-encryption). +可以通过实现资源加密插件为Lua源文件添加自定义加密。资源加密插件由构建时部分和运行时部分组成,构建时部分用于在构建过程中加密资源,运行时部分用于在从游戏存档中读取资源时解密资源。一个基本的资源加密插件(可用作您自己加密的起点)在[GitHub上可用](https://github.com/defold/extension-resource-encryption)。 -## 游戏防作弊 -游戏产业的历史就是作弊产业的历史. 以前作弊码常常被印刷在著名游戏杂志里, 有些外挂在早期家用电脑时代已经开始售卖. 随着游戏产业的进化, 作弊器和手段也在不断进化. 目前流行的作弊机制包括: +### 编码项目配置值 +*game.project*文件将原样包含在您的应用程序包中。有时您可能希望存储公共API访问密钥或类似的敏感但非私密性质的值。为了加强这类值的安全性,可以将它们包含在应用程序二进制文件中,而不是存储在*game.project*中,同时仍然可以通过Defold API函数(如`sys.get_config_string()`等)访问它们。您可以通过在*game.project*中添加原生扩展,并使用`DM_DECLARE_CONFIGFILE_EXTENSION`宏来提供您自己的覆盖,以使用Defold API函数获取配置值。一个可用作起点的示例项目在[GitHub上可用](https://github.com/defold/example-configfile-extension/tree/master)。 -* 注入作弊代码重新打包游戏 -* 能让游戏更快或更慢执行的速度齿轮外挂 -* 通过屏幕可视化分析实现的自动瞄准或自动行动机器人 -* 实时代码注入和内存数值修改以增加分数, 子弹数, 命数等等 +## 保护您的游戏免受作弊者侵害 +视频游戏作弊的历史与游戏行业本身一样悠久。作弊码曾经流行于视频游戏杂志中,特殊的作弊盒卡带也在早期的家用电脑上销售。随着行业和游戏的演变,作弊者和他们的方法也在不断演变。一些最流行的游戏作弊机制包括: -防止作弊很难, 难到接近不可能. 即使是云游戏, 那种运行在远程云服务器上, 然后把输出串流到用户设备上的游戏, 都不能完全杜绝作弊. +* 重新打包游戏内容以注入自定义逻辑 +* 速度黑客,使游戏运行速度比正常更快或更慢 +* 自动化和视觉分析,用于自动瞄准和机器人 +* 代码和内存注入,以修改分数、生命值、弹药等 -Defold 不提供任何引擎内置反作弊方案或者工具, 而是把这些工作外包给专门做游戏反作弊技术的公司. +防止作弊很困难,几乎是不可能的。即使是云游戏,即游戏在远程服务器上运行并直接流式传输到用户设备,也不能完全免受作弊的影响。 +Defold在引擎或工具中不提供任何反作弊解决方案,而是将这类工作交给众多专门为游戏提供反作弊解决方案的公司之一。 -## 网络通信安全 -Defold 套接字和 HTTP 通信支持使用安全套接字连接. 在客户端与服务器的双向通讯中, 推荐全部使用安全套接字连接以进行身份验证并保证任何交换数据的隐私和完整. Defold 使用流行并被广泛使用的 [Mbed TLS](https://github.com/Mbed-TLS/mbedtls) 作为 TLS 和 SSL 协议的实现. Mbed TLS 由 ARM 及其伙伴科技公司所开发. -### SSL 证书验证 -为防止黑客在您的网络交流中实施中间人攻击, 可以在与服务器进行连接时的 SSL 握手期间验证证书链. 通过 Defold 向网络客户端提供公钥列表即可. 关于网络通信安全的更多信息请参阅 [网络教程](https://defold.com/manuals/networking/#secure-connections) 的 SSL 验证章节. +## 保护您的网络通信 +Defold套接字和HTTP通信支持安全套接字连接。建议对任何服务器通信使用安全连接,以验证服务器并保护在客户端到服务器以及服务器到客户端传输过程中任何交换数据的隐私和完整性。Defold使用流行且广泛采用的TLS和SSL协议开源实现[Mbed TLS](https://github.com/Mbed-TLS/mbedtls)。Mbed TLS由ARM及其技术合作伙伴开发。 +### SSL证书验证 +为了防止网络通信中的中间人攻击,可以在与服务器协商连接时的SSL握手期间验证证书链。这可以通过在Defold中向网络客户端提供公钥列表来实现。有关保护网络通信的更多信息,请阅读[网络手册](https://defold.com/manuals/networking/#secure-connections)中关于SSL验证的部分。 -## 第三方软件使用安全 -虽然不用第三方库或者原生扩展也能做游戏, 但是从我们的官方 [资源大厅](https://defold.com/assets/) 获取资源提升开发效率已成为开发者的共识. 资源大厅里有超多资源, 从第三方 SDK 整合包, 到屏幕管理, UI 库, 摄像机控制等等等等. -资源大厅里的资源未经 Defold 基金会审核, Defold 基金会不为使用它们造成的计算机系统损坏或设备损坏或数据丢失等任何后果负责. 详见我们的 [条款与条件](https://defold.com/terms-and-conditions/#3-no-warranties). +## 保护您对第三方软件的使用 +虽然创建游戏不一定需要使用任何第三方库或原生扩展,但使用官方[资源门户](https://defold.com/assets/)中的资源来加速开发已成为开发者的常见做法。资源门户包含大量资源,从第三方SDK集成到屏幕管理器、UI库、摄像机等等。 -我们建议您在使用这些资源之前仔细审核, 确认资源适用于您的项目之后马上把它拷贝一份出来, 以保证它不会受到未来更新造成的未经审核的改变. +资源门户中的任何资源都未经Defold基金会审核,我们不对通过资源门户获取的任何资源使用导致的计算机系统或其他设备损坏或数据丢失负责。您可以在我们的[条款与条件](https://defold.com/terms-and-conditions/#3-no-warranties)中阅读细则。 +我们建议您在使用任何资源之前进行审查,一旦您认为它适合在您的项目中使用,就创建该资源的分支或副本,以确保它不会在您不知情的情况下发生变化。 -## 云编译服务器的使用安全 -Defold 云编译服务器 (同时也是扩展编译服务器) 用于帮助开发者为游戏增加功能而不用重新编译 Defold 引擎. 当包含原生代码的 Defold 项目被第一次编译时, 所有相关资源都会被上传到云编译服务器用来编译一个自定义版本的 Defold 引擎然后回传给开发者. 类似流程也会发生于当项目使用自定义 application manifest 剔除了不必要的引擎组件时. -云编译服务器被托关于 AWS 并且尽最大可能做好了安全工作. 然而 Defold 基金并不承诺云编译服务器完全满足您的需求, 没有缺陷, 没有病毒, 没有错误, 绝对安全或者永远在线, 使用安全. 详见我们的 [条款与条件](https://defold.com/terms-and-conditions/#3-no-warranties). +## 保护您对云构建服务器的使用 +Defold云构建服务器(也称为扩展器服务器)的创建是为了帮助开发者为Defold引擎添加新功能,而无需重新构建引擎本身。当包含原生代码的Defold项目首次构建时,原生代码及任何相关资源会被发送到云构建服务器,在那里创建一个自定义版本的Defold引擎并返回给开发者。当项目使用自定义应用程序清单来移除引擎中未使用的组件时,也会应用相同的过程。 -如果安全性和可用性对您来说非常重要, 我们建议您自己架设私有编译服务器. 假设私有编译服务器的方法详见 GitHub 上扩展仓库的 [主 readme 文件](https://github.com/defold/extender). +云构建服务器托管在AWS上,并根据安全最佳实践创建。然而,Defold基金会不保证云构建服务器将满足您的要求,没有缺陷、没有病毒、安全或没有错误,或者您对服务器的使用将是不间断或安全的。您可以在我们的[条款与条件](https://defold.com/terms-and-conditions/#3-no-warranties)中阅读细则。 +如果您对构建服务器的安全性和可用性感到担忧,我们建议您设置自己的私有构建服务器。有关如何设置自己的服务器的说明可以在GitHub上扩展器仓库的[主readme文件](https://github.com/defold/extender)中找到。 -## 可下载资源的安全 -Defold 的热更新系统允许开发者把主游戏包的部分内容划分出来以便随用随下. 典型的用例就是随着玩家游戏进程的推进, 按需下载关卡, 地图或者游戏世界. -这些被划分出来的更新包被下载好准备用于游戏的时候, 引擎会对它们进行加密验证已确定它们没有被篡改. 该验证包括多项检查: +## 保护您的可下载内容 +Defold的Live Update系统允许开发者将内容从主游戏包中排除,以便稍后下载和使用。典型的用例是随着玩家在游戏中的进展下载额外的关卡、地图或世界。 -* 二进制格式是否正确? -* 当前运行的引擎版本是否支持下载的内容? -* 下载的内容是否使用了正确的公私钥进行签名? -* 下载的内容是否完整且没有遗漏任何文件? +当被排除的内容被下载并准备在游戏中使用时,引擎会在使用前对其进行加密验证,以确保它没有被篡改。验证包括多项检查: -该过程的详细步骤请参考 [热更新教程](https://defold.com/manuals/live-update/#manifest-verification). +* 二进制格式是否正确? +* 当前运行的引擎版本是否支持下载的内容? +* 下载的内容是否使用了正确的公私钥对进行签名? +* 下载的内容是否完整且没有遗漏任何文件? + +您可以在[Live Update手册](https://defold.com/manuals/live-update/#manifest-verification)中阅读有关此过程的更多信息。 diff --git a/docs/zh/manuals/atlas.md b/docs/zh/manuals/atlas.md index 709a19e1..8c3f21c3 100644 --- a/docs/zh/manuals/atlas.md +++ b/docs/zh/manuals/atlas.md @@ -1,215 +1,233 @@ --- -title: 图集教程 -brief: 本教程介绍了 Defold 中图集资源是如何工作的. +title: 图集手册 +brief: 本手册介绍了 Defold 中图集资源是如何工作的。 --- -# Atlas +# 图集 -sprites 通常使用单个的小图片, 但是处于性能考虑, 最好把小图片合并成大图, 称为图集. 相比桌面设备和游戏机, 手机这种性能不太高的地方, 合并小图的做法就十分必要了. +虽然单个图像通常用作精灵的源,但出于性能考虑,图像需要合并成更大的图像集,称为图集。将较小的图像集合并成图集在移动设备上尤其重要,因为与桌面计算机或专用游戏机相比,移动设备的内存和处理能力更为稀缺。 -在 Defold 中, 图集资源由一系列单个图片组成, 这些小图最终会自动合并成大图. +在 Defold 中,图集资源是一个单独图像文件的列表,这些文件会自动合并成一个更大的图像。 ## 创建图集 -在 *Assets* 浏览器右键菜单中选择 New... ▸ Atlas. 命名图集文件. 图集编辑器会自动打开. -*Properties* 面板会显示出图集的可编辑属性 (详见下文). +在 *Assets* 浏览器中的上下文菜单中选择 New... ▸ Atlas。命名新的图集文件。编辑器现在将在图集编辑器中打开该文件。图集属性显示在 +*Properties* 面板中,因此您可以编辑它们(详见下文)。 -先要向图集里填入图片或者动画才能在 Sprites 和 ParticleFX 之类的可视组件里使用. +您需要先用图像或动画填充图集,然后才能将其用作精灵和粒子效果组件等对象组件的图形源。 -确保你需要的图片都存在项目里 (把图片文件拖放到 *Assets* 浏览器的适当位置). +确保您已将图像添加到项目中(将图像文件拖放到 *Assets* 浏览器中的正确位置)。 -加入单张图片 -: 在 *Outline* 面板上 右键点击 图集根节点. +添加单个图像 + +: 从 *Asset* 面板将图像拖放到编辑器视图中。 - 从弹出菜单中选择 Add Images 来加入单张图片. + 或者,在 *Outline* 面板中 右键点击 根 Atlas 条目。 + + 从弹出的上下文菜单中选择 Add Images 以添加单个图像。 - 此时会弹出选择图片的菜单. 注意此菜单支持文件名过滤和多选功能. + 将打开一个对话框,您可以从中查找并选择要添加到 Atlas 的图像。请注意,您可以过滤图像文件并一次选择多个文件。 ![Creating an atlas, adding images](images/atlas/add.png) - 被加入的图片会显示在 *Outline* 列表里, 而且编辑器里也会显示出图片合成的图集. 可以按 F键 (菜单栏 View ▸ Frame Selection) 来居中显示. + 添加的图像列在 *Outline* 中,完整的图集可以在中心编辑器视图中看到。您可能需要按 F(从菜单中选择 View ▸ Frame Selection)来框选选择。 ![Images added](images/atlas/single_images.png) -加入逐帧动画 -: 在 *Outline* 面板上 右键点击 图集根节点. +添加翻书动画 - 从弹出菜单中选择 Add Animation Group 来加入逐帧动画. +: 在 *Outline* 面板中 右键点击 根 Atlas 条目。 - 一个默认命名为 ("New Animation") 的新建空动画组就被加入图集了. + 从弹出的上下文菜单中选择 Add Animation Group 以创建翻书动画组。 - 右键点击 动画组, 选择 Add Images 加入来图片. + 一个新的、空的、具有默认名称("New Animation")的动画组被添加到图集中。 - 同样会弹出选择图片菜单, 选择的图片都会被加入到动画组. + 从 *Asset* 面板将图像拖放到编辑器视图中,将它们添加到当前选定的组中。 + 或者,右键点击 新组并从上下文菜单中选择 Add Images。 + + 将打开一个对话框,您可以从中查找并选择要添加到动画组的图像。 + ![Creating an atlas, adding images](images/atlas/add_animation.png) - 选中动画组后按 空格键 即可预览动画, Ctrl/Cmd+T 关闭预览. 动画 *Properties* 可以自由修改 (见下文). + 选定动画组后按 空格键 预览,按 Ctrl/Cmd+T 关闭预览。根据需要调整动画的 *Properties*(见下文)。 ![Animation group](images/atlas/animation_group.png) -选中图片后按 Alt + Up/down 可以更改顺序. 也可以拷贝粘贴任意图片 (通过 Edit 菜单栏, 右键菜单或者快捷键). +您可以通过选择图像并按 Alt + Up/down 来重新排列 Outline 中的图像。您还可以通过在 Outline 中复制和粘贴图像(从 Edit 菜单、右键上下文菜单或键盘快捷键)轻松创建副本。 ## 图集属性 -图集资源有一系列属性. 在 *Outline* 视图中选中图集后属性出现在 *Properties* 面板中. +当您在 *Outline* 面板中选择图集根节点时,可以在 *Properties* 面板中编辑以下属性: -Size -: 图集空间占用大小. 宽高取2的整数幂. 注意如果开启了纹理压缩, 某些格式需要纹理为正方形. 非正方形纹理将加入空白以建立正方形. 详情请见 [Texture profiles 教程](/manuals/texture-profiles/). +![Atlas properties](images/atlas/atlas_properties.png) -Margin -: 每两个图片之间的间隙. +*Size* -Inner Padding -: 每个图片四周加入的空白. +: 图集输出图像的尺寸。图集构建器将尝试将所有图像打包到此尺寸的图像中。如果图像无法适应,将创建多个输出图像。默认尺寸为 2048x2048 像素。 -Extrude Borders -: 每个图片四周的边缘挤出. 片元着色器采样图片边缘的时候, 相邻图片 (同个图集) 边缘可能会被采集到. 挤出边缘就可以解决这个问题. +*Margin* -Max Page Size -: 多页图集的最大尺寸. 可以用来把一个图集切分成多页来限制图集尺寸同时仍然只用一个 draw call. 它必须与`/builtins/materials/*_paged_atlas.material` 里开启 multi-page atlas enabled materials 一起使用. +: 添加到每个图像边缘的空白区域(以像素为单位)。这有助于防止图像在缩放时边缘像素被"污染"。默认值为 0。 -![Multi-page atlas](images/atlas/multipage_atlas.png) +*Inner Padding* -Rename Patterns -: 以逗号 (´,´) 分隔的搜索和替换用的表达式列表, 每个表达式的形式为 `search=replace`. -图片的原始名字 (文件名) 会用这些表达式改变. (比如 表达式 `hat=cat,_normal=`, 会重命名 `hat_normal` 为 `cat`). 这在多图集间匹配动画时很有用. +: 添加到每个图像内部的空白区域(以像素为单位)。这有助于防止图像在缩放时相邻像素被"污染"。默认值为 0。 -这里用四个 64x64 正方形图片做图集不同属性设置的演示. 注意这里图集一旦超过 128x128 就会跳到 256x256, 从而造成了资源浪费. +*Extrude Borders* -![Atlas properties](images/atlas/atlas_properties.png) +: 如果启用,将复制每个图像的边缘像素并扩展图像。这有助于防止图像在缩放时边缘像素被"污染"。默认值为 false。 -## 图片属性 +*Border Padding* -图集中的每个图片都有一系列属性: +: 添加到整个图集边缘的空白区域(以像素为单位)。这有助于防止图集在缩放时边缘像素被"污染"。默认值为 0。 -Id -: 图片名称 (只读). +*Page Width/Height* -Size -: 图片宽高 (只读). +: 图集输出图像的尺寸。图集构建器将尝试将所有图像打包到此尺寸的图像中。如果图像无法适应,将创建多个输出图像。默认尺寸为 2048x2048 像素。 -Sprite Trim Mode -: 决定sprite如何渲染. 默认以矩形渲染 (Sprite Trim Mode 为 Off). 如果图片由许多透明的地方最好用4个或8个顶点设定非矩形渲染. +*Texture Compression* -Image -: 图片路径. +: 用于图集输出图像的纹理压缩格式。默认值为 "None"。 + +*Output Format* + +: 图集输出图像的格式。默认值为 "PNG"。 + +*Output Path* + +: 图集输出图像的路径。默认值为 "atlas"。 + +## 图片属性 + +当您在 *Outline* 面板中选择单个图像时,可以在 *Properties* 面板中编辑以下属性: ![Image properties](images/atlas/image_properties.png) +*Image* + +: 图像的路径。您可以通过点击此字段并从文件浏览器中选择新图像来更改图像。 + +*Trim Mode* + +: 图像的修剪模式。默认值为 "None"。 + +*Trim Threshold* + +: 图像的修剪阈值。默认值为 0。 + +*Trim Margin* + +: 图像的修剪边距。默认值为 0。 + +*Pivot* + +: 图像的轴心点。默认值为 "Center"。 + +*Extrude Borders* + +: 如果启用,将复制图像的边缘像素并扩展图像。这有助于防止图像在缩放时边缘像素被"污染"。默认值为 false。 + +*Inner Padding* + +: 添加到图像内部的空白区域(以像素为单位)。这有助于防止图像在缩放时相邻像素被"污染"。默认值为 0。 + +*Border Padding* + +: 添加到图像边缘的空白区域(以像素为单位)。这有助于防止图像在缩放时边缘像素被"污染"。默认值为 0。 + ## 动画属性 -动画组除了组成动画的图片, 还提供了一些属性: +当您在 *Outline* 面板中选择动画组时,可以在 *Properties* 面板中编辑以下属性: + +![Animation properties](images/atlas/animation_properties.png) + +*Id* + +: 动画的标识符。您可以通过点击此字段并输入新名称来更改动画的名称。 + +*Fps* + +: 动画的帧率。默认值为 30。 + +*Flip Horizontal* + +: 如果启用,动画将水平翻转。默认值为 false。 + +*Flip Vertical* -Id -: 动画名称. +: 如果启用,动画将垂直翻转。默认值为 false。 -Fps -: 动画播放速率, 以帧每秒 (FPS) 表示. +*Playback* -Flip horizontal -: 动画水平翻转. +: 动画的播放模式。默认值为 "Once Forward"。 -Flip vertical -: 动画垂直翻转. +*Width* -Playback -: 设置动画播放方式: +: 动画组的宽度(以像素为单位)。默认值为 0。 - - `None` 不播放, 只显示第一张图片. - - `Once Forward` 从第一张图片到最后一张图片播放一次. - - `Once Backward` 从最后一张图片到第一张图片播放一次. - - `Once Ping Pong` 从第一张图片播放到最后一张图片再反向播放一次. - - `Loop Forward` 从第一张图片到最后一张图片循环播放. - - `Loop Backward` 从最后一张图片到第一张图片循环播放. - - `Loop Ping Pong` 从第一张图片播放到最后一张图片再反向循环播放. +*Height* + +: 动画组的高度(以像素为单位)。默认值为 0。 + +*Origin X/Origin Y* + +: 动画组的原点。默认值为 0。 ## 运行时纹理及图集建立 -自从 Defold 1.4.2 版本, 可以在运行时创建纹理和图集. +Defold 支持在运行时动态创建纹理和图集。这对于动态加载资源、生成程序内容或实现动态纹理打包等功能非常有用。 -### 在运行时创建纹理资源 +### 运行时纹理 -使用 [`resource.create_texture(path, params)`](https://defold.com/ref/stable/resource/#resource.create_texture:path-table) 创建纹理资源: +您可以使用 `resource.create_texture()` 函数在运行时创建纹理。此函数接受一个表格参数,其中包含纹理的宽度、高度、类型和格式等信息。 ```lua - local params = { - width = 128, - height = 128, - type = resource.TEXTURE_TYPE_2D, +local texture_params = { + width = 256, + height = 256, + type = resource.TEXTURE_TYPE_2D, format = resource.TEXTURE_FORMAT_RGBA, - } - local my_texture_id = resource.create_texture("/my_custom_texture.texturec", params) +} +local texture_id = resource.create_texture(texture_params) ``` -纹理创建好之后就可以用 [`resource.set_texture(path, params, buffer)`](https://defold.com/ref/stable/resource/#resource.set_texture:path-table-buffer) 设置纹理的像素: +创建纹理后,您可以使用 `resource.set_texture()` 函数更新纹理的内容。 ```lua - local width = 128 - local height = 128 - local buf = buffer.create(width * height, { { name=hash("rgba"), type=buffer.VALUE_TYPE_UINT8, count=4 } } ) - local stream = buffer.get_stream(buf, hash("rgba")) - for y=1, height do - for x=1, width do - local index = (y-1) * width * 4 + (x-1) * 4 + 1 - stream[index + 0] = 0xff - stream[index + 1] = 0x80 - stream[index + 2] = 0x10 - stream[index + 3] = 0xFF - end - end - - local params = { width=width, height=height, x=0, y=0, type=resource.TEXTURE_TYPE_2D, format=resource.TEXTURE_FORMAT_RGBA, num_mip_maps=1 } - resource.set_texture(my_texture_id, params, buf) +local buffer = "..." -- 包含纹理数据的缓冲区 +resource.set_texture(texture_id, buffer) ``` -::: sidenote -可以使用 `resource.set_texture()` 更新局部纹理, 方法是设置 buffer 的 width 和 height 小于纹理完整尺寸, 然后指定 `resource.set_texture()` 的 x 和 y 参数. -::: +### 运行时图集 -纹理可以用 `go.set()` 直接应用于 [模型组件](/manuals/model/) 上: +您可以使用 `resource.create_atlas()` 函数在运行时创建图集。此函数接受一个表格参数,其中包含图集的图像列表和动画列表等信息。 ```lua - go.set("#model", "texture0", my_texture_id) +local atlas_params = { + images = { + { id = "image1", x = 0, y = 0, width = 64, height = 64 }, + { id = "image2", x = 64, y = 0, width = 64, height = 64 }, + }, + animations = { + { id = "animation1", fps = 30, playback = resource.PLAYBACK_ONCE_FORWARD, frames = { "image1", "image2" } }, + }, +} +local atlas_id = resource.create_atlas(atlas_params) ``` -### 运行时创建图集 - -如果纹理要用在 [sprite 组件](/manuals/sprite/) 上, 要先转换成图集. 使用 [`resource.create_atlas(path, params)`](https://defold.com/ref/stable/resource/#resource.create_atlas:path-table) 创建图集: +创建图集后,您可以使用 `resource.set_atlas()` 函数更新图集的内容。 ```lua - local params = { - texture = texture_id, - animations = { - { - id = "my_animation", - width = width, - height = height, - frame_start = 1, - frame_end = 2, - } - }, - geometries = { - { - vertices = { - 0, 0, - 0, height, - width, height, - width, 0 - }, - uvs = { - 0, 0, - 0, height, - width, height, - width, 0 - }, - indices = {0,1,2,0,2,3} - } - } - } - local my_atlas_id = resource.create_atlas("/my_atlas.texturesetc", params) - -- 给当前游戏对象上的 'sprite' 组件指定图集 - go.set("#sprite", "image", my_atlas_id) - -- 播放 "逐帧动画" - sprite.play_flipbook("#sprite", "my_animation") -``` \ No newline at end of file +local buffer = "..." -- 包含图集数据的缓冲区 +resource.set_atlas(atlas_id, buffer) +``` + +## 相关手册 + +- [Sprite 手册](/manuals/sprite) +- [粒子效果手册](/manuals/particlefx) +- [Tile Source 手册](/manuals/tilesource) +- [材质手册](/manuals/material) +- [纹理配置文件手册](/manuals/texture-profiles) +- [资源手册](/manuals/resource) \ No newline at end of file diff --git a/docs/zh/manuals/bob.md b/docs/zh/manuals/bob.md index 27ebefa0..169618f1 100644 --- a/docs/zh/manuals/bob.md +++ b/docs/zh/manuals/bob.md @@ -1,25 +1,25 @@ --- -title: Defold 项目编译教程 -brief: Bob 是用于 Defold 项目的命令行编译工具. 本教程详述如何使用这个工具. +title: Defold 项目构建器手册 +brief: Bob 是用于构建 Defold 项目的命令行工具。本手册详述如何使用这个工具。 --- -# 编译器 Bob +# 构建器 Bob -Bob 是一个用于Defold项目编辑器之外的命令行编译工具. +Bob 是一个命令行工具,用于在正常编辑器工作流程之外构建 Defold 项目。 -Bob 用来编译操作 (对应编辑器里的 Project ▸ Build), 来创建数据档或者创建可独立发布的应用 (对应编辑器里的 Project ▸ Bundle ▸ ... 选项) +Bob 能够构建数据(对应于选择编辑器菜单项 Project ▸ Build 的构建步骤),创建数据存档,并创建独立的、可分发的应用程序包(对应于编辑器菜单项 Project ▸ Bundle ▸ ... 选项)。 -Bob 集合了编译所需的一切, 作为Java包 _JAR_ 发布. 最新的 *bob.jar* 发布在 [Defold 下载页](http://d.defold.com) 和 [GitHub 发布页](https://github.com/defold/defold/releases) 上. 选择一个版本, 下载 *bob/bob.jar*. 如果你使用的是 Defold 1.9.6, 您需要安装 OpenJDK 21. 对于 Defold 老版本, 你需要 openJDK 17. +Bob 作为 Java _JAR_ 存档分发,其中包含构建所需的一切。您可以在 [GitHub 发布页面](https://github.com/defold/defold/releases) 上找到最新的 *bob.jar* 分发版本。选择一个发布版本,然后下载 *bob/bob.jar*。如果您使用的是 Defold 1.9.6,您将需要 OpenJDK 21 来运行它。对于旧版本的 Defold,您将需要 OpenJDK 17 或 11。 -兼容 OpenJDK 21 镜像 (自从 Defold 1.9.6): -* https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-21 -* https://github.com/adoptium/temurin21-binaries/releases / https://adoptium.net/ +兼容的 OpenJDK 21 镜像(从 Defold 1.9.6 开始): +* [Microsoft 提供的 OpenJDK 21](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-21) +* [Adoptium 工作组提供的 OpenJDK 21](https://github.com/adoptium/temurin21-binaries/releases) / [Adoptium.net](https://adoptium.net/) -比如在 Windows 平台上, 需要下载 OpenJDK 21 的 .msi 安装包. +如果您在 Windows 上,您需要 OpenJDK 的 `.msi` 文件安装程序。 ## 用法 -Bob 运行于命令行界面 `java` (再Windows上是 `java.exe`) 后跟bob的jar包作为参数: +Bob 通过在 shell 或命令行中调用 `java`(在 Windows 上是 `java.exe`)并提供 bob java 存档作为参数来运行: ```text $ java -jar bob.jar --help @@ -144,55 +144,55 @@ usage: bob [options] [commands] applicable) ``` -支持的命令: +可用命令: `clean` -: 清空编译目录下的编译文件. +: 删除构建目录中已构建的文件。 `distclean` -: 清空编译目录下的所有文件. +: 删除构建目录中的所有文件。 `build` -: 编译所有项目文件. 加入 `--archive` 选项可生成编译数据包 (编译目录下生成 "game.darc" 文件). +: 构建所有项目数据。添加 `--archive` 选项以构建数据存档文件(构建目录中的 "`game.darc`")。 `bundle` -: 指定平台打包. 打包需要数据包已经编译生成 (`build` 加入 `--archive` 选项) 然后指定打包平台 (使用 `--platform` 选项). Bob 会把应用打包到编译目录下, 除非使用 `--bundle-output` 选项手动指定打包输出目录. 包名根据 *game.project* 文件中设置的项目名命名. 使用 `--variant` 指定打何种运行类型的包, 连同 `--strip-executable` 选项代替了老的 `--debug` 选项. 如果 `--variant` 没有指定, 默认时release类型的 (去除 Android 和 iOS 的debug信息). 把 `--variant` 设置为 debug 而省略 `--strip-executable` 选项, 就相当于老的 `--debug`选项. +: 创建特定于平台的应用程序包。打包需要存在已构建的存档(使用 `--archive` 选项的 `build`)并指定目标平台(使用 `--platform` 选项)。除非使用 `--bundle-output` 选项指定不同的目录,否则 Bob 会在输出目录中创建包。包根据 *game.project* 中的项目名称设置命名。`--variant` 指定打包时构建哪种类型的可执行文件,它与 `--strip-executable` 选项一起取代了 `--debug` 选项。如果没有指定 `--variant`,您将获得引擎的发布版本(在 Android 和 iOS 上剥离符号)。将 `--variant` 设置为 debug 并省略 `--strip-executable` 会产生与 `--debug` 过去相同的可执行文件类型。 `resolve` -: 解析所有外部依赖库. +: 解析所有外部库依赖项。 -支持平台和架构: +可用平台和架构: -`x86_64-darwin` (Defold 1.3.5 及更老版本) +`x86_64-darwin` (Defold 1.3.5 及更早版本) `x86_64-macos` (Defold 1.3.6 及更新版本) -: macOS 64 bit +: macOS 64 位 -`arm64-macos` (Defold 1.5.0 and older) +`arm64-macos` (Defold 1.5.0 及更早版本) : macOS Apple Silicon (ARM) `x86_64-win32` -: Windows 64 bit +: Windows 64 位 `x86-win32` -: Windows 32 bit +: Windows 32 位 `x86_64-linux` -: Linux 64 bit +: Linux 64 位 `x86_64-ios` -: iOS macOS 64 bit (iOS 模拟器) +: iOS macOS 64 位 (iOS 模拟器) -`armv7-darwin` (Defold 1.3.5 及更老版本) +`armv7-darwin` (Defold 1.3.5 及更早版本) `armv7-ios` (Defold 1.3.6 及更新版本) -: iOS 支持 32-bit `armv7-darwin` 和 64-bit `arm64-darwin` 架构. 默认情况下, `--architectures` 参数值为 `armv7-darwin,arm64-darwin`. +: iOS,具有可用的 32 位 `armv7-darwin` 和 64 位 `arm64-darwin` 架构。默认情况下,`--architectures` 参数值为 `armv7-darwin,arm64-darwin`。 `armv7-android` -: Android 支持 32 bit `armv7-android` 和 64 bit `arm64-android` 架构. 默认情况下, `--architectures` 参数值为 `armv7-android,arm64-android`. +: Android,具有可用的 32 位 `armv7-android` 和 64 位 `arm64-android` 架构。默认情况下,`--architectures` 参数值为 `armv7-android,arm64-android`。 `js-web` -: HTML5 支持 `js-web` 和 `wasm-web` 架构. 默认情况下, `--architectures` 参数值为 `js-web,wasm-web`. +: HTML5,具有可用的 `js-web` 和 `wasm-web` 架构。默认情况下,`--architectures` 参数值为 `js-web,wasm-web`。 -默认情况下, Bob 在当前目录下寻找项目来编译. 切换到 Defold 项目目录下使用 bob, 它会把数据编译到默认输出 *build/default* 目录下. +默认情况下,Bob 在当前目录中寻找要构建的项目。如果您将当前目录更改为 Defold 项目并调用 bob,它将在默认输出目录 *build/default* 中构建项目的数据。 ```sh $ cd /Applications/Defold-beta/branches/14/4/main @@ -201,7 +201,7 @@ $ java -jar bob.jar $ ``` -还可以把命令连成一行一起执行. 下面的例子包含了解析库, 清理编译目录, 编译数据包然后打包成 macOS 应用 (命名为 *My Game.app*): +您可以将命令串联在一起以一次性执行一系列任务。以下示例解析库,清除构建目录,构建存档数据并将 macOS 应用程序捆绑(命名为 *My Game.app*): ```sh $ java -jar bob.jar --archive --platform x86-darwin resolve distclean build bundle diff --git a/docs/zh/manuals/buffer.md b/docs/zh/manuals/buffer.md index ffcdba0f..ea01c3be 100644 --- a/docs/zh/manuals/buffer.md +++ b/docs/zh/manuals/buffer.md @@ -1,11 +1,11 @@ --- -title: 缓存教程 -brief: 本教程介绍了 Defold 的缓存资源. +title: Buffer手册 +brief: 本手册介绍了Defold中的Buffer资源的工作原理. --- # Buffer -缓存资源用来描述一个或多个数据流, 比如位置或颜色. 每种流有名字, 数据类型, 数目及数据自身. 例如: +Buffer资源用于描述一个或多个值流,例如位置或颜色。每个流都有名称、数据类型、计数和数据本身。示例: ``` [ @@ -26,8 +26,8 @@ brief: 本教程介绍了 Defold 的缓存资源. ] ``` -上例描述了三维位置数据流, 用 32-bit 浮点数表示. 缓存类型是 JSON, 文件扩展名是 `.buffer`. +上面的示例描述了三维位置流,表示为32位浮点数。Buffer文件的格式为JSON,文件扩展名为`.buffer`。 -缓存资源一般由扩展工具或脚本创建, 比如用 Blender 导出模型时创建. +Buffer资源通常使用外部工具或脚本创建,例如从Blender等建模工具导出时。 -缓存资源可以用作 [模型资源](/manuals/mesh) 的数据. 缓存资源还可以使用 `buffer.create()` 和 [相关 API 函数](/ref/stable/buffer/#buffer.create:element_count-declaration) 在运行时创建. \ No newline at end of file +Buffer资源可用作[Mesh组件](/manuals/mesh)的输入。也可以使用`buffer.create()`和[相关API函数](/ref/stable/buffer/#buffer.create:element_count-declaration)在运行时创建Buffer资源。 \ No newline at end of file diff --git a/docs/zh/manuals/building-blocks.md b/docs/zh/manuals/building-blocks.md index 7735f012..0a8c8569 100644 --- a/docs/zh/manuals/building-blocks.md +++ b/docs/zh/manuals/building-blocks.md @@ -1,65 +1,65 @@ --- -title: Defold 构成 -brief: 本教程详述游戏对象, 组件和集合是如何工作的. +title: Defold的构建块 +brief: 本手册深入探讨游戏对象、组件和集合的工作原理细节。 --- -# 构成 +# 构建块 -理解 Defold 核心设计中的各种概念是很重要的. 本教程介绍了 Defold 游戏的各个组成部分. 看完本教程后, 可以去参考 [定位教程](/manuals/addressing) 和 [消息传递教程](/manuals/message-passing). 编辑器中提供了一些 [教程](/tutorials/getting-started) 也可以帮助学习理解. +Defold设计的核心是几个非常重要的概念,掌握这些概念至关重要。本手册解释了Defold的构建块由什么组成。阅读本手册后,请继续阅读[定位手册](/manuals/addressing)和[消息传递手册](/manuals/message-passing)。编辑器中还提供了一套[教程](/tutorials/getting-started),可以帮助您快速上手。 ![Building blocks](images/building_blocks/building_blocks.png) -Defold 游戏主要由三大部分组成: +您可以使用三种基本类型的构建块来构建Defold游戏: Collection -: 集合文件构成了你的游戏. 集合可以包含具有嵌套关系的游戏对象与其他集合. 可以用来构建诸如关卡, 敌人队伍, 多个游戏对象嵌套组成的一个角色之类的各种内容. +: 集合是用于构建游戏结构的文件。在集合中,您可以构建游戏对象和其他集合的层次结构。它们通常用于构建游戏关卡、敌人群体或由多个游戏对象组成的角色。 Game object -: 游戏对象是一个带 id 的容器, 具有位置, 旋转和缩放. 用来容纳组件. 可以用来构建主角, 子弹, 游戏逻辑或者资源加载/卸载程序. +: 游戏对象是具有ID、位置、旋转和缩放的容器。它用于容纳组件。它们通常用于创建玩家角色、子弹、游戏规则系统或关卡加载器。 Component -: 组件被放置在游戏对象中用来产生游戏里可视, 可听, 可运行的东西. 可以用来构建 sprite, 脚本, 音效或者粒子特效. +: 组件是放置在游戏对象中的实体,为游戏对象提供视觉、听觉和/或逻辑表示。它们通常用于创建角色精灵、脚本文件、添加音效或添加粒子效果。 ## 集合 -集合是包含嵌套游戏对象和其他集合的树形结构. 通常集合作为文件保存于项目中. +集合是包含游戏对象和其他集合的树形结构。集合总是存储在文件中。 -Defold 游戏引擎启动时, 首先导入一个 *game.project* 配置文件中指定的 _启动集合_. 启动集合一般叫做 "main.collection", 当然也可以根据喜好随意设置. +当Defold引擎启动时,它会加载一个在*game.project*设置文件中指定的_引导集合_。引导集合通常命名为"main.collection",但您可以自由使用任何您喜欢的名称。 -集合可以包含游戏对象和其他集合 (通过引用子集合文件), 它们可以随意嵌套. 下面是一个 "main.collection" 集合示例. 它包含了一个游戏对象 (id 叫做 "can") 和一个子集合 (id 叫做 "bean"). 这个子集合, 又包含了两个游戏对象: "bean" 和 "shield". +集合可以包含游戏对象和其他集合(通过引用子集合文件),可以任意深度嵌套。下面是一个名为"main.collection"的文件示例。它包含一个游戏对象(ID为"can")和一个子集合(ID为"bean")。子集合又包含两个游戏对象:"bean"和"shield"。 ![Collection](images/building_blocks/collection.png) -注意这个 id 叫做 "bean" 的子集合也是一个集合文件, 路径是 "/main/bean.collection", 这个文件被 "main.collection" 引用: +请注意,ID为"bean"的子集合存储在自己的文件中,名为"/main/bean.collection",仅在"main.collection"中被引用: ![Bean collection](images/building_blocks/bean_collection.png) -运行时无法用集合的 id 对 "main" 和 "bean" 这样的集合定位. 但是, 使用 _路径_ 引用游戏对象时可能会用到集合的 id (详情请见 [定位教程](/manuals/addressing)): +您无法对集合本身进行寻址,因为没有与"main"和"bean"集合对应的运行时对象。但是,您有时需要使用集合的标识作为游戏对象_路径_的一部分(详细信息请参阅[定位手册](/manuals/addressing)): ```lua -- file: can.script --- 定位 "bean" 集合的 "bean" 对象 +-- 获取"bean"集合中"bean"游戏对象的位置 local pos = go.get_position("bean/bean") ``` -集合只能用文件引用的方式添加到其他集合中: +集合总是作为对集合文件的引用添加到另一个集合中: -在 *Outline* 视图中 右键点击 选择 Add Collection File. +在*Outline*视图中右键单击集合并选择Add Collection File。 ## 游戏对象 -游戏对象在游戏运行时有自己的生命周期. 游戏对象有位置, 旋转和缩放. 这些属性可以在运行时进行控制也可用于属性动画. +游戏对象是在游戏执行期间各自具有独立生命周期的简单对象。游戏对象具有位置、旋转和缩放,每个属性都可以在运行时进行操作和动画处理。 ```lua --- 将 "can" 游戏对象的 X 坐标位置创建属性动画 +-- 为"can"游戏对象的X位置设置动画 go.animate("can", "position.x", go.PLAYBACK_LOOP_PINGPONG, 100, go.EASING_LINEAR, 1.0) ``` -游戏对象可以是空的 (比如作为位置标记) 但通常包含各种组件, 比如 sprite, 声音, 脚本, 模型, 工厂什么的. 游戏对象可以使用编辑器进行创建, 放入集合, 或者在运行时使用 _factory_ 组件动态生成. +游戏对象可以空着使用(例如作为位置标记),但通常配备各种组件,如精灵、声音、脚本、模型、工厂等。游戏对象要么在编辑器中创建,放置在集合文件中,要么在运行时通过_factory_组件动态生成。 -游戏对象可以直接放入集合, 或者作为文件被集合引用: +游戏对象可以直接添加到集合中,或者作为对游戏对象文件的引用添加到集合中: -在 *Outline* 视图中 右键点击 集合选择 Add Game Object (直接放入) 或者 Add Game Object File (作为文件引用). +在*Outline*视图中右键单击集合并选择Add Game Object(就地添加)或Add Game Object File(作为文件引用添加)。 ## 组件 @@ -68,43 +68,43 @@ go.animate("can", "position.x", go.PLAYBACK_LOOP_PINGPONG, 100, go.EASING_LINEAR 可用组件列表详见 [组件概述](/manuals/components/). -## 直接放入还是作为文件引用 +## 就地添加或通过引用添加的对象 -创建集合, 游戏对象或者组件 _文件_ 的时候, 实际上是创建了一个蓝图, 或者称为原型. 原型文件保存于项目中, 而不是游戏里. 要在游戏里使用这些原型的实例就需要在集合中把原型实例化. +当您创建集合、游戏对象或组件_文件_时,您创建的是我们所说的原型(在其他引擎中也称为"预制件"或"蓝图")。这只是在项目文件结构中添加了一个文件,并没有在运行的游戏中添加任何内容。要添加基于原型文件的集合、游戏对象或组件的实例,您需要在其中一个集合文件中添加它的实例。 -在大纲视图中可以看到各个实例是基于哪个原型的. 下例中 "main.collection" 包含了3个基于文件的实例: +您可以在大纲视图中看到对象实例基于哪个文件。"main.collection"文件包含三个基于文件的实例: -1. "bean" 子集合. -2. "bean" 子集合里 "bean" 对象的 "bean" 脚本组件. -3. "can" 游戏对象的 "can" 脚本组件. +1. "bean"子集合。 +2. "bean"子集合中"bean"游戏对象中的"bean"脚本组件。 +3. "can"游戏对象中的"can"脚本组件。 ![Instance](images/building_blocks/instance.png) -如果你有许多游戏对象或者集合的实例, 这种基于文件的设计就很方便: +当您有多个游戏对象或集合的实例并希望更改所有实例时,创建原型文件的好处就变得明显: ![GO instances](images/building_blocks/go_instance.png) -修改了原型文件, 那么它的所有实例都能一同被修改. +通过更改原型文件,任何使用该文件的实例都将立即更新。 ![GO changing prototype](images/building_blocks/go_change_blueprint.png) -这里原型的 sprite 图片被更改, 同时所有使用该文件的实例都被更新: +这里原型文件的精灵图像被更改,所有使用该文件的实例立即更新: ![GO instances updated](images/building_blocks/go_instance2.png) -## 游戏对象层级 +## 创建游戏对象的父子关系 -在集合文件中, 可以将游戏对象设置成父子层级关系. 只需要 拖拽 游戏对象到父级对象 放开鼠标 即可完成父子层级的建立: +在集合文件中,您可以构建游戏对象的层次结构,使一个或多个游戏对象成为单个父游戏对象的子对象。只需简单地拖动一个游戏对象并将其放置到另一个游戏对象上,被拖动的游戏对象就成为目标游戏对象的子对象: ![Childing game objects](images/building_blocks/childing.png) -这种动态的父子关系影响了它们的变化方式. 不论是在编辑器还是运行时, 父级的各种变化 (包括位置, 旋转和缩放) 都会自动应用到它的所有子级上: +对象父子层次结构是一种动态关系,影响对象如何响应变换。在编辑器和运行时,应用于对象的任何变换(移动、旋转或缩放)都会依次应用于对象的子对象: ![Child transform](images/building_blocks/child_transform.png) -反过来说, 子级的变化都基于父级的坐标空间. 在编辑器中, 你可以使用 Edit ▸ World Space (默认设置) 或者 Edit ▸ Local Space 来设置一个对象变化基于的坐标空间. +相反,子对象的平移是在父对象的局部空间中完成的。在编辑器中,您可以通过选择Edit ▸ World Space(默认)或Edit ▸ Local Space来选择在局部空间或世界空间中编辑子游戏对象。 -运行时可以通过发送 `set_parent` 消息改变对象的父级. +也可以在运行时通过向对象发送`set_parent`消息来更改对象的父级。 ```lua local parent = go.get_id("bean") @@ -112,5 +112,5 @@ msg.post("child_bean", "set_parent", { parent_id = parent }) ``` ::: important -一个常见的误解是对象层级改变了那么它的定位地址也会改变. 但是, 这其实是两码事. 父子关系改变的是场景的层级. 集合嵌套关系才决定对象地址. 在对象整个生命周期中, 地址是不会变化的. +一个常见的误解是,当游戏对象成为父子层次结构的一部分时,它在集合层次结构中的位置会发生变化。然而,这是两个非常不同的概念。父子层次结构动态地改变场景图,允许对象在视觉上相互附加。决定游戏对象地址的唯一因素是它在集合层次结构中的位置。地址在对象的整个生命周期中是静态的。 ::: diff --git a/docs/zh/manuals/bundling.md b/docs/zh/manuals/bundling.md index 4020b20e..cbf629f9 100644 --- a/docs/zh/manuals/bundling.md +++ b/docs/zh/manuals/bundling.md @@ -1,93 +1,93 @@ --- -title: 打包应用 -brief: 本教程介绍了如何打包应用. +title: 打包应用程序 +brief: 本手册介绍了如何创建应用程序包. --- -# 打包应用 +# 打包应用程序 -开发项目时常常需要在目标平台上进行测试. 开发中越早发现性能问题越好解决. 同样鼓励在各平台间做测试以便发现诸如shader之类的兼容问题. 在做手机开发时可以使用 [手机开发应用](/manuals/dev-app/) 把内容推送到手机上, 避免反复的安装和卸载. +在开发应用程序时,您应该养成在目标平台上尽可能频繁地测试游戏的习惯。您应该这样做是为了在开发过程的早期阶段发现性能问题,这些问题在此时更容易解决。还建议在所有目标平台上进行测试,以发现着色器等方面的差异。在移动设备上开发时,您可以选择使用[移动开发应用](/manuals/dev-app/)将内容推送到应用程序,而不必进行完整的打包和卸载/安装循环。 -你可以在 Defold 编辑器中生成其支持的所有平台应用, 不需要外界工具辅助. 也可以在控制台使用命令行工具打包应用. 如果应用里包含 [原生扩展](/manuals/extensions) 的话打包时需要网络连接. +您可以在Defold编辑器本身中为Defold支持的所有平台创建应用程序包,无需任何外部工具。您也可以使用我们的命令行工具从命令行打包。如果您的项目包含一个或多个[原生扩展](/manuals/extensions),应用程序打包需要网络连接。 ## 从编辑器中打包 -使用 Project 菜单的 Bundle 选项进行打包: +您可以通过项目菜单和Bundle选项创建应用程序包: ![](images/bundling/bundle_menu.png) -选择不同的打包平台会出现不同的对话窗. +选择任何菜单选项都会为该特定平台打开Bundle对话框。 -### 编译报告 +### 构建报告 -有一个编译选项控制编译时是否生成报告. 从报告中可以方便检查游戏包中各个资源占用的空间. 在编译时打开 *Generate build report* 选项即可. +打包游戏时,有一个选项可以创建构建报告。这对于了解构成游戏包的所有资源的大小非常有用。只需在打包游戏时勾选*Generate build report*复选框即可。 ![build report](images/profiling/build_report.png) -关于编译报告详情请见 [调试教程](/manuals/profiling/#编译报告). +要了解更多关于构建报告的信息,请参考[性能分析手册](/manuals/profiling/#build-reports)。 ### Android -建立安卓应用 (.apk 文件) 详见 [安卓教程](/manuals/android/#安卓应用打包). +创建Android应用程序包(.apk文件)的文档记录在[Android手册](/manuals/android/#creating-an-android-application-bundle)中。 ### iOS -建立苹果移动应用 (.ipa 文件) 详见 [iOS 教程](/manuals/ios/#iOS应用打包). +创建iOS应用程序包(.ipa文件)的文档记录在[iOS手册](/manuals/ios/#creating-an-ios-application-bundle)中。 ### macOS -建立Mac系统应用 (.app 文件) 详见 [macOS 教程](/manuals/macos). +创建macOS应用程序包(.app文件)的文档记录在[macOS手册](/manuals/macos)中。 ### Linux -建立Linux应用无需特别设置. +创建Linux应用程序包不需要特定设置,也不需要在*game.project*[项目设置文件](/manuals/project-settings/#linux)中进行可选的平台特定配置。 ### Windows -建立Windows应用 (.exe 文件) 详见 [Windows 教程](/manuals/windows). +创建Windows应用程序包(.exe文件)的文档记录在[Windows手册](/manuals/windows)中。 ### HTML5 -建立HTML5应用及其参数设置详见 [HTML5 教程](/manuals/html5/#HTML5游戏打包). +创建HTML5应用程序包以及可选设置的文档记录在[HTML5手册](/manuals/html5/#creating-html5-bundle)中。 #### Facebook Instant Games -可以为 Facebook Instant Games 打包成 HTML5 应用的一种特殊版本. 这一过程详见 [Facebook Instant Games 教程](/manuals/instant-games/). +可以为Facebook Instant Games创建一个特殊版本的HTML5应用程序包。这个过程在[Facebook Instant Games手册](/manuals/instant-games/)中有详细说明。 -## 命令行打包 +## 从命令行打包 -编辑器使用命令行工具 [Bob](/manuals/bob/) 进行应用打包. +编辑器使用我们的命令行工具[Bob](/manuals/bob/)来打包应用程序。 -日常开发中一般使用 Defold 编辑器编译和打包应用. 如果需要自动生成机制, 比如发布新版本时批处理所有平台或者使用持续集成环境持续生成最新版本. 可以使用 [Bob 命令行工具](/manuals/bob/) 编译和打包. +在进行应用程序的日常开发时,您可能会在Defold编辑器内进行构建和打包。在其他情况下,您可能希望自动生成应用程序包,例如在发布新版本时为所有目标进行批量构建,或者创建最新版本游戏的夜间构建,也许在CI环境中。应用程序的构建和打包可以在正常编辑器工作流程之外使用[Bob命令行工具](/manuals/bob/)完成。 -## Bundle 结构 +## 包布局 -Bundle 的逻辑结构是这样的: +逻辑包布局结构如下: ![](images/bundling/bundle_schematic_01.png) -Bundle 会被输出到一个文件夹. 不同平台位置各异, 还有可能作为 zip 文件包含进 `.apk` 或者 `.ipa` 中. -Bundle 文件夹的内容每个平台也不一样. +包被输出到一个文件夹中。根据平台的不同,该文件夹也可能被zip归档到`.apk`或`.ipa`中。 +文件夹的内容取决于平台。 -除了可执行文件, 打包过程中也会收集相关平台所必须的资源 (比如安卓平台用的 .xml 资源文件). +除了可执行文件外,我们的打包过程还会收集平台所需的资源(例如Android的.xml资源文件)。 -通过 [bundle_resources](https://defold.com/manuals/project-settings/#bundle-resources) 项, 设置应打包进 bundle 里的资源. -可以针对不同平台分别设置. +使用[bundle_resources](https://defold.com/manuals/project-settings/#bundle-resources)设置,您可以配置应原样放置在包中的资源。 +您可以按平台控制这一点。 -游戏资源被保存在 `game.arcd` 文件中, 使用 LZ4 算法逐个压缩. -通过 [custom_resources](https://defold.com/manuals/project-settings/#custom-resources) 项, 设置应打包 (同时也被压缩) 进 game.arcd 里的资源. -这类资源可以使用 [sys.load_resource()](https://defold.com/ref/sys/#sys.load_resource) 函数来进行访问. +游戏资源位于`game.arcd`文件中,它们使用LZ4压缩单独压缩。 +使用[custom_resources](https://defold.com/manuals/project-settings/#custom-resources)设置,您可以配置应放置(并压缩)在`game.arcd`中的资源。 +这些资源可以通过[`sys.load_resource()`](https://defold.com/ref/sys/#sys.load_resource)函数访问。 ## Release 与 Debug -打包游戏时有个选项允许选择创建 debug 还是 release 应用. 这两种应用包类似但是要记得两者存在如下区别: +创建应用程序包时,您可以选择创建debug或release包。这两种包之间的差异很小,但重要的是要记住: -* Release 包不包含 [性能分析器](/manuals/profiling) -* Release 包不包含 [屏幕录制器](/ref/stable/sys/#start_record) -* Release 不输出调用 `print()` 产生的信息也不输出原生扩展产生的任何信息 -* Release 包的 `sys.get_engine_info()` 中的 `is_debug` 被设置为 `false` -* Release 包调用 `tostring()` 时不会反查 `hash` 值. 也就是说对于一个类型为 `url` 或 `hash` 值的 `tostring()` 不会返回原值字符串而是返回一个数字表示的字符串 (`'hash: [/camera_001]'` 对比 `'hash: [11844936738040519888 (unknown)]'`) -* Release 包不支持编辑器中设置的用于 [热重载](/manuals/hot-reload) 和类似功能的 target +* Release构建不包含[性能分析器](/manuals/profiling) +* Release构建不包含[屏幕录制器](/ref/stable/sys/#start_record) +* Release构建不显示任何对`print()`的调用的输出或任何原生扩展的输出 +* Release构建在`sys.get_engine_info()`中将`is_debug`值设置为`false` +* Release构建在调用`tostring()`时不会对`hash`值进行反向查找。这在实践中意味着,对于类型为`url`或`hash`的值的`tostring()`将返回其数字表示,而不是原始字符串(`'hash: [/camera_001]'`对比`'hash: [11844936738040519888 (unknown)]'`) +* Release构建不支持来自编辑器的[热重载](/manuals/hot-reload)和类似功能的targeting diff --git a/docs/zh/manuals/caching-assets.md b/docs/zh/manuals/caching-assets.md index 8e10d0ce..79e66c4f 100644 --- a/docs/zh/manuals/caching-assets.md +++ b/docs/zh/manuals/caching-assets.md @@ -1,51 +1,51 @@ --- -title: 资源缓存 -brief: 跟教程介绍了如何利用资源缓存提高编译速度. +title: 缓存资源 +brief: 本手册解释了如何使用资源缓存来加速构建. --- -# 资源缓存 +# 缓存资源 -Defold 编译通常需要几秒钟, 但是随着项目和资源的扩大, 耗时也会增加. 对于大项目而言, 编译时间大量消耗在编译字体和压缩纹理上, 所以引入资源缓存功能使得再次编译时只编译改变了的资源, 那些没改变的资源直接从缓存获取以提高编译效率. +使用Defold创建的游戏通常在几秒钟内就能构建完成,但随着项目的增长,资源量也会增加。在大型项目中,编译字体和压缩纹理可能需要大量时间,而资源缓存的存在是为了通过只重新构建已更改的资源来加速构建,同时从缓存中使用已编译的资源来处理未更改的资源。 -Defold 使用了三重缓存: +Defold使用三层缓存: 1. 项目缓存 2. 本地缓存 -3. 原创缓存 +3. 远程缓存 ## 项目缓存 -Defold 默认把编译好的资源缓存到项目的 `build/default` 文件夹中. 下次编译时只编译改变了的资源, 未改变的资源从项目缓存中获取. 这种缓存在编辑器和命令行都会开启. +Defold默认将编译的资源缓存在Defold项目的`build/default`文件夹中。项目缓存将加速后续构建,因为只有修改过的资源需要重新编译,而没有更改的资源将从项目缓存中使用。此缓存始终启用,编辑器和命令行工具都会使用它。 -可以手动删除项目目录下 `build/default` 中的文件, 或者使用 [命令行编译工具 Bob](/manuals/bob) 的 `clean` 命令, 达到清除项目缓存的目的. +可以通过删除`build/default`中的文件或通过[命令行构建工具Bob](/manuals/bob)发出`clean`命令来手动删除项目缓存。 ## 本地缓存 -自 Defold 1.2.187 版本引入 +Defold 1.2.187版本新增 -本地缓存作为可选功能, 把编译后的资源保存在本机或者云盘上. 这相当于是项目缓存的备份. 本地缓存还可以供项目的合作开发人员共享. 目前该缓存只能使用命令行工具开启. 即命令行参数 `resource-cache-local` 项: +本地缓存是一个可选的第二层缓存,其中编译的资源存储在同一台机器上的外部文件位置或网络驱动器上。由于其外部位置,缓存内容在清理项目缓存后仍然存在。它也可以由在同一项目上工作的多个开发人员共享。该缓存目前仅在使用命令行工具构建时可用。它通过`resource-cache-local`选项启用: ```sh java -jar bob.jar --resource-cache-local /Users/john.doe/defold_local_cache ``` -本地缓存通过基于 Defold 引擎版本, 资源文件, 项目编译参数等计算出的校验码进行访问. 这样做就能保证不同 Defold 版本之间资源缓存的唯一性. +从本地缓存访问编译资源是基于计算的校验和,该校验和考虑了Defold引擎版本、源资源的名称和内容以及项目构建选项。这将保证缓存的资源是唯一的,并且缓存可以在多个Defold版本之间共享。 ::: sidenote -本地缓存文件永远存在. 想要清除只能手动删除这些文件. +存储在本地缓存中的文件将无限期存储。由开发人员手动删除旧/未使用的文件。 ::: ## 远程缓存 -自 Defold 1.2.187 版本引入 +Defold 1.2.187版本新增 -远程缓存作为可选功能, 把编译后的资源保存至服务器然后通过 HTTP 请求进行访问. 目前该缓存只能使用命令行工具开启. 即命令行参数 `resource-cache-remote` 项: +远程缓存是一个可选的第三层缓存,其中编译的资源存储在服务器上并通过HTTP请求访问。该缓存目前仅在使用命令行工具构建时可用。它通过`resource-cache-remote`选项启用: ```sh -java -jar bob.jar --resource-cache-remote https://http://192.168.0.100/ +java -jar bob.jar --resource-cache-remote http://192.168.0.100/ ``` -远程缓存同样通过校验码进行访问. 使用 HTTP 请求的 GET, PUT 方法和 HEAD 获取缓存文件. Defold 不提供远程服务器程序. 开发者可以自行搭建. 比如 [这个使用 Python 做的简单的服务器程序](https://github.com/britzl/httpserver-python). +与本地缓存一样,所有资源都是基于计算的校验和从远程缓存访问的。缓存的资源通过HTTP请求方法GET、PUT和HEAD访问。Defold不提供远程缓存服务器。这取决于每个开发人员来设置它。[一个基本的Python服务器示例可以在这里看到](https://github.com/britzl/httpserver-python)。 diff --git a/docs/zh/manuals/camera.md b/docs/zh/manuals/camera.md index f448faab..8c078658 100644 --- a/docs/zh/manuals/camera.md +++ b/docs/zh/manuals/camera.md @@ -1,66 +1,109 @@ --- -title: 摄像机组件教程 -brief: 本教程介绍了 Defold 摄像机组件的功能. +title: 摄像机组件手册 +brief: 本手册描述了Defold摄像机组件的功能. --- # 摄像机 -Defold 的摄像机组件控制游戏世界的视口与映射. 摄像机组件定义了透视和平视的视口与映射矩阵用于渲染脚本进行渲染. +Defold中的摄像机是一种改变游戏世界视口和投影的组件。摄像机组件定义了一个基本的透视或正交摄像机,它为渲染脚本提供视图和投影矩阵。 -透视摄像机一般服务于 3D 游戏, 摄像机视口与物体透视基于视锥体, 摄像机到游戏物体的距离和视角. +透视摄像机通常用于3D游戏,其中摄像机的视图以及物体的大小和透视基于视锥体以及从摄像机到游戏中物体的距离和视角。 -平视摄像机一般服务于 2D 游戏. 摄像机视口不是基于视锥体, 而是基于视立方体. 平视摄像机中的物体不因距离远近而缩放. 1000米远的物体和摄像机面前的物体是等大的. +对于2D游戏,通常希望使用正交投影来渲染场景。这意味着摄像机的视图不再由视锥体决定,而是由一个立方体决定。正交投影是不现实的,因为它不会根据物体的距离改变物体的大小。一个1000单位远的物体将与摄像机正前方的物体以相同的大小渲染。 ![projections](images/camera/projections.png) ## 创建摄像机 -要创建摄像机, 在游戏对象上 右键点击 选择 Add Component ▸ Camera. 或者先创建组件文件再链接到游戏对象上. +要创建摄像机,右键点击一个游戏对象并选择Add Component ▸ Camera。您也可以在项目层次结构中创建一个组件文件,并将该组件文件添加到游戏对象中。 ![create camera component](images/camera/create.png) -摄像机有以下属性用以建立 *视锥*: +摄像机组件具有以下定义摄像机*视锥体*的属性: ![camera settings](images/camera/settings.png) Id -: 组件id +: 组件的ID Aspect Ratio -: (**透视摄像机可用**) - 视锥宽高比. 1.0 代表正方形视口. 4:3 显示器 1024x768 这样的分辨率用 1.33. 16:9 的显示器用 1.78. 如果设置了 *Auto Aspect Ratio* 则此属性无效. +: (**仅透视摄像机**) - 视锥体宽度和高度之间的比率。1.0表示您假设的是方形视图。1.33适用于4:3视图,如1024x768。1.78适用于16:9视图。如果设置了*自动宽高比*,则此设置将被忽略。 Fov -: (**透视摄像机可用**) - 以 _弧度_ 表示的摄像机 *垂直* 视域. 视域越宽, 摄像机看到的内容越多. 注意目前默认值 (45) 有点误导. 要 45 度的视域, 要设置值为 0.785 (`π / 4`). +: (**仅透视摄像机**) - 以_弧度_表示的摄像机*垂直*视野。视野越宽,摄像机将看到的内容越多。 Near Z -: 近端裁剪平面z值. +: 近裁剪平面的Z值。 Far Z -: 远端裁剪平面z值. +: 远裁剪平面的Z值。 Auto Aspect Ratio -: (**透视摄像机可用**) - 自动设置宽高比. +: (**仅透视摄像机**) - 设置此项以让摄像机自动计算宽高比。 Orthographic Projection -: 切换摄像机为平视摄像机 (见下文). +: 设置此项以将摄像机切换到正交投影(见下文)。 Orthographic Zoom -: (**基于平视摄像机**) - 正交透视倍数 (> 1 = 放大, < 1 = 缩小). +: (**仅正交摄像机**) - 用于正交投影的缩放(> 1 = 放大,< 1 = 缩小)。 ## 使用摄像机 -通过调用 `camera.acquire_focus` 函数或者向组件发送 `acquire_camera_focus` 消息, 激活摄像机并填充视口同时向渲染脚本提供映射矩阵: +所有摄像机都会在帧期间自动启用和更新,并且lua `camera`模块在所有脚本上下文中都可用。自Defold 1.8.1起,不再需要通过向摄像机组件发送`acquire_camera_focus`消息来显式启用摄像机。旧的获取和释放消息仍然可用,但建议改为使用"enable"和"disable"消息,就像您希望启用或禁用的任何其他组件一样: ```lua -camera.acquire_focus("#camera") +msg.post("#camera", "disable") +msg.post("#camera", "enable") ``` -或者 + +要列出所有当前可用的摄像机,您可以使用camera.get_cameras(): + +```lua +-- 注意:渲染调用仅在渲染脚本中可用。 +-- camera.get_cameras()函数可以在任何地方使用, +-- 但render.set_camera只能在渲染脚本中使用。 + +for k,v in pairs(camera.get_cameras()) do + -- 摄像机表包含所有摄像机的URL + render.set_camera(v) + -- 在这里进行渲染 - 这里渲染的任何使用指定了 + -- 视图和投影矩阵的材质的内容,都将使用来自摄像机的矩阵。 +end +-- 要禁用摄像机,将nil(或根本不传递参数)传递给render.set_camera。 +-- 此调用后,所有渲染调用将使用在渲染上下文中指定的 +-- 视图和投影矩阵(render.set_view和render.set_projection) +render.set_camera() +``` + +脚本`camera`模块有多个可用于操作摄像机的函数。这里只是几个可以使用的函数,要查看所有可用函数,请查阅[API文档](/ref/camera/)中的手册。 + ```lua -msg.post("#camera", "acquire_camera_focus") +camera.get_aspect_ratio(camera) -- 获取宽高比 +camera.get_far_z(camera) -- 获取远z值 +camera.get_fov(camera) -- 获取视野 +camera.set_aspect_ratio(camera, ratio) -- 设置宽高比 +camera.set_far_z(camera, far_z) -- 设置远z值 +camera.set_near_z(camera, near_z) -- 设置近z值 +... 等等 ``` -被激活的摄像机, 会在每一帧向 "@render" 接口发送 `set_view_projection` 消息, 也就是说渲染脚本会接收此消息: +摄像机通过URL标识,它是组件的完整场景路径,包括集合、它所属的游戏对象和组件ID。在这个例子中,您将使用URL `/go#camera` 来从同一集合中标识摄像机组件,而在从不同集合或渲染脚本访问摄像机时使用 `main:/go#camera`。 + +![create camera component](images/camera/create.png) + +```lua +-- 从同一集合中的脚本访问摄像机: +camera.get_fov("/go#camera") + +-- 从不同集合中的脚本访问摄像机: +camera.get_fov("main:/go#camera") + +-- 从渲染脚本访问摄像机: +render.set_camera("main:/go#camera") +``` + +每帧,当前具有摄像机焦点的摄像机组件将向"@render"套接字发送`set_view_projection`消息: ```lua -- builtins/render/default.render_script @@ -72,113 +115,165 @@ function on_message(self, message_id, message) end end ``` -1. 这个消息中包含一个视口矩阵和一个映射矩阵. +1. 从摄像机组件发布的消息包括一个视图矩阵和一个投影矩阵。 -摄像机组件支持基于摄像机属性 *Orthographic Projection* 的, 使用透视或者平视透视矩阵的渲染脚本. 投射矩阵还参考了定义好的远近裁切平面和摄像机的视角及宽高比. +摄像机组件根据摄像机的*正交投影*属性为渲染脚本提供透视或正交投影矩阵。投影矩阵还考虑了定义的近和远裁剪平面、摄像机的视野和宽高比设置。 -摄相机提供的视图矩阵定义了摄相机的位置和方向. *Orthographic Projection* 的摄像机基于其父游戏对象的位置居中视口, 而 *Perspective Projection* 的摄像机会基于其父游戏对象的位置确定视口左下角的位置. +摄像机提供的视图矩阵定义了摄像机的位置和方向。具有*正交投影*的摄像机将视图居中在它所附加的游戏对象的位置上,而具有*透视投影*的摄像机将视图的左下角定位在它所附加的游戏对象上。 -::: important -出于向后兼容性的原因,默认渲染脚本会忽略相机提供的透视,并始终使用平视透视. 关于渲染脚本, 视口和透视矩阵更多详情参见 [渲染教程](/manuals/render/#default-view-projection). -::: -可以用以下代码告诉渲染脚本使用摄像机提供的透视: +### 渲染脚本 + +从Defold 1.9.6开始,当使用默认渲染脚本时,Defold将自动设置应使用的最后启用的摄像机。在此更改之前,项目中的某个脚本需要显式地向渲染器发送`use_camera_projection`消息,以通知它应使用摄像机组件的视图和投影。这不再必要,但为了向后兼容性,仍然可以这样做。 + +或者,您可以在渲染脚本中设置应使用的特定摄像机。这在需要更具体控制应使用哪个摄像机进行渲染的情况下可能很有用,例如在多人游戏中。 + +```lua +-- render.set_camera将自动使用视图和投影矩阵 +-- 用于任何渲染,直到调用render.set_camera()。 +render.set_camera("main:/my_go#camera") +``` + +要检查摄像机是否处于活动状态,您可以使用[摄像机API](https://defold.com/ref/alpha/camera/#camera.get_enabled:camera)中的`get_enabled`函数: ```lua -msg.post("@render:", "use_camera_projection") +if camera.get_enabled("main:/my_go#camera") then + -- 摄像机已启用,使用它进行渲染! + render.set_camera("main:/my_go#camera") +end ``` +::: sidenote +要将`set_camera`函数与视锥体剔除一起使用,您需要将此作为选项传递给函数: +`render.set_camera("main:/my_go#camera", {use_frustum = true})` +::: -### 摄像机平移 +### 平移摄像机 -平移摄像机所在游戏对象就相当于平移摄像机. 摄像机会根据当前x和y坐标更新视口矩阵. +您通过移动摄像机组件所附加到的游戏对象来在世界中平移/移动摄像机。摄像机组件将根据摄像机的当前x和y轴位置自动发送更新的视图矩阵。 -### 摄像机缩放 +### 缩放摄像机 -延 z 轴移动透视摄像机所在游戏对象就相当于缩放摄像机. 摄像机会根据当前z坐标更新视口矩阵. +使用透视摄像机时,您可以通过沿z轴移动摄像机所附加到的游戏对象来放大和缩小。摄像机组件将根据摄像机的当前z位置自动发送更新的视图矩阵。 -可以通过设置平视摄像机的 *Orthographic Zoom* 属性来进行缩放: +使用正交摄像机时,您可以通过更改摄像机的*正交缩放*属性来放大和缩小: ```lua go.set("#camera", "orthographic_zoom", 2) ``` -### 摄像机跟随 +### 自适应缩放 -把摄像机对象放在要跟随的游戏对象子级就能实现摄像机跟随: +自适应缩放背后的概念是当显示分辨率从*game.project*中设置的初始分辨率改变时调整摄像机缩放值。 -![follow game object](images/camera/follow.png) +自适应缩放的两种常见方法是: -或者自己写脚本每帧更新摄像机位置也可以. +1. 最大缩放 - 计算一个缩放值,使得*game.project*中初始分辨率所覆盖的内容将填充并扩展到屏幕边界之外,可能会隐藏侧面或上方和下方的一些内容。 +2. 最小缩放 - 计算一个缩放值,使得*game.project*中初始分辨率所覆盖的内容将完全包含在屏幕边界内,可能会在侧面或上方和下方显示额外内容。 -### 鼠标位置转换为世界坐标 +示例: -摄像机平移缩放后 `on_input()` 函数提供的鼠标位置就不再与世界坐标匹配了. 此时需要进行手动矫正. 把鼠标/屏幕坐标转换到世界坐标的代码如下: +```lua +local DISPLAY_WIDTH = sys.get_config_int("display.width") +local DISPLAY_HEIGHT = sys.get_config_int("display.height") + +function init(self) + local initial_zoom = go.get("#camera", "orthographic_zoom") + local display_scale = window.get_display_scale() + window.set_listener(function(self, event, data) + if event == window.WINDOW_EVENT_RESIZED then + local window_width = data.width + local window_height = data.height + local design_width = DISPLAY_WIDTH / initial_zoom + local design_height = DISPLAY_HEIGHT / initial_zoom + + -- 最大缩放:确保初始设计尺寸将填充并扩展到屏幕边界之外 + local zoom = math.max(window_width / design_width, window_height / design_height) / display_scale + + -- 最小缩放:确保初始设计尺寸将缩小并包含在屏幕边界内 + --local zoom = math.min(window_width / design_width, window_height / design_height) / display_scale + + go.set("#camera", "orthographic_zoom", zoom) + end + end) +end +``` -::: sidenote -[本教程第三方摄像机解决方案部分](/manuals/camera/#第三方摄像机解决方案) 提供了坐标转换方法. -::: +自适应缩放的完整示例可以在[此示例项目](https://github.com/defold/sample-adaptive-zoom)中看到。 + +### 跟随游戏对象 + +您可以通过将摄像机组件所附加到的游戏对象设置为要跟随的游戏对象的子对象来使摄像机跟随游戏对象: + +![follow game object](images/camera/follow.png) + +另一种方法是每帧更新摄像机组件所附加到的游戏对象的位置,随着要跟随的游戏对象移动。 + +### 将鼠标转换为世界坐标 + +当摄像机已经平移、缩放或将其投影从默认的正交Stretch投影更改时,`on_input()`生命周期函数中提供的鼠标坐标将不再与游戏对象的世界坐标匹配。您需要手动考虑视图或投影的变化。将鼠标/屏幕坐标转换为世界坐标的代码如下: ```Lua --- 从屏幕坐标变换到世界坐标 --- @param sx 屏幕 x --- @param sy 屏幕 y --- @param sz 屏幕 z --- @param window_width 窗口宽度 (使用 render.get_width() 或 window.get_size().x) --- @param window_height 窗口高度 (使用 render.get_height() 或 window.get_size().y) --- @param projection Camera/render 映射 (使用 go.get("#camera", "projection")) --- @param view Camera/render 视口 (使用 go.get("#camera", "view")) --- @return wx 世界 x --- @return wy 世界 y --- @return wz 世界 z -local function screen_to_world(sx, sy, sz, window_width, window_height, projection, view) - local inv = vmath.inv(projection * view) - sx = (2 * sx / window_width) - 1 - sy = (2 * sy / window_height) - 1 - sz = (2 * sz) - 1 - local wx = sx * inv.m00 + sy * inv.m01 + sz * inv.m02 + inv.m03 - local wy = sx * inv.m10 + sy * inv.m11 + sz * inv.m12 + inv.m13 - local wz = sx * inv.m20 + sy * inv.m21 + sz * inv.m22 + inv.m23 - return wx, wy, wz +--- 转换屏幕到世界坐标,考虑 +-- 特定摄像机的视图和投影 +-- @param camera 用于转换的摄像机URL +-- @param screen_x 要转换的屏幕x坐标 +-- @param screen_y 要转换的屏幕y坐标 +-- @param z 可选的z坐标以通过转换,默认为0 +-- @return world_x 屏幕坐标的结果世界x坐标 +-- @return world_y 屏幕坐标的结果世界y坐标 +-- @return world_z 屏幕坐标的结果世界z坐标 +function M.screen_to_world(camera, screen_x, screen_y, z) + local projection = go.get(camera, "projection") + local view = go.get(camera, "view") + local w, h = window.get_size() + + -- https://defold.com/manuals/camera/#converting-mouse-to-world-coordinates + local inv = vmath.inv(projection * view) + local x = (2 * screen_x / w) - 1 + local y = (2 * screen_y / h) - 1 + local x1 = x * inv.m00 + y * inv.m01 + z * inv.m02 + inv.m03 + local y1 = x * inv.m10 + y * inv.m11 + z * inv.m12 + inv.m13 + return x1, y1, z or 0 end ``` -参见 [示例页面](https://defold.com/examples/render/screen_to_world/) 的屏幕坐标到世界坐标转换实例. 还有一个 [实例项目](https://github.com/defold/sample-screen-to-world-coordinates/) 介绍了屏幕坐标到世界坐标的转换. +请记住,来自`on_input()`的值`action.screen_x`和`action.screen_y`应该用作此函数的参数。访问[示例页面](https://defold.com/examples/render/screen_to_world/)以查看屏幕到世界坐标转换的实际操作。还有一个[示例项目](https://github.com/defold/sample-screen-to-world-coordinates/)展示了如何进行屏幕到世界坐标的转换。 ::: sidenote -[本教程使用的第三方摄像机库](/manuals/camera/#third-party-camera-solutions) 提供了自屏幕坐标和到屏幕坐标的转换功能. +[本手册中提到的第三方摄像机解决方案](/manuals/camera/#third-party-camera-solutions)提供了用于屏幕坐标之间转换的函数。 ::: -## 运行时控制 -可以通过一些消息和属性控制运行时的摄像机 (用法参考 [API 文档](/ref/camera/)). +## 运行时操作 +您可以通过多种不同的消息和属性在运行时操作摄像机(请参阅[API文档以了解用法](/ref/camera/))。 -摄像机的一些属性可以通过 `go.get()` 和 `go.set()` 控制: +摄像机有许多不同的属性可以使用`go.get()`和`go.set()`进行操作: `fov` -: 摄像机视野 (`number`). +: 摄像机视野(`number`)。 `near_z` -: 摄像机近端Z值 (`number`). +: 摄像机近Z值(`number`)。 `far_z` -: 摄像机远端Z值 (`number`). +: 摄像机远Z值(`number`)。 `orthographic_zoom` -: 平视摄像机缩放 (`number`). +: 正交摄像机缩放(`number`)。 `aspect_ratio` -: 自从 Defold 1.4.8 加入. 视口宽高比. 在计算透视摄像机投射时使用. (`number`). +: Defold 1.4.8中添加。视锥体宽度和高度之间的比率。用于计算透视摄像机的投影。(`number`)。 `view` -: 自从 Defold 1.4.8 加入. 摄像机视口矩阵值. 只读. (`matrix4`). +: Defold 1.4.8中添加。摄像机的计算视图矩阵。只读。(`matrix4`)。 `projection` -: 自从 Defold 1.4.8 加入. 摄像机投射矩阵值. 只读. (`matrix4`). +: Defold 1.4.8中添加。摄像机的计算投影矩阵。只读。(`matrix4`)。 ## 第三方摄像机解决方案 -社区制作了一些摄像机插件实现了屏幕抖动, 对象跟随, 屏幕与世界坐标转换等摄像机功能. 可以在 Defold 资源大厅找到: +有社区制作的摄像机解决方案实现了常见功能,如屏幕抖动、跟随游戏对象、屏幕到世界坐标转换等等。它们可以从Defold资产门户下载: -- [Ortographic camera](https://defold.com/assets/orthographic/) (仅 2D) 由 Björn Ritzl 开发. -- [Defold Rendy](https://defold.com/assets/defold-rendy/) (2D 和 3D) 由 Klayton Kowalski 开发. \ No newline at end of file +- [正交摄像机](https://defold.com/assets/orthographic/)(仅2D)由Björn Ritzl开发。 +- [Defold Rendy](https://defold.com/assets/defold-rendy/)(2D和3D)由Klayton Kowalski开发。 \ No newline at end of file diff --git a/docs/zh/manuals/collection-factory.md b/docs/zh/manuals/collection-factory.md index 7a35c166..7ce8c207 100644 --- a/docs/zh/manuals/collection-factory.md +++ b/docs/zh/manuals/collection-factory.md @@ -1,57 +1,57 @@ --- -title: 集合工厂教程 -brief: 本教程介绍了如何使用集合工厂创建一组嵌套的游戏对象. +title: 集合工厂手册 +brief: 本手册解释了如何使用集合工厂组件来生成游戏对象的层级结构. --- # 集合工厂 -集合工厂组件用于在游戏运行时创建一组保存于集合之中的嵌套的游戏对象. +集合工厂组件用于将存储在集合文件中的游戏对象组和层级结构生成到运行中的游戏中。 -Defold 提供了集合或者称为 "prefabs" 的可重用模板机制 . 关于集合概述, 请见 [Defold 组成](/manuals/building-blocks#collections). 使用集合可以在编辑器, 或者在运行时动态插入游戏内容. +集合提供了一种强大的机制来创建可重用的模板,或称为Defold中的"预制件"。有关集合的概述,请参阅[构建块文档](/manuals/building-blocks#collections)。集合可以放置在编辑器中,也可以动态插入到您的游戏中。 -使用集合工厂可以依照一个集合文件向游戏世界插入内容. 这与游戏对象工厂的做法类似只不过集合工厂能创建一组含有父子嵌套层级关系的游戏对象. 典型用法比如动态生成一组敌人 (敌人对象和其武器对象的组合). +使用集合工厂组件,您可以将集合文件的内容生成到游戏世界中。这类似于对集合内的所有游戏对象执行工厂生成,然后在对象之间构建父子层级关系。一个典型的用例是生成由多个游戏对象组成的敌人(例如,敌人+武器)。 -## 创建集合 +## 生成集合 -假设有一个角色对象, 在它的子集有个盾牌对象. 我们就可以把这种含嵌套层级关系的组合保存为一个集合文件 "bean.collection". +假设我们想要一个角色游戏对象和一个单独的盾牌游戏对象作为角色的子对象。我们在一个集合文件中构建游戏对象层级结构,并将其保存为"bean.collection"。 ::: sidenote -*集合代理* 组件用于创建基于集合的游戏世界, 里面可以包含独立的物理世界. 通过新建一个接口, 集合中的所有内容通过集合代理加载. 这样可以实现诸如切换关卡之类的功能. 游戏世界通常包含很多东西, 如果加载少量内容, 不要使用集合代理. 详情请见 [集合代理教程](/manuals/collection-proxy). +*集合代理*组件用于基于集合创建新的游戏世界,包括单独的物理世界。新世界通过新的套接字访问。当您向代理发送消息开始加载时,集合中包含的所有资产都通过代理加载。这使得它们对于例如在游戏中切换关卡非常有用。然而,新的游戏世界带来了相当多的开销,因此不要将它们用于少量内容的动态加载。有关更多信息,请参阅[集合代理文档](/manuals/collection-proxy)。 ::: ![Collection to spawn](images/collection_factory/collection.png) -把 *集合工厂* 加入游戏对象然后设置其 *原型* 为 "bean.collection": +然后,我们将*集合工厂*添加到一个游戏对象中,该游戏对象将负责生成,并将"bean.collection"设置为组件的*原型*: ![Collection factory](images/collection_factory/factory.png) -这样只需调用 `collectionfactory.create()` 函数即可创建角色加盾牌的组合了: +现在,生成bean和shield只需要调用`collectionfactory.create()`函数: ```lua local bean_ids = collectionfactory.create("#bean_factory") ``` -此函数有5个参数: +该函数接受5个参数: `url` -: 要使用的集合工厂组件的id. +: 应该生成新游戏对象组的集合工厂组件的id。 `[position]` -: (可选) 新建游戏对象组合的世界坐标位置. 以 `vector3` 表示. 如果不指定位置, 默认位置是集合工厂所在游戏对象的位置. +: (可选)生成的游戏对象的世界位置。这应该是一个`vector3`。如果您不指定位置,对象将在集合工厂组件的位置生成。 `[rotation]` -: (可选) 新创建游戏对象组合的世界坐标旋转. 以 `quat` 表示. +: (可选)新游戏对象的世界旋转。这应该是一个`quat`。 `[properties]` -: (可选) 新创建游戏对象组合的 `id`-`table` 属性初始化 Lua 表. 关于此表结果见下文. +: (可选)一个带有`id`-`table`对的Lua表,用于初始化生成的游戏对象。有关如何构造此表的信息,请参见下文。 `[scale]` -: (可选) 新创建游戏对象组合的等比缩放. 以 `number` (大于 0) 表示. 或者以 `vector3` 表示每个坐标轴上的非等比缩放. +: (可选)生成的游戏对象的缩放比例。缩放可以表示为一个`number`(大于0),它指定所有轴上的均匀缩放。您也可以提供一个`vector3`,其中每个组件指定相应轴上的缩放。 -`collectionfactory.create()` 返回一个包含每个新建对象id的表. 表的内容是集合内每个对象id对应运行时每个对象id: +`collectionfactory.create()`将生成的游戏对象的标识作为表返回。表键将每个对象的集合本地id的哈希映射到每个对象的运行时id: ::: sidenote -"bean" 与 "shield" 的父子层级关系 *不会* 在表里反应出来. 只能从运行时画面看出这种层级关系, 即做变化时两个对象同时变化. 改变层级关系与游戏对象id无关. +"bean"和"shield"之间的父子关系*不会*在返回的表中反映出来。这种关系仅存在于运行时场景图中,即对象如何一起变换。重新设置对象的父级永远不会改变其id。 ::: ```lua @@ -64,11 +64,11 @@ pprint(bean_ids) -- hash: [/bean] = hash: [/collection0/bean], -- } ``` -1. 前缀 `/collection[N]/` 中, `[N]` 是计数器, 以确保每个id的唯一性: +1. 添加了前缀`/collection[N]/`,其中`[N]`是一个计数器,以唯一标识每个实例: ## 属性 -创建集合的对象时, 可以把属性表作为参数传给集合工厂. 表里的键是对象id, 值是这个对象需要设置的属性表. +生成集合时,您可以通过构造一个表来将属性参数传递给每个游戏对象,其中键是对象id,值是包含要设置的脚本属性的表。 ```lua local props = {} @@ -76,7 +76,7 @@ props[hash("/bean")] = { shield = false } local ids = collectionfactory.create("#bean_factory", nil, nil, props) ``` -假设 "bean.collection" 里的 "bean" 对象有一个叫 "shield" 的脚本属性. 关于脚本属性详情请见 [脚本属性教程](/manuals/script-properties). +假设"bean.collection"中的"bean"游戏对象定义了"shield"属性。[脚本属性手册](/manuals/script-properties)包含有关脚本属性的信息。 ```lua -- bean/controller.script @@ -91,16 +91,16 @@ end ## 工厂资源的动态加载 -开启工厂属性的 *Load Dynamically*, 工厂资源将会被延迟加载. +通过选中集合工厂属性中的*动态加载*复选框,引擎会推迟与工厂关联的资源的加载。 ![Load dynamically](images/collection_factory/load_dynamically.png) -关闭动态加载, 则加载集合工厂组件时会同时加载其需要的资源以便工厂可以尽快创建新游戏对象. +取消选中该复选框时,引擎在加载集合工厂组件时加载原型资源,因此它们立即可用于生成。 -开启动态加载, 有两种用法: +选中该复选框时,您有两种使用选项: 同步加载 -: 调用 [`collectionfactory.create()`](/ref/collectionfactory/#collectionfactory.create:url-[position]-[rotation]-[properties]-[scale]) 函数创建新对象时. 资源会同步加载, 这意味着游戏可能会卡一下, 加载完成后再创建新对象. +: 当您想要生成对象时调用[`collectionfactory.create()`](/ref/collectionfactory/#collectionfactory.create:url-[position]-[rotation]-[properties]-[scale])。这将同步加载资源,这可能会导致卡顿,然后生成新实例。 ```lua function init(self) @@ -123,7 +123,7 @@ end ``` 异步加载 -: 调用 [`collectionfactory.load()`](/ref/collectionfactory/#collectionfactory.load:[url]-[complete_function]) 函数进行资源的异步加载. 资源加载完毕后, 回调用回调函数. +: 调用[`collectionfactory.load()`](/ref/collectionfactory/#collectionfactory.load:[url]-[complete_function])以异步方式显式加载资源。当资源准备好生成时,会收到一个回调。 ```lua function load_complete(self, url, result) @@ -150,13 +150,13 @@ end ``` -## 动态 prototype +## 动态原型 -可以通过点选集合工厂的 *Dynamic Prototype* 选项来更改它的 *Prototype*. +可以通过选中集合工厂属性中的*动态原型*复选框来更改集合工厂可以创建的*原型*。 -![动态 prototype](images/collection_factory/dynamic_prototype.png) +![Dynamic prototype](images/collection_factory/dynamic_prototype.png) -当 *Dynamic Prototype* 选项被选中, 则集合工厂组件就可使用 `collectionfactory.set_prototype()` 函数更改其原型. 例如: +当*动态原型*选项被选中时,集合工厂组件可以使用`collectionfactory.set_prototype()`函数更改原型。示例: ```lua collectionfactory.unload("#factory") -- 卸载之前的资源 @@ -165,6 +165,6 @@ local ids = collectionfactory.create("#factory") ``` ::: important -当 *Dynamic Prototype* 被选中, 集合组件数优化则不可使用, 宿主集合将使用 *game.project* 文件中定义的默认最大组件数目. +当设置*动态原型*选项时,集合组件计数无法优化,拥有集合将使用*game.project*文件中的默认组件计数。 ::: diff --git a/docs/zh/manuals/collection-proxy.md b/docs/zh/manuals/collection-proxy.md index 1910f243..eee57fd6 100644 --- a/docs/zh/manuals/collection-proxy.md +++ b/docs/zh/manuals/collection-proxy.md @@ -1,54 +1,54 @@ --- -title: 集合代理教程 -brief: 本教程介绍了如何动态创建新游戏世界以及在游戏世界间进行切换. +title: 集合代理手册 +brief: 本手册解释了如何动态创建新游戏世界以及在游戏世界间进行切换. --- # 集合代理 -集合代理组件用于基于集合文件内容动态加载卸载新的游戏 "世界". 可以用来实现切换关卡, GUI 屏幕, 在关卡里加载卸载插播 "场景", 加载卸载迷你游戏等等功能. +集合代理组件用于基于集合文件内容动态加载和卸载新的游戏"世界"。它们可以用来实现游戏关卡之间的切换、GUI屏幕、在关卡中加载和卸载叙事"场景"、加载/卸载迷你游戏等等。 -Defold 把所有游戏对象组织在集合里. 集合可以包含游戏对象和其他集合 (即子集合). 集合代理可以让你把内容拆分到各个集合然后用脚本动态加载卸载这些集合. +Defold将所有游戏对象组织在集合中。一个集合可以包含游戏对象和其他集合(即子集合)。集合代理允许您将内容拆分为单独的集合,然后通过脚本动态管理这些集合的加载和卸载。 -集合代理不像 [集合工厂组件](/manuals/collection-factory/). 集合工厂用于在当前游戏世界创建集合. 集合代理用于运行时创建全新游戏世界, 它们用处不同. +集合代理与[集合工厂组件](/manuals/collection-factory/)不同。集合工厂将集合的内容实例化到当前游戏世界中。集合代理在运行时创建一个新的游戏世界,因此有不同的用例。 ## 创建集合代理组件 -1. 把一个集合代理组件加入到游戏对象上 右键点击 并从上下文菜单中选择 Add Component ▸ Collection Proxy. +1. 通过右键点击游戏对象并从上下文菜单中选择Add Component ▸ Collection Proxy,将集合代理组件添加到游戏对象。 -2. 设置 *Collection* 属性来引用一个集合, 就是你希望动态加载进运行环境的集合. 这些引用是静态的, 所以确保把游戏所需的各个部分都通过集合引用到. +2. 将*Collection*属性设置为您希望稍后动态加载到运行时中的集合的引用。该引用是静态的,确保所引用集合的所有内容最终都会出现在游戏中。 ![add proxy component](images/collection-proxy/create_proxy.png) -(也可以编译时排除一部分内容需要的时候用代码下载而不使用 *Exclude* 选项和 [热更新功能](/manuals/live-update/).) +(您可以通过勾选*Exclude*框并使用[实时更新功能](/manuals/live-update/)在构建中排除内容,然后通过代码下载。) -## 启动集合 +## 引导集合 -当 Defold 引擎开始工作最先把 *启动集合* 导入运行环境并对其中的所有游戏对象进行初始化. 然后开启游戏对象和它们的组件. 在 [项目配置](/manuals/project-settings/#Main Collection) 里设置把哪个集合作为启动集合使用. 依照惯例启动集合都叫做 "main.collection". +当Defold引擎启动时,它会从*引导集合*加载并实例化所有游戏对象到运行时。然后初始化并启用游戏对象及其组件。引擎应该使用哪个引导集合是在[项目设置](/manuals/project-settings/#main-collection)中设置的。按照惯例,这个集合文件通常命名为"main.collection"。 ![bootstrap](images/collection-proxy/bootstrap.png) -启动集合实例化时引擎会为 "游戏世界" 里的游戏对象和组件分配足够的内存空间. 对于物理模拟和碰撞对象, 引擎会为其建立另一个游戏世界. +为了容纳游戏对象及其组件,引擎为整个"游戏世界"分配所需的内存,引导集合的内容被实例化到这个世界中。还为任何碰撞对象和物理模拟创建了一个单独的物理世界。 -因为脚本要能定位任何地方的游戏对象, 包括启动集合之外的集合里的对象, 所以集合必须有独立的属性: *Name*: +由于脚本组件需要能够处理游戏中的所有对象,即使是来自引导世界之外的对象,它被赋予一个唯一的名称:您在集合文件中设置的*Name*属性: ![bootstrap](images/collection-proxy/collection_id.png) -如果被加载集合里还有集合代理, 代理引用的集合 *不会* 被自动加载. 需要手动写代码进行加载. +如果加载的集合包含集合代理组件,那些代理引用的集合*不会*被自动加载。您需要通过脚本控制这些资源的加载。 -## 载入集合 +## 加载集合 -通过代理动态载入集合需要用脚本给代理发送 `"load"` 消息: +通过代理动态加载集合是通过从脚本向代理组件发送名为`"load"`的消息来完成的: ```lua --- 让代理 "myproxy" 开始加载集合. +-- 告诉代理 "myproxy" 开始加载。 msg.post("#myproxy", "load") ``` ![load](images/collection-proxy/proxy_load.png) -集合代理会告诉引擎需要为新游戏世界分配多大空间内存. 另一个物理世界也被建立起来连同集合 "mylevel.collection" 里的游戏对象都会被实例化. +代理组件将指示引擎为新世界分配空间。还会创建一个单独的运行时物理世界,并且集合"mylevel.collection"中的所有游戏对象都被实例化。 -新游戏世界的创建通过 *Name* 属性引用的集合文件为蓝图, 本例中是 "mylevel". 不能有重名. 如果集合文件 *Name* 重名, 引擎会报错: +新世界从集合文件中的*Name*属性获取其名称,在本示例中设置为"mylevel"。名称必须是唯一的。如果在集合文件中设置的*Name*已被用于已加载的世界,引擎将发出名称冲突错误: ```txt ERROR:GAMEOBJECT: The collection 'default' could not be created since there is already a socket with the same name. @@ -56,12 +56,12 @@ WARNING:RESOURCE: Unable to create resource: build/default/mylevel.collectionc ERROR:GAMESYS: The collection /mylevel.collectionc could not be loaded. ``` -当集合加载完毕, 集合代理会向发送 `"load"` 消息的脚本发回 `"proxy_loaded"` 消息. 收到此消息就可以进行集合初始化等工作了: +当引擎完成加载集合后,集合代理组件将向发送`"load"`消息的脚本发送一个名为`"proxy_loaded"`的消息。然后脚本可以初始化并启用集合作为对该消息的响应: ```lua function on_message(self, message_id, message, sender) if message_id == hash("proxy_loaded") then - -- 新集合已加载完毕. 初始化并激活它. + -- 新世界已加载。初始化并启用它。 msg.post(sender, "init") msg.post(sender, "enable") ... @@ -70,56 +70,71 @@ end ``` `"load"` -: 此消息通知集合代理组件开始为新游戏世界加载集合. 完成后会发送 `"proxy_loaded"` 消息. +: 此消息告诉集合代理组件开始将其集合加载到新世界中。完成后,代理将发送回一个名为`"proxy_loaded"`的消息。 `"async_load"` -: 此消息通知集合代理组件开始在后台为新游戏世界加载集合. 完成后会发送 `"proxy_loaded"` 消息. +: 此消息告诉集合代理组件开始在后台将其集合加载到新世界中。完成后,代理将发送回一个名为`"proxy_loaded"`的消息。 `"init"` -: 此消息通知集合代理组件集合里的游戏对象和组件已实例化完毕, 可以进行初始化了. 此时所有脚本里的 `init()` 函数会被调用. +: 此消息告诉集合代理组件所有已实例化的游戏对象和组件应该被初始化。此时会调用所有脚本的`init()`函数。 `"enable"` -: 此消息通知集合代理组件集合里的游戏对象和组件已实例化完毕, 可以激活它们了. 此时会进行sprite渲染等等工作. +: 此消息告诉集合代理组件所有游戏对象和组件应该被启用。例如,所有精灵组件在启用时开始绘制。 -## 新游戏世界定位 +## 定位到新世界 -使用集合文件的 *Name* 属性用来定位其中的游戏对象和组件. 比如启动集合里有个加载器对象, 一关结束后让它加载下一关: +在集合文件属性中设置的*Name*用于定位已加载世界中的游戏对象和组件。例如,如果您在引导集合中创建了一个加载器对象,您可能需要从任何已加载的集合中与它通信: ```lua --- 告诉加载器加载下一关: +-- 告诉加载器加载下一关: msg.post("main:/loader#script", "load_level", { level_id = 2 }) ``` ![load](images/collection-proxy/message_passing.png) -## 新游戏世界卸载 +如果您需要从加载器与已加载集合中的游戏对象通信,您可以使用[对象的完整URL](/manuals/addressing/#urls)发送消息: -卸载需要发送的消息和加载相反: +```lua +msg.post("mylevel:/myobject", "hello") +``` + +::: important +无法从集合外部直接访问已加载集合中的游戏对象: + +```lua +local position = go.get_position("mylevel:/myobject") +-- loader.script:42: function called can only access instances within the same collection. +``` +::: + +## 卸载世界 + +要卸载已加载的集合,您发送与加载步骤相反的消息: ```lua --- 卸载当前关卡 +-- 卸载关卡 msg.post("#myproxy", "disable") msg.post("#myproxy", "final") msg.post("#myproxy", "unload") ``` `"disable"` -: 此消息通知集合代理组件关闭游戏对象和组件. 此时sprite不再进行渲染工作. +: 此消息告诉集合代理组件禁用世界中的所有游戏对象和组件。精灵在此阶段停止渲染。 `"final"` -: 此消息通知集合代理组件析构游戏对象和组件. 此时所有脚本里的 `final()` 函数会被调用. +: 此消息告诉集合代理组件完成世界中的所有游戏对象和组件。此时会调用所有脚本的`final()`函数。 `"unload"` -: 此消息通知集合代理组件把游戏世界从内存中清除. +: 此消息告诉集合代理将世界完全从内存中移除。 -如果不那么细致, 只发送 `"unload"` 消息就好. 在卸载前代理会自动进行关闭和析构工作. +如果您不需要更细粒度的控制,可以直接发送`"unload"`消息,而无需先禁用和完成集合。代理将在卸载之前自动禁用并完成集合。 -当即和卸载完毕, 集合代理会向发送 `"unload"` 消息的脚本发回 `"proxy_unloaded"` 消息: +当集合代理完成卸载集合后,它将向发送`"unload"`消息的脚本发送一个`"proxy_unloaded"`消息: ```lua function on_message(self, message_id, message, sender) if message_id == hash("proxy_unloaded") then - -- Ok, 游戏世界卸载完成... + -- 好的,世界已卸载... ... end end @@ -128,25 +143,25 @@ end ## 时间步 -集合代理的更新周期可以使用 _time step_ 进行缩放. 也就是说即使游戏是 60 FPS 的, 集合代理游戏世界的速度还是可以变得可以更快或者更慢, 收以下几方面影响: +集合代理更新可以通过改变_时间步_来进行缩放。这意味着即使游戏以稳定的60 FPS运行,代理也可以以更高或更低的速度更新,影响诸如: -* 物理模拟器步进 -* `update()` 函数里的 `dt` -* [游戏对象和gui属性动画](https://defold.com/manuals/animation/#property-animation-1) -* [逐帧动画](https://defold.com/manuals/animation/#flip-book-animation) -* [粒子特效模拟器](https://defold.com/manuals/particlefx/) -* lua逻辑计时器速度 +* 物理模拟速度 +* 传递给`update()`的`dt` +* [游戏对象和GUI属性动画](https://defold.com/manuals/animation/#property-animation-1) +* [翻页动画](https://defold.com/manuals/animation/#flip-book-animation) +* [粒子FX模拟](https://defold.com/manuals/particlefx/) +* 计时器速度 -还可以设置刷新执行模式, 可以控制游戏刷新是分散的(速度缩放小于1.0有效) 还是连续的. +您还可以设置更新模式,这允许您控制缩放是否应该离散执行(仅在缩放因子低于1.0时才有意义)或连续执行。 -通过发送 `set_time_step` 消息给集合代理组件来设置时间步缩放系数与执行模式: +您通过向代理发送`set_time_step`消息来控制缩放因子和缩放模式: ```lua --- 把加载的游戏世界时间放慢为1/5. -msg.post("#myproxy", "set_time_step", {factor = 0.2, mode = 1} +-- 以五分之一速度更新已加载的世界。 +msg.post("#myproxy", "set_time_step", {factor = 0.2, mode = 1}) ``` -这样做的结果, 我们可以通过一段代码来进行观察: +为了了解更改时间步时发生的情况,我们可以创建一个对象,在其脚本组件中放置以下代码,并将其放在我们正在更改时间步的集合中: ```lua function update(self, dt) @@ -154,7 +169,7 @@ function update(self, dt) end ``` -时间步系数为 0.2, 控制台打印如下输出: +时间步为0.2时,我们在控制台中得到以下结果: ```txt INFO:DLIB: SSDP started (ssdp://192.168.0.102:54967, http://0.0.0.0:62162) @@ -172,21 +187,21 @@ DEBUG:SCRIPT: update() with timestep (dt) 0 DEBUG:SCRIPT: update() with timestep (dt) 0.016666667535901 ``` -`update()` 仍然是每秒调用 60 次, 但是 `dt` 值变了. 可以看到只有 1/5 (0.2) 的 `update()` 调用包含 1/60 秒的 `dt` 参数, 其他都是 0. 物理模拟也基于 dt 每 5 帧步进一次. +`update()`仍然每秒调用60次,但`dt`的值发生了变化。我们看到只有1/5(0.2)的`update()`调用会有1/60(对应60 FPS)的`dt`——其余为零。所有物理模拟也将根据该`dt`更新,并且仅在五分之一的帧中前进。 ::: sidenote -可以使用集合的时间步功能来暂停游戏, 例如弹出窗口或者游戏窗口失去焦点时, 使用 `msg.post("#myproxy", "set_time_step", {factor = 0, mode = 0})` 暂停游戏, 然后使用 `msg.post("#myproxy", "set_time_step", {factor = 1, mode = 1})` 继续游戏. +您可以使用集合时间步功能来暂停游戏,例如在显示弹出窗口或窗口失去焦点时。使用`msg.post("#myproxy", "set_time_step", {factor = 0, mode = 0})`暂停,使用`msg.post("#myproxy", "set_time_step", {factor = 1, mode = 1})`恢复。 ::: -详情请见 [`set_time_step`](/ref/collectionproxy#set_time_step). +有关更多详细信息,请参见[`set_time_step`](/ref/collectionproxy#set_time_step)。 -## 注意事项与常见问题 +## 注意事项和常见问题 物理 -: 通过集合代理可以导入多个集合, 或称 *游戏世界*. 要注意的是每个顶级集合都有自己的物理世界. 物理交互 (碰撞, 触发, 射线) 只发生与同一物理世界的物体之间. 所以即使分别来自两个游戏世界的两个物体即使被放在一起, 也不会有碰撞发生. +: 通过集合代理,可以将多个顶级集合或*游戏世界*加载到引擎中。这样做时,重要的是要知道每个顶级集合都是一个单独的物理世界。物理交互(碰撞、触发、射线投射)只发生在属于同一世界的对象之间。因此,即使来自两个世界的碰撞对象在视觉上恰好彼此重叠,它们之间也不会有任何物理交互。 内存 -: 被载入的游戏世界都要占不少内存. 如果同时加载了很多集合, 推荐优化你的游戏规则. 创建多个游戏对象实例的话, [集合工厂](/manuals/collection-factory) 更加适用. +: 每个加载的集合都会创建一个新的游戏世界,这带来了相对较大的内存占用。如果您通过代理同时加载几十个集合,您可能需要重新考虑您的设计。要生成游戏对象层级的许多实例,[集合工厂](/manuals/collection-factory)更适合。 输入 -: 要让集合里的游戏对象获得输入信息, 首先要确保集合代理所在的游戏对象获得了输入焦点. 当游戏对象收到输入消息时, 这些消息将传播到该对象的组件也就是集合代理中去. 输入动作通过集合代理下发到其载入的集合里. +: 如果您在加载的集合中有需要输入操作的对象,您需要确保包含集合代理的游戏对象获取输入。当游戏对象接收到输入消息时,这些消息会传播到该对象的组件,即集合代理。输入操作通过代理发送到加载的集合中。 diff --git a/docs/zh/manuals/components.md b/docs/zh/manuals/components.md index c9daee9c..091a1add 100644 --- a/docs/zh/manuals/components.md +++ b/docs/zh/manuals/components.md @@ -1,6 +1,6 @@ --- title: 游戏对象组件 -brief: 本教程提供了游戏对象组件概览及其使用方法. +brief: 本手册提供了组件概览及其使用方法. --- # 组件 @@ -9,99 +9,98 @@ brief: 本教程提供了游戏对象组件概览及其使用方法. ## 组件类型 -Defold 提供以下组件类型: +Defold 支持以下组件类型: -* [Collection factory](/manuals/collection-factory) - 创建集合实例 -* [Collection proxy](/manuals/collection-proxy) - 加载卸载集合 +* [Collection factory](/manuals/collection-factory) - 生成集合 +* [Collection proxy](/manuals/collection-proxy) - 加载和卸载集合 * [Collision object](/manuals/physics) - 2D 和 3D 物理 -* [Camera](/manuals/camera) - 修改游戏世界的视口和映射 -* [Factory](/manuals/factory) - 创建游戏对象实例 +* [Camera](/manuals/camera) - 更改游戏世界的视口和投影 +* [Factory](/manuals/factory) - 生成游戏对象 * [GUI](/manuals/gui) - 渲染图形用户界面 -* [Label](/manuals/label) - 渲染文本 -* [Mesh](/manuals/mesh) - 显示3D网格 (同时具有实时创建和维护功能) -* [Model](/manuals/model) 显示3D模型 (可以带动画) -* [Particle FX](/manuals/particlefx) - 创建粒子 +* [Label](/manuals/label) - 渲染一段文本 +* [Mesh](/manuals/mesh) - 显示3D网格(具有运行时创建和操作功能) +* [Model](/manuals/model) - 显示3D模型(带有可选动画) +* [Particle FX](/manuals/particlefx) - 生成粒子 * [Script](/manuals/script) - 添加游戏逻辑 -* [Sound](/manuals/sound) - 播放音效音乐 -* [Sprite](/manuals/sprite) - 显示2D图像 (可以带逐帧动画) -* [Tilemap](/manuals/tilemap) - 显示一组瓷砖图 +* [Sound](/manuals/sound) - 播放声音或音乐 +* [Sprite](/manuals/sprite) - 显示2D图像(带有可选翻页动画) +* [Tilemap](/manuals/tilemap) - 显示瓦片网格 -其他组件可以通过载入扩展插件的方式导入项目: +可以通过扩展添加其他组件: -* [Rive model](/extension-rive) - 渲染 Rive 动画 -* [Spine model](/extension-spine) - 渲染 Spine 动画 +* [Rive model](/extension-rive) - 渲染Rive动画 +* [Spine model](/extension-spine) - 渲染Spine动画 -## 开启关闭组件 +## 启用和禁用组件 -游戏对象在创建时其组件是开启的. 如果需要关闭组件可以给组件发送 [`disable`](/ref/go/#disable) 消息: +游戏对象的组件在游戏对象创建时被启用。如果您希望禁用组件,可以通过向组件发送[`disable`](/ref/go/#disable)消息来完成: ```lua --- 把此脚本所在的游戏对象上 id 为 'weapon' 的组件关闭 +-- 禁用与此脚本在同一游戏对象上的id为'weapon'的组件 msg.post("#weapon", "disable") --- 关闭 'enemy' 游戏对象上 id 为 'shield' 的所有组件 +-- 禁用'enemy'游戏对象上id为'shield'的组件 msg.post("enemy#shield", "disable") --- 关闭当前游戏对象上的所有组件 +-- 禁用当前游戏对象上的所有组件 msg.post(".", "disable") --- 关闭 'enemy' 游戏对象上的所有组件 +-- 禁用'enemy'游戏对象上的所有组件 msg.post("enemy", "disable") ``` -需要开启组件可以给组件发送 [`enable`](/ref/go/#enable) 消息: +要再次启用组件,您可以向组件发送[`enable`](/ref/go/#enable)消息: ```lua --- 开启 id 为 'weapon' 的组件 +-- 启用id为'weapon'的组件 msg.post("#weapon", "enable") ``` ## 组件属性 -Defold 组件属性各不相同.在 [Outline 面板](/manuals/editor/#Outline 面板) 中当前选中的组件属性会显示在编辑器的 [Properties 面板](/manuals/editor/#Properties 面板) 中. 可用组件的详细属性详情请见API教程. +Defold组件类型都有不同的属性。编辑器中的[属性面板](/manuals/editor/#the-editor-views)将显示[大纲面板](/manuals/editor/#the-editor-views)中当前选定组件的属性。请参考不同组件类型的手册以了解有关可用组件属性的更多信息。 -## 位置, 旋转和缩放 +## 组件位置、旋转和缩放 -可视组件通常含有位置, 旋转以及缩放属性. 这些属性可以在编辑器里修改但是绝大多数情况下不能在运行时修改 (sprite 和 label 的缩放属性是例外情况). +可视组件通常具有位置和旋转属性,大多数情况下还具有缩放属性。这些属性可以从编辑器中更改,并且在几乎所有情况下都不能在运行时更改(唯一的例外是精灵和标签组件缩放,可以在运行时更改)。 -如果需要在运行时修改组件的而不是组件所在游戏对象的位置, 旋转和缩放. 有个副作用就是游戏对象所有组件都会受影响. 如果你想维护游戏对象上的一个组件而不是多个, 建议将该组件移入另一个游戏对象并作为原来那个游戏对象的子对象. +如果您需要在运行时更改组件的位置、旋转或缩放,您可以修改组件所属游戏对象的位置、旋转或缩放。这会产生副作用,即游戏对象上的所有组件都会受到影响。如果您希望只操作附加到游戏对象的多个组件中的一个,建议将相关组件移动到单独的游戏对象,并作为该组件原本所属的游戏对象的子游戏对象添加。 -## 组件渲染顺序 +## 组件绘制顺序 -可视组件的渲染顺序取决于两个方面: +可视组件的绘制顺序取决于两个方面: -### 渲染脚本的渲染优先级 -每个组件都有 [材质](/manuals/material/) 而且每个材质都有一个或多个标签. 渲染脚本依次定义一系列优先级, 每个优先级匹配一个或多个材质标签. 渲染脚本在 *update()* 函数里 [按优先级依次渲染](/manuals/render/#渲染优先级) , 匹配优先级标签的组件会被显示出来. 默认渲染脚本先绘制 sprites 和 tilemaps, 再渲染粒子特效, 二者都使用世界坐标系. 最后渲染脚本会在屏幕坐标系中渲染 GUI 组件. +### 渲染脚本谓词 +每个组件都被分配一个[材质](/manuals/material/),每个材质有一个或多个标签。渲染脚本反过来定义多个谓词,每个谓词匹配一个或多个材质标签。渲染脚本[谓词在渲染脚本的*update()*函数中逐个绘制](/manuals/render/#render-predicates),匹配每个谓词中定义的标签的组件将被绘制。默认渲染脚本首先在一个通道中绘制精灵和瓦片地图,然后在另一个通道中绘制粒子效果,两者都在世界空间中。然后渲染脚本将继续在屏幕空间中的单独通道中绘制GUI组件。 ### 组件z值 -所有游戏对象都使用一个 vector3 作为其在 3D 空间中的位置. 如果是 2D 游戏, 则 X 和 Y 表示其在 "横向" 和 "纵向" 轴上的位置, 而 Z 值表示其在 "深度" 轴上的位置. 使用Z值可以操控游戏对象之间的层叠关系: Z 值是 1 的总是显示在 Z 值是 0 的对象上面. 默认情况下, Defold 使用 Z 值范围是 -1 到 1: +所有游戏对象和组件都使用vector3对象表示的位置定位在3D空间中。当您以2D查看游戏的图形内容时,X和Y值确定对象在"宽度"和"高度"轴上的位置,而Z位置确定对象在"深度"轴上的位置。Z位置允许您控制重叠对象的可见性:Z值为1的精灵将出现在Z位置0的精灵前面。默认情况下,Defold使用允许Z值在-1和1之间的坐标系: ![model](images/graphics/z-order.png) -匹配某个优先级的组件在一起渲染, 它们之间的渲染顺序取决于组件的 z 值. 组件的最终z值是由组件的z值, 游戏对象和其父级游戏对象的z值之和. +匹配[渲染谓词](/manuals/render/#render-predicates)的组件一起绘制,它们绘制的顺序取决于组件的最终z值。组件的最终z值是组件本身的z值、它所属的游戏对象的z值以及任何父游戏对象的z值之和。 ::: sidenote -各个 GUI 组件的渲染顺序 **不是** 由 GUI 组件的z值决定的. GUI 组件的渲染顺序由 [gui.set_render_order()](/ref/gui/#gui.set_render_order:order) 函数控制. +多个GUI组件的绘制顺序**不是**由GUI组件的z值决定的。GUI组件绘制顺序由[gui.set_render_order()](/ref/gui/#gui.set_render_order:order)函数控制。 ::: -例如: 两个游戏对象 A 和 B. B 是 A 的子集. B 有一个sprite组件. +示例:两个游戏对象A和B。B是A的子对象。B有一个精灵组件。 -| 元素 | z值 | -|----------|---------| -| A | 2 | -| B | 1 | -| B#sprite | 0.5 | +| 内容 | Z值 | +|----------|-----| +| A | 2 | +| B | 1 | +| B#sprite | 0.5 | ![](images/graphics/component-hierarchy.png) -在上述定义中 B 的sprite组件最终z值是 2 + 1 + 0.5 = 3.5. +使用上述层次结构,B上精灵组件的最终z值为2 + 1 + 0.5 = 3.5。 -::: sidenote -如果两个组件 z 值相同则可能造成两个组件来回穿梭闪烁或者不同平台顺序不同的结果. +::: important +如果两个组件具有完全相同的z值,则顺序是未定义的,您可能会遇到组件来回闪烁或组件在一个平台上以一种顺序渲染而在另一个平台上以另一种顺序渲染的情况。 -渲染脚本为 z 值定义了极近端和极远端平面. z值在此范围之外的组件不会被渲染. 默认范围是 -1 到 1 但是 [可以任意修改](/manuals/render/#默认视口映射). -Z 值得极近与极远坐标范围是 -1 到 1 的话, 需要很高的数值精度. 在处理 3D 资源时, 可能需要在你的自定义渲染脚本中修改极近和极远的坐标范围. 详情请见 [渲染教程](/manuals/render/). +渲染脚本为z值定义了近裁剪面和远裁剪面。任何z值超出此范围的组件都不会被渲染。默认范围是-1到1,但[可以轻松更改](/manuals/render/#default-view-projection)。当近和远限制为-1和1时,Z值的数值精度非常高。在处理3D资源时,您可能需要在自定义渲染脚本中更改默认投影的近和远限制。有关更多信息,请参见[渲染手册](/manuals/render/)。 ::: -:[组件最大数配置](../shared/component-max-count-optimizations.md) \ No newline at end of file +:[组件最大数量优化](../shared/component-max-count-optimizations.md) \ No newline at end of file diff --git a/docs/zh/manuals/compute.md b/docs/zh/manuals/compute.md new file mode 100644 index 00000000..cb8173cc --- /dev/null +++ b/docs/zh/manuals/compute.md @@ -0,0 +1,234 @@ +--- +title: Defold 计算着色器手册 +brief: 本手册解释了如何使用计算程序、着色器常量和采样器。 +--- + +# 计算程序 + +::: sidenote +Defold 中的计算着色器支持目前处于*技术预览*状态。 +这意味着某些功能尚不完善,并且 API 将来可能会发生变化。 +::: + +计算着色器是在 GPU 上执行通用计算的强大工具。它们允许您利用 GPU 的并行处理能力来执行物理模拟、图像处理等任务。计算着色器对存储在缓冲区或纹理中的数据进行操作,在许多 GPU 线程上并行执行操作。这种并行性使得计算着色器在密集计算方面非常强大。 + +* 有关渲染管线的更多信息,请参阅[渲染文档](/manuals/render)。 +* 有关着色器程序的深入解释,请参阅[着色器文档](/manuals/shader)。 + +## 我可以用计算着色器做什么? + +由于计算着色器旨在用于通用计算,因此您可以用它们做的事情实际上没有限制。以下是计算着色器通常用于的一些示例: + +图像处理 + - 图像过滤:应用模糊、边缘检测、锐化滤镜等。 + - 色彩分级:调整图像的色彩空间。 + +物理 + - 粒子系统:模拟大量粒子以产生烟雾、火焰和流体动力学等效果。 + - 软体物理:模拟可变形物体,如布丁和果冻。 + - 剔除:遮挡剔除、视锥体剔除 + +程序化生成 + - 地形生成:使用噪声函数创建详细地形。 + - 植被和树叶:创建程序化生成的植物和树木。 + +渲染效果 + - 全局光照:通过模拟光线在场景中反弹的方式来模拟逼真的照明。 + - 体素化:从网格数据创建 3D 体素网格。 + +## 计算着色器是如何工作的? + +在高层次上,计算着色器通过将一个任务划分为许多可以同时执行的小任务来工作。这是通过`工作组`和`调用`的概念实现的: + +工作组 +: 计算着色器在`工作组`的网格上运行。每个工作组包含固定数量的调用(或线程)。工作组的大小和调用的数量在着色器代码中定义。 + +调用 +: 每个调用(或线程)执行计算着色器程序。工作组内的调用可以通过共享内存共享数据,允许它们之间进行有效的通信和同步。 + +GPU 通过在多个工作组上并行启动许多调用来执行计算着色器,为适合的任务提供强大的计算能力。 + +## 创建计算程序 + +要创建计算程序,在*资源*浏览器中右键单击目标文件夹,然后选择新建... ▸ 计算。(您也可以从菜单中选择文件 ▸ 新建...,然后选择计算)。命名新的计算文件并按确定。 + +![计算文件](images/compute/compute_file.png) + +新的计算将在*计算编辑器*中打开。 + +![计算编辑器](images/compute/compute.png) + +计算文件包含以下信息: + +计算程序 +: 要使用的计算着色器程序文件(*`.cp`*)。该着色器操作"抽象工作项",这意味着输入和输出数据类型没有固定定义。程序员需要定义计算着色器应该产生什么。 + +常量 +: 将传递给计算着色器程序的统一变量。有关可用常量的列表,请参见下文。 + +采样器 +: 您可以选择在材质文件中配置特定的采样器。添加采样器,根据着色器程序中使用的名称命名它,并根据您的喜好设置包装和过滤设置。 + + +## 在 Defold 中使用计算程序 + +与材质不同,计算程序不分配给任何组件,也不属于正常渲染流程的一部分。计算程序必须在渲染脚本中`调度`才能执行任何工作。然而,在调度之前,您需要确保渲染脚本有对计算程序的引用。目前,渲染脚本了解计算程序的唯一方法是将其添加到包含对渲染脚本引用的 .render 文件中: + +![计算渲染文件](images/compute/compute_render_file.png) + +要使用计算程序,首先需要将其绑定到渲染上下文。这与材质的绑定方式相同: + +```lua +render.set_compute("my_compute") +-- 在此处执行计算工作,调用 render.set_compute() 解除绑定 +render.set_compute() +``` + +虽然计算常量将在程序调度时自动应用,但无法从编辑器中将任何输入或输出资源(纹理、缓冲区等)绑定到计算程序。相反,这必须通过渲染脚本完成: + +```lua +render.enable_texture("blur_render_target", "tex_blur") +render.enable_texture(self.storage_texture, "tex_storage") +``` + +要在您决定的工作空间中运行程序,您需要调度该程序: + +```lua +render.dispatch_compute(128, 128, 1) +-- dispatch_compute 也接受一个选项表作为最后一个参数 +-- 您可以使用此参数表将渲染常量传递给调度调用 +local constants = render.constant_buffer() +constants.tint = vmath.vector4(1, 1, 1, 1) +render.dispatch_compute(32, 32, 32, {constants = constants}) +``` + +### 从计算程序写入数据 + +目前,从计算程序生成任何类型的输出只能通过`存储纹理`完成。存储纹理类似于"常规纹理",只是它们支持更多功能和可配置性。顾名思义,存储纹理可以用作通用缓冲区,您可以从计算程序中读取和写入数据。然后,您可以将同一缓冲区绑定到不同的着色器程序以进行读取。 + +要在 Defold 中创建存储纹理,您需要从常规的 .script 文件执行此操作。渲染脚本没有此功能,因为动态纹理需要通过仅在常规 .script 文件中可用的资源 API 创建。 + +```lua +-- 在 .script 文件中: +function init(self) + -- 像往常一样创建纹理资源,但添加"storage"标志 + -- 以便它可以用作计算程序的后备存储 + local t_backing = resource.create_texture("/my_backing_texture.texturec", { + type = resource.TEXTURE_TYPE_IMAGE_2D, + width = 128, + height = 128, + format = resource.TEXTURE_FORMAT_RGBA32F, + flags = resource.TEXTURE_USAGE_FLAG_STORAGE + resource.TEXTURE_USAGE_FLAG_SAMPLE, + }) + + -- 从资源中获取纹理句柄 + local t_backing_handle = resource.get_texture_info(t_backing).handle + + -- 通知渲染器有关后备纹理的信息,以便它可以通过 render.enable_texture 绑定 + msg.post("@render:", "set_backing_texture", { handle = t_backing_handle }) +end +``` + +## 将所有内容整合在一起 + +### 着色器程序 + +```glsl +// compute.cp +#version 450 + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +// 指定输入资源 +uniform vec4 color; +uniform sampler2D texture_in; + +// 指定输出图像 +layout(rgba32f) uniform image2D texture_out; + +void main() +{ + // 这不是一个特别有趣的着色器,但它演示了 + // 如何从纹理和常量缓冲区读取并写入存储纹理 + + ivec2 tex_coord = ivec2(gl_GlobalInvocationID.xy); + vec4 output_value = vec4(0.0, 0.0, 0.0, 1.0); + vec2 tex_coord_uv = vec2(float(tex_coord.x)/(gl_NumWorkGroups.x), float(tex_coord.y)/(gl_NumWorkGroups.y)); + vec4 input_value = texture(texture_in, tex_coord_uv); + output_value.rgb = input_value.rgb * color.rgb; + + // 将输出值写入存储纹理 + imageStore(texture_out, tex_coord, output_value); +} +``` + +### 脚本组件 +```lua +-- 在 .script 文件中 + +-- 这里我们指定稍后将绑定到 +-- 计算程序的输入纹理。我们可以将此纹理分配给模型组件, +-- 或在渲染脚本中将其启用到渲染上下文。 +go.property("texture_in", resource.texture()) + +function init(self) + -- 像往常一样创建纹理资源,但添加"storage"标志 + -- 以便它可以用作计算程序的后备存储 + local t_backing = resource.create_texture("/my_backing_texture.texturec", { + type = resource.TEXTURE_TYPE_IMAGE_2D, + width = 128, + height = 128, + format = resource.TEXTURE_FORMAT_RGBA32F, + flags = resource.TEXTURE_USAGE_FLAG_STORAGE + resource.TEXTURE_USAGE_FLAG_SAMPLE, + }) + + local textures = { + texture_in = resource.get_texture_info(self.texture_in).handle, + texture_out = resource.get_texture_info(t_backing).handle + } + + -- 通知渲染器有关输入和输出纹理的信息 + msg.post("@render:", "set_backing_texture", textures) +end +``` + +### 渲染脚本 +```lua +-- 响应消息"set_backing_texture" +-- 为计算程序设置后备纹理 +function on_message(self, message_id, message) + if message_id == hash("set_backing_texture") then + self.texture_in = message.texture_in + self.texture_out = message.texture_out + end +end + +function update(self) + render.set_compute("compute") + -- 我们可以将纹理绑定到特定的命名常量 + render.enable_texture(self.texture_in, "texture_in") + render.enable_texture(self.texture_out, "texture_out") + render.set_constant("color", vmath.vector4(0.5, 0.5, 0.5, 1.0)) + -- 调度计算程序的次数与我们拥有的像素数一样多。 + -- 这构成了我们的"工作组"。着色器将被调用 + -- 128 x 128 x 1 次,或每个像素一次。 + render.dispatch_compute(128, 128, 1) + -- 当我们完成计算程序后,需要解除绑定 + render.set_compute() +end +``` + +## 兼容性 + +Defold 目前支持以下图形适配器中的计算着色器: + +- Vulkan +- Metal(通过 MoltenVK) +- OpenGL 4.3+ +- OpenGL ES 3.1+ + +::: sidenote +目前无法检查运行中的客户端是否支持计算着色器。 +这意味着如果图形适配器是基于 OpenGL 或 OpenGL ES 的,则无法保证客户端支持运行计算着色器。 +Vulkan 和 Metal 从 1.0 版本开始支持计算着色器。要使用 Vulkan,您需要创建自定义清单并选择 Vulkan 作为后端。 +::: \ No newline at end of file diff --git a/docs/zh/manuals/debugging-game-and-system-logs.md b/docs/zh/manuals/debugging-game-and-system-logs.md index 46ee34c9..4d02328f 100644 --- a/docs/zh/manuals/debugging-game-and-system-logs.md +++ b/docs/zh/manuals/debugging-game-and-system-logs.md @@ -1,59 +1,59 @@ --- title: 调试 - 游戏和系统日志 -brief: 本教程介绍了获取游戏和系统日志的方法. +brief: 本手册解释了如何读取游戏和系统日志。 --- # 游戏和系统日志 -游戏日志保存了引擎的所有输出, 包括原生扩展和脚本代码上的输出. 用 [print()](/ref/stable/base/#print:...) 和 [pprint()](/ref/stable/builtins/?q=pprint#pprint:v) 函数就能在日志里留下游戏输出. 可以使用原生扩展 [dmLog](/ref/stable/dmLog/) 下的函数读写日志文件. 各种文本阅读编辑器都能打开日志文件. +游戏日志显示了引擎、原生扩展和游戏逻辑的所有输出。您可以从脚本和Lua模块中使用[print()](/ref/stable/base/#print:...)和[pprint()](/ref/stable/builtins/?q=pprint#pprint:v)命令在游戏日志中显示信息。您可以使用[`dmLog`命名空间](/ref/stable/dmLog/)中的函数从原生扩展写入游戏日志。游戏日志可以从编辑器、终端窗口、使用平台特定工具或从日志文件中读取。 -系统日志是由操作系统提供的, 也许会对调试游戏有所帮助. 系统日志可以记录应用崩溃时的调用堆栈和内存不足等信息. +系统日志由操作系统生成,它可以提供帮助您 pinpoint 问题的附加信息。系统日志可以包含崩溃的堆栈跟踪和低内存警告。 -::: sidenote -游戏日志只会在 debug 模式的编译版本中出现. 发布版本会去掉所有日志输出. +::: important +游戏日志只会在调试构建中显示信息。在发布构建中,日志将完全为空。 ::: -## 用编辑器查看游戏日志 +## 从编辑器读取游戏日志 -当在编辑器本地或者连接了 [mobile 开发版应用](/manuals/dev-app) 运行游戏时, 所有日志输出都能做编辑器控制台看到: +当您从编辑器本地运行游戏或连接到[移动开发应用](/manuals/dev-app)时,所有输出将显示在编辑器的控制台窗格中: ![Editor 2](images/editor/editor2_overview.png) -## 用控制台查看游戏日志 +## 从终端读取游戏日志 -当使用控制台启动游戏时, 日志文件也会打印在当前控制台上. 在 Windows 和 Linux 控制台上直接打游戏可执行文件名即可. 在 macOS 控制台上要打 .app 文件里面的游戏引擎名: +当您从终端运行Defold游戏时,日志将显示在终端窗口本身中。在Windows和Linux上,您在终端中键入可执行文件的名称来启动游戏。在macOS上,您需要从.app文件内启动引擎: ``` -$ > ./mygame.app/Contenst/MacOS/mygame +$ > ./mygame.app/Contents/MacOS/mygame ``` -## 不同平台的日志读取 +## 使用平台特定工具读取游戏和系统日志 ### HTML5 -绝大多数浏览器都提供了显示日志输出的控制台. +可以使用大多数浏览器提供的开发者工具读取日志。 * [Chrome](https://developers.google.com/web/tools/chrome-devtools/console) - 菜单 > 更多工具 > 开发者工具 -* [Firefox](https://developer.mozilla.org/en-US/docs/Tools/Browser_Console) - 工具 > 网络开发者 > 网络控制台 +* [Firefox](https://developer.mozilla.org/en-US/docs/Tools/Browser_Console) - 工具 > Web开发者 > Web控制台 * [Edge](https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide/console) * [Safari](https://support.apple.com/guide/safari-developer/log-messages-with-the-console-dev4e7dedc90/mac) - 开发 > 显示JavaScript控制台 ### Android -可以使用 Android Debug Bridge (ADB) 工具来查看游戏和系统日志. +您可以使用Android调试桥(ADB)工具查看游戏和系统日志。 :[Android ADB](../shared/android-adb.md) - 工具安装好之后, 通过 USB 连接你的设备, 启动控制台, 输入: +安装并设置好后,通过USB连接设备,打开终端并运行: ```txt $ cd /platform-tools/ $ adb logcat ``` -设备会把所有日志信息打印在当前控制台上, 包含游戏输出信息. +设备随后会将所有输出转储到当前终端,以及来自游戏的任何打印信息。 -要想只查看 Defold 输出的日志信息, 可以这么输入: +如果您只想查看Defold应用程序输出,请使用此命令: ```txt $ cd /platform-tools/ @@ -71,33 +71,36 @@ D/defold ( 6210): DEBUG:SCRIPT: Hello there, log! ### iOS -可以使用 [控制台工具](https://support.apple.com/guide/console/welcome/mac) 读取游戏和系统日志. 可以使用 LLDB 调试器连接设备上运行的游戏. 调试前确保设备上存有该应用的 “Apple Developer Provisioning Profile”. 从编辑器的打包对话框那里提供档案文件 (只能在 macOS 平台上打包 iOS 应用). +您有多种选择可以在iOS上读取游戏和系统日志: -需要一个叫做 [ios-deploy](https://github.com/phonegap/ios-deploy) 的工具才能进行调试. 命令如下: +1. 您可以使用[控制台工具](https://support.apple.com/guide/console/welcome/mac)读取游戏和系统日志。 +2. 您可以使用LLDB调试器附加到设备上运行的游戏。要调试游戏,它需要使用包含您要调试的设备的"Apple Developer Provisioning Profile"进行签名。从编辑器打包游戏,并在打包对话框中提供配置文件(iOS打包仅在macOS上可用)。 + +要启动游戏并附加调试器,您需要一个名为[ios-deploy](https://github.com/phonegap/ios-deploy)的工具。通过在终端中运行以下命令来安装和调试您的游戏: ```txt ios-deploy --debug --bundle # 注意: 不是 .ipa 文件 ``` -它会把应用安装到设备上, 启动应用并且把 LLDB 调试器连接到应用上. 如果不熟悉 LLDB, 请参考 [LLDB 基础教程](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-basics.html). +这将在您的设备上安装应用程序,启动它并自动将LLDB调试器附加到它。如果您是LLDB的新手,请阅读[LLDB入门](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-basics.html)。 -## 从日志文件中读取日志信息 +## 从日志文件读取游戏日志 -如果你在 *game.project* 文件里打开了 *Write Log* 项, 所有游戏输出都会被记录到硬盘上, 保存为 "`log.txt`" 文件. 下面介绍了从设备上获取日志文件的方法: +如果您在*game.project*中启用*Write Log*设置,任何游戏输出都将写入磁盘,到一个名为"`log.txt`"的文件中。以下是在设备上运行游戏时如何提取文件的方法: iOS -: 把设备连接到安装有 macOS 和 Xcode 的电脑上. +: 将设备连接到安装了macOS和Xcode的计算机上。 - 启动 Xcode 选择 Window ▸ Devices and Simulators. + 打开Xcode并转到Window ▸ Devices and Simulators。 - 在列表中选择你的设备, 然后在 *Installed Apps* 列表中选择你的游戏应用. + 在列表中选择您的设备,然后在*Installed Apps*列表中选择相关的应用程序。 - 点击列表下方的齿轮图标选择 Download Container.... + 单击列表下方的齿轮图标并选择Download Container...。 ![download container](images/debugging/download_container.png) - 容器被下载解压之后就可以在 *Finder* 中看到了. 右键单击容器选择 Show Package Content. 找到 "`log.txt`", 一般位于 "`AppData/Documents/`". + 容器提取后,它将显示在*Finder*中。右键单击容器并选择Show Package Content。找到文件"`log.txt`",它应该位于"`AppData/Documents/`"中。 -Android -: "`log.txt`" 的获取取决于操作系统版本和制造商. 这里有一个简单的 [步骤教程](https://stackoverflow.com/a/48077004/129360). +Android( +: 提取"`log.txt`"的能力取决于操作系统版本和制造商。这里有一个简短的简单的[逐步指南](https://stackoverflow.com/a/48077004/]129360)。 diff --git a/docs/zh/manuals/debugging-game-logic.md b/docs/zh/manuals/debugging-game-logic.md index b1ba1879..77bdf65d 100644 --- a/docs/zh/manuals/debugging-game-logic.md +++ b/docs/zh/manuals/debugging-game-logic.md @@ -1,139 +1,139 @@ --- title: Defold 中的调试 -brief: 本教程介绍了 Defold 中自带的调试功能. +brief: 本手册解释了 Defold 中提供的调试功能。 --- # 调试游戏逻辑 -Defold 内置集成式 Lua 调试器及检视工具. 加上内置的 [分析工具](/manuals/profiling) 构成强大工具集帮你快速找到逻辑错误和性能问题. +Defold 包含一个带有检查功能的集成 Lua 调试器。与内置的[分析工具](/manuals/profiling)一起,它是一个强大的工具,可以帮助您找到游戏逻辑中错误的原因或帮助分析性能问题。 -## 打印输出与可视调试 +## 打印和可视化调试 -Defold 最基础的调试功能是 [控制台打印输出](http://en.wikipedia.org/wiki/Debugging#Techniques). 使用 `print()` 或 [`pprint()`](/ref/builtins#pprint) 来检查变量值或者逻辑流程. 如果有个游戏对象不正常, 只需要将包含调试代码的脚本组件拖上去就行了. 打印输出函数会把信息发送到编辑器的 *控制台* 与 [游戏日志](/manuals/debugging-game-and-system-logs) 中. +在 Defold 中调试游戏的最简单方法是使用[打印调试](http://en.wikipedia.org/wiki/Debugging#Techniques)。使用 `print()` 或 [`pprint()`](/ref/builtins#pprint) 语句来观察变量或指示执行流程。如果一个没有脚本的游戏对象表现异常,您可以只附加一个脚本,其唯一目的是调试。使用任何打印函数都会在编辑器的*控制台*视图和[游戏日志](/manuals/debugging-game-and-system-logs)中打印。 -更进一步来说, 引擎有绘制调试信息的功能, 可以显示文字和画线. 用此功能需要向 `@render` 接口发送消息: +除了打印之外,引擎还可以在屏幕上绘制调试文本和直线。这是通过向 `@render` 套接字发布消息来完成的: ```lua --- 把变量 "my_val" 画在屏幕上 +-- 在屏幕上用调试文本绘制 "my_val" 的值 msg.post("@render:", "draw_text", { text = "My value: " .. my_val, position = vmath.vector3(200, 200, 0) }) --- 画出带颜色的文字 +-- 在屏幕上绘制彩色文本 local color_green = vmath.vector4(0, 1, 0, 1) msg.post("@render:", "draw_debug_text", { text = "Custom color", position = vmath.vector3(200, 180, 0), color = color_green }) --- 在主角和敌人之间画一条线 +-- 在屏幕上绘制玩家和敌人之间的调试线 local start_p = go.get_position("player") local end_p = go.get_position("enemy") local color_red = vmath.vector4(1, 0, 0, 1) msg.post("@render:", "draw_line", { start_point = start_p, end_point = end_p, color = color_red }) ``` -调试绘制与普通的渲染处于同一个渲染管线之上. +可视化调试消息向渲染管线添加数据,并作为常规渲染管线的一部分进行绘制。 -* `"draw_line"` 实际是是使用渲染脚本的 `render.draw_debug3d()` 函数进行绘制的. -* `"draw_text"` 使用的是 `/builtins/fonts/debug/always_on_top.font` 字体和 `/builtins/fonts/debug/always_on_top_font.material` 材质. -* `"draw_debug_text"` 与 `"draw_text"` 类似, 只是可以自定义文字颜色. +* `"draw_line"` 添加使用渲染脚本中的 `render.draw_debug3d()` 函数渲染的数据。 +* `"draw_text"` 使用 `/builtins/fonts/debug/always_on_top.font` 字体渲染,该字体使用 `/builtins/fonts/debug/always_on_top_font.material` 材质。 +* `"draw_debug_text"` 与 `"draw_text"` 相同,但它以自定义颜色渲染。 -注意一般调试信息都需要实时更新所以把它们放在 `update()` 函数中是个好主意. +请注意,您可能希望每帧更新此数据,因此在 `update()` 函数中发布这些消息是个好主意。 -## Running the debugger +## 运行调试器 -一种办法是通过 Debug ▸ Run with Debugger 运行游戏并且自动接入调试器, 另一种是通过 Debug ▸ Attach Debugger 把调试器接入正在运行中的游戏上. +要运行调试器,选择 Debug ▸ Start/Attach,这将以附加的调试器启动游戏或将调试器附加到已经运行的游戏上。 ![overview](images/debugging/overview.png) -调试器接入后, 就可以使用控制台上的调试控制按钮, 或者使用 Debug 菜单了: +一旦调试器附加,您就可以通过控制台中的调试器控制按钮或通过 Debug 菜单控制游戏的执行: Break : ![pause](images/debugging/pause.svg){width=60px .left} - 立即断下游戏. 游戏于此点暂停. 此时可以观察游戏状态, 逐步运行, 或者运行到下一个断点. 断点会在代码编辑器上标识出来: + 立即中断游戏执行。游戏将在其当前点中断。您现在可以检查游戏状态,逐步推进游戏,或继续运行直到下一个断点。当前执行点在代码编辑器中标记: ![script](images/debugging/script.png) Continue : ![play](images/debugging/play.svg){width=60px .left} - 继续运行游戏. 直到按下暂停键或者遇到断点. 如果遇到断点停下, 运行点会在代码编辑器的断点标识之上标识出来: + 继续运行游戏。游戏代码将继续运行,直到您按下暂停或执行遇到您设置的断点。如果执行在设置的断点处中断,则执行点在代码编辑器中断点标记上方标记: ![break](images/debugging/break.png) Stop : ![stop](images/debugging/stop.svg){width=60px .left} - 停止调试. 立即停止调试, 断开调试器, 关闭游戏. + 停止调试器。按下此按钮将立即停止调试器,将其从游戏中断开并终止正在运行的游戏。 Step Over : ![step over](images/debugging/step_over.svg){width=60px .left} - 步越. 步进时如果运行到某个 Lua 函数, 步越 _不会进入这个函数_ 而是执行它然后停在函数下一行上. 图中, 如果用户按下 "step over", 调试器会执行代码直至调用 `nextspawn()` 函数下面的 `end` 位置处: + 将程序执行推进一个步骤。如果执行涉及运行另一个 Lua 函数,执行_不会进入该函数_,而是继续运行并停止在函数调用下方的下一行。在此示例中,如果用户按下"step over",调试器将执行代码并停止在调用 `nextspawn()` 函数的行下方的 `end` 语句处: ![step](images/debugging/step.png) ::: sidenote -一行Lua代码不一定就是一句Lua表达式. 调试器按表达式步进, 也就是说有可能出现一行多个表达式的情况就要多按几下步进才会运行到下一行. +一行 Lua 代码并不对应单个表达式。在调试器中步进是一次推进一个表达式,这意味着目前您可能需要多次按下步进按钮才能推进到下一行。 ::: Step Into : ![step in](images/debugging/step_in.svg){width=60px .left} - 步入. 步进时如果运行到某个 Lua 函数, 步入 _会进入这个函数_. 一个函数调用会在调用堆栈上增加一项. 可以在堆栈列表上点选来查看各函数入口及其所有变量信息. 图中, 用户步入了 `nextspawn()` 函数: + 将程序执行推进一个步骤。如果执行涉及运行另一个 Lua 函数,执行_将进入该函数_。调用该函数会在调用堆栈中添加一个条目。您可以单击调用堆栈列表中的每个条目来查看该闭包中的入口点和所有变量的内容。在这里,用户已经进入了 `nextspawn()` 函数: ![step into](images/debugging/step_into.png) Step Out : ![step out](images/debugging/step_out.svg){width=60px .left} - 步出. 运行游戏直到函数出口. 如果步入了一个函数, 按 "step out" 按钮能运行代码到函数返回位置. + 继续执行直到从当前函数返回。如果您已经将执行步入一个函数,按下"step out"按钮将继续执行直到函数返回。 -设置/清除断点 -: 可以在代码中随意设置断点. 接入调试器的游戏运行时, 会在断点处暂停, 等待你的下一步交互. +设置和清除断点 +: 您可以在 Lua 代码中设置任意数量的断点。当游戏在附加了调试器的情况下运行时,它将在遇到的下一个断点处停止执行,并等待您的进一步交互。 ![add breakpoint](images/debugging/add_breakpoint.png) - 设置/清除断点, 可以在代码编辑器里行号右边右键点击. 还可以从菜单中选择 Edit ▸ Toggle Breakpoint. + 要设置或清除断点,请在代码编辑器中行号右侧的列中单击。您也可以从菜单中选择 Edit ▸ Toggle Breakpoint。 设置条件断点 -: 可以设置需要计算条件为真才触发的断点. 条件可以读取随着代码执行当前行的本地变量. +: 您可以将断点配置为包含需要评估为 true 才能触发断点的条件。该条件可以访问代码执行期间在该行可用的局部变量。 ![edit breakpoint](images/debugging/edit_breakpoint.png) - 要编辑断电条件, 右键点击代码编辑器行号的右边的列, 或者从菜单栏点选 Edit ▸ Edit Breakpoint. + 要编辑断点条件,请在代码编辑器中行号右侧的列中右键单击,或从菜单中选择 Edit ▸ Edit Breakpoint。 -执行Lua表达式 -: 调试器停在断点上时, 可以直接使用包含有当前上下文的 Lua 运行时. 在控制台底部输入表达式后按 回车键 来运行: +评估 Lua 表达式 +: 在调试器附加且游戏在断点处停止的情况下,可以使用带有当前上下文的 Lua 运行时。在控制台底部键入 Lua 表达式并按 Enter 来评估它们: ![console](images/debugging/console.png) - 目前不支持用表达式来修改变量. + 目前无法通过评估器修改变量。 -断开调试器 -: 通过选择 Debug ▸ Detach Debugger 可以把调试器从游戏上断开. 游戏会继续运行. +分离调试器 +: 选择 Debug ▸ Detach Debugger 将调试器从游戏中分离。它将立即继续运行。 ## Lua 调试库 -Lua 包含一个有用的调试库, 帮你查看 Lua 环境的底层. 详情请见: http://www.lua.org/pil/contents.html#23. +Lua 附带一个在某些情况下有用的调试库,特别是如果您需要检查 Lua 环境的内部。您可以在 [Lua 手册中关于调试库的章节](http://www.lua.org/pil/contents.html#23) 中找到更多相关信息。 -## 调试步骤 +## 调试检查清单 -如果发现错误或者bug, 建议进行如下调试: +如果您遇到错误或您的游戏行为不符合预期,这里有一个调试检查清单: -1. 检查控制台看看输出什么报错没有. +1. 检查控制台输出并验证没有运行时错误。 -2. 在适当地方加入 `print` 语句证明这段代码运行到了没有. +2. 向代码中添加 `print` 语句以验证代码确实在运行。 -3. 看看编辑器各种设置是否配置正确. 代码加到游戏对象上了吗? 输入得到焦点了吗? 输入消息监听对了吗? 材质上有着色程序吗? 等等. +3. 如果代码没有运行,请检查您是否已在编辑器中完成了代码运行所需的正确设置。脚本是否添加到正确的游戏对象?您的脚本是否获取了输入焦点?输入触发器是否正确?着色器代码是否添加到材质?等等。 -4. 如果某些代码取决于变量的值 (比如if语句), 使用 `print` 或者调试器看看那些变量值对不对. +4. 如果您的代码依赖于变量的值(例如在 if 语句中),要么在使用或检查这些变量的位置 `print` 这些值,要么使用调试器检查它们。 -有的 bug 藏得很深, 调试起来很费时, 需要一丝一丝地捋, 逐步缩小可能出错的范围最终消灭错误源头. 这种情况下建议使用 "二分法": +有时查找错误可能是一个困难和耗时的过程,需要您逐位检查代码,检查所有内容并缩小有问题的代码范围并消除错误源。这最好通过称为"分而治之"的方法来完成: -1. 线确定哪一半代码一定包含着错误. -2. 继续二分二分, 缩小范围. -3. 最终找到并消灭错误. +1. 确定哪一半(或更少)的代码必须包含错误。 +2. 再次确定那一半中的哪一半必须包含错误。 +3. 继续缩小必须导致错误的代码范围,直到找到它为止。 -祝你调试愉快! +祝您调试愉快! -## 物理引擎调试 +## 调试物理问题 -如果使用物理系统过程中发现错误请开启物理调试. 在 *game.project* 文件的 *Physics* 部分, 勾选 *Debug* 项: +如果您在物理方面遇到问题,碰撞没有按预期工作,建议启用物理调试。在 *game.project* 文件的 *Physics* 部分中勾选 *Debug* 复选框: ![physics debug setting](images/debugging/physics_debug_setting.png) -这样 Defold 就会把所有物理形状和碰撞接触点绘制到屏幕上: +当此复选框启用时,Defold 将绘制所有碰撞形状和碰撞接触点: ![physics debug visualisation](images/debugging/physics_debug_visualisation.png) diff --git a/docs/zh/manuals/debugging-native-code-android.md b/docs/zh/manuals/debugging-native-code-android.md index 7d46398f..ec13b0dd 100644 --- a/docs/zh/manuals/debugging-native-code-android.md +++ b/docs/zh/manuals/debugging-native-code-android.md @@ -1,20 +1,20 @@ --- title: Android 平台调试 -brief: 本教程介绍了在使用 Android Studio 调试游戏的方法. +brief: 本手册描述了如何使用 Android Studio 调试构建版本。 --- # Android 平台调试 -下面介绍了如何使用 [Android Studio](https://developer.android.com/studio/), 即 Google 的 Android 操作系统的官方 IDE, 来调试游戏的方法. +这里我们描述如何使用 [Android Studio](https://developer.android.com/studio/)(Google 的 Android 操作系统的官方 IDE)来调试构建版本。 ## Android Studio -* 在 *game.project* 中设置 `android.debuggable` +* 通过在 *game.project* 中设置 `android.debuggable` 选项来准备捆绑包 ![android.debuggable](images/extensions/debugging/android/game_project_debuggable.png) -* 在 debug 模式下打包游戏 +* 在调试模式下将应用程序捆绑到您选择的文件夹中 ![bundle_android](images/extensions/debugging/android/bundle_android.png) @@ -24,60 +24,62 @@ brief: 本教程介绍了在使用 Android Studio 调试游戏的方法. ![debug_apk](images/extensions/debugging/android/android_profile_or_debug.png) -* 选择刚刚输出的apk文件 +* 选择您刚刚创建的 apk 捆绑包 ![select_apk](images/extensions/debugging/android/android_select_apk.png) -* 选择主 `.so` 文件, 确保里面含有调试信息 +* 选择主要的 `.so` 文件,并确保它具有调试符号 ![select_so](images/extensions/debugging/android/android_missing_symbols.png) -* 没有的话可以上传完整 `.so` 文件. (文件大约 20mb) +* 如果没有,上传一个未剥离的 `.so` 文件。(大小约为 20mb) -* 路径映射帮助建立从编译 (在云端) 到本地文件夹的文件对应关系. +* 路径映射帮助您重新映射从可执行文件构建位置(在云端)到本地驱动器上的实际文件夹的各个路径。 -* 选择 .so 文件, 添加路径映射 +* 选择 .so 文件,然后向您的本地驱动器添加映射 ![path_mapping1](images/extensions/debugging/android/path_mappings_android.png) ![path_mapping2](images/extensions/debugging/android/path_mappings_android2.png) -* 要是动过引擎源码, 也要对引擎代码添加路径映射 +* 如果您有权访问引擎源代码,也为其添加路径映射。 -* 注意一定要获取与你所用版本完全一致的引擎版本 +* 确保检出您当前正在调试的版本 defold$ git checkout 1.2.148 -* 点击 `Apply changes` +* 按 `Apply changes` -* 这时路径映射已经生效 +* 您现在应该在项目中看到映射的源代码 ![source](images/extensions/debugging/android/source_mappings_android.png) -* 加断点 +* 添加断点 ![breakpoint](images/extensions/debugging/android/breakpoint_android.png) -* 点击 `Run` -> `Debug "Appname"` 然后运行断点处的程序 +* 按 `Run` -> `Debug "Appname"` 并调用您打算中断进入的代码 ![breakpoint](images/extensions/debugging/android/callstack_variables_android.png) -* 步进可用, 变量和调用堆栈一目了然 +* 您现在可以在调用堆栈中步进以及检查变量 -## 注意 +## 注意事项 -### 原生扩展 job 文件夹 +### 原生扩展作业文件夹 -目前, 开发流程有点麻烦. 因为job文件夹名是随机的, 每次编译都不一样. +目前,工作流程对开发来说有点麻烦。这是因为作业文件夹名称对于每个构建都是随机的,使得每次构建的路径映射都无效。 -但是还是有办法使用的. +然而,它对于调试会话工作得很好。 -路径映射保存于 Android Studio 项目的 .iml 文件中. +路径映射存储在 Android Studio 项目的项目 `.iml` 文件中。 -运行下列命令就能得到job文件夹名 +可以从可执行文件获取作业文件夹 - $ arm-linux-androideabi-readelf --string-dump=.debug_str build/armv7-android/libdmengine.so | grep /job +```sh +$ arm-linux-androideabi-readelf --string-dump=.debug_str build/armv7-android/libdmengine.so | grep /job +``` -类似 `job1298751322870374150` 这样的名字, 后面的数字每次编译都不相同. +作业文件夹的命名类似于 `job1298751322870374150`,每次都有不同的随机数。 diff --git a/docs/zh/manuals/debugging-native-code-ios.md b/docs/zh/manuals/debugging-native-code-ios.md index abf77f7d..6f9bcaf7 100644 --- a/docs/zh/manuals/debugging-native-code-ios.md +++ b/docs/zh/manuals/debugging-native-code-ios.md @@ -1,136 +1,155 @@ --- title: 在 iOS/macOS 中调试 -brief: 本教程介绍了如何使用 Xcode 进行调试. +brief: 本手册描述了如何使用 Xcode 调试构建版本。 --- # 在 iOS/macOS 中调试 -这里我们介绍如何使用 [Xcode](https://developer.apple.com/Xcode/), Apple的 macOS 和 iOS 首选开发环境来调试应用. +这里我们描述如何使用 [Xcode](https://developer.apple.com/xcode/)(Apple 的 macOS 和 iOS 首选 IDE)来调试构建版本。 ## Xcode -* 使用 bob, 加上 `--with-symbols` 选项打包应用 ([更多详情](/manuals/debugging-native-code/#symbolicate-a-callstack)): +* 使用 bob,通过 `--with-symbols` 选项打包应用 ([更多信息](/manuals/debugging-native-code/#symbolicate-a-callstack)): - $ cd myproject - $ wget http://d.defold.com/archive//bob/bob.jar - $ java -jar bob.jar --platform armv7-darwin build --with-symbols --variant debug --archive bundle -bo build/ios -mp .mobileprovision --identity "iPhone Developer: Your Name (ID)" +```sh +$ cd myproject +$ wget http://d.defold.com/archive//bob/bob.jar +$ java -jar bob.jar --platform armv7-darwin build --with-symbols --variant debug --archive bundle -bo build/ios -mp .mobileprovision --identity "iPhone Developer: Your Name (ID)" +``` -* 安装应用, 可以通过 `Xcode`, `iTunes` 或者 [ios-deploy](https://github.com/ios-control/ios-deploy) +* 安装应用,可以通过 `Xcode`、`iTunes` 或 [ios-deploy](https://github.com/ios-control/ios-deploy) - $ ios-deploy -b .ipa +```sh +$ ios-deploy -b .ipa +``` -* 得到 `.dSYM` 文件夹 (即调试 symbols) +* 获取 `.dSYM` 文件夹(即调试符号) - * 如果没使用原生扩展, 可以从 [d.defold.com](http://d.defold.com) 下载 `.dSYM` 文件 + * 如果没有使用原生扩展,可以从 [d.defold.com](http://d.defold.com) 下载 `.dSYM` 文件 - * 如果使用了原生扩展, 可以使用 [bob.jar](https://www.defold.com/manuals/bob/) 生成 `.dSYM` 文件夹. 只需要 building (不需要 archive 和 bundling): + * 如果您正在使用原生扩展,那么在使用 [bob.jar](https://www.defold.com/manuals/bob/) 构建时会生成 `.dSYM` 文件夹。只需要构建(不需要打包或捆绑): - $ cd myproject - $ unzip .internal/cache/arm64-ios/build.zip - $ mv dmengine.dSYM .dSYM - $ mv .dSYM/Contents/Resources/DWARF/dmengine .dSYM/Contents/Resources/DWARF/ +```sh +$ cd myproject +$ unzip .internal/cache/arm64-ios/build.zip +$ mv dmengine.dSYM .dSYM +$ mv .dSYM/Contents/Resources/DWARF/dmengine .dSYM/Contents/Resources/DWARF/ +``` ### 创建项目 -要正确的调试, 我们需要一个项目, 以及一个代码映射(source map). -这次项目不是用来编译的, 只是调试举例. +要正确调试,我们需要有一个项目和映射的源代码。 +我们不是使用这个项目来构建东西,只是用于调试。 -*新建 Xcode 项目, 选择 `Game` 模板 +* 创建新的 Xcode 项目,选择 `Game` 模板 ![project_template](images/extensions/debugging/ios/project_template.png) -* 指定一个名字 (例如 `debug`) 并且使用默认设置 +* 选择一个名称(例如 `debug`)和默认设置 -* 选择一个存放项目的目录 +* 选择一个文件夹来保存项目 -* 为应用加入代码文件 +* 将您的代码添加到应用中 ![add_files](images/extensions/debugging/ios/add_files.png) -* 确保 "Copy items if needed" 未选中. +* 确保 "Copy items if needed" 未被选中。 ![add_source](images/extensions/debugging/ios/add_source.png) -* 结果是这样 +* 这是最终结果 ![added_source](images/extensions/debugging/ios/added_source.png) -* 关闭 `Build` 步骤 +* 禁用 `Build` 步骤 ![edit_scheme](images/extensions/debugging/ios/edit_scheme.png) ![disable_build](images/extensions/debugging/ios/disable_build.png) -* 设置 `Deployment target` 版本 +* 设置 `Deployment target` 版本,使其大于您设备的 iOS 版本 ![deployment_version](images/extensions/debugging/ios/deployment_version.png) -* 设置目标设备 +* 选择目标设备 ![select_device](images/extensions/debugging/ios/select_device.png) ### 启动调试器 -调试应用有如下方法 +您有几种选项来调试应用 -1. 可以使用 `Debug` -> `Attach to process...` 然后选择要调试应用 +1. 选择 `Debug` -> `Attach to process...` 并从那里选择应用 -1. 也可以选择 `Attach to process by PID or Process name` +2. 或者选择 `Attach to process by PID or Process name` ![select_device](images/extensions/debugging/ios/attach_to_process_name.png) -1. 从设备上启动应用 +3. 在设备上启动应用 -1. 在 `Edit Scheme` 中加入 .app 作为可运行文件夹 +4. 在 `Edit Scheme` 中将 .app 文件夹添加为可执行文件 -### 调试 symbols +### 调试符号 -**要使用 lldb, 运行必须先暂停** +**要使用 lldb,执行必须暂停** -* 把 `.dSYM` 目录加入到 lldb 中 +* 将 `.dSYM` 路径添加到 lldb - (lldb) add-dsym +``` +(lldb) add-dsym +``` ![add_dsym](images/extensions/debugging/ios/add_dsym.png) -* 确认 `lldb` 成功读取 symbols +* 验证 `lldb` 是否成功读取了符号 - (lldb) image list +``` +(lldb) image list +``` ### 路径映射 -* 加入引擎路径 (根据你的安装目录自行调整) +* 添加引擎源代码(根据您的需求进行相应更改) - (lldb) settings set target.source-map /Users/builder/ci/builds/engine-ios-64-master/build /Users/mathiaswesterdahl/work/defold - (lldb) settings append target.source-map /private/var/folders/m5/bcw7ykhd6vq9lwjzq1mkp8j00000gn/T/job4836347589046353012/upload/videoplayer/src /Users/mathiaswesterdahl/work/projects/extension-videoplayer-native/videoplayer/src +``` +(lldb) settings set target.source-map /Users/builder/ci/builds/engine-ios-64-master/build /Users/mathiaswesterdahl/work/defold +(lldb) settings append target.source-map /private/var/folders/m5/bcw7ykhd6vq9lwjzq1mkp8j00000gn/T/job4836347589046353012/upload/videoplayer/src /Users/mathiaswesterdahl/work/projects/extension-videoplayer-native/videoplayer/src +``` - * 从可运行文件夹里可以得到 job 文件夹. - job 文件夹命名类似这样 `job1298751322870374150`, 每次都是随机数字. +* 可以从可执行文件获取作业文件夹。作业文件夹命名为 `job1298751322870374150`,每次都有随机数。 - $ dsymutil -dump-debug-map 2>&1 >/dev/null | grep /job +```sh +$ dsymutil -dump-debug-map 2>&1 >/dev/null | grep /job +``` -* 验证路径映射 +* 验证源代码映射 - (lldb) settings show target.source-map +``` +(lldb) settings show target.source-map +``` -可以使用如下命令确定 symbol 的源代码文件 +您可以使用以下命令检查符号源自哪个源文件 - (lldb) image lookup -va +``` +(lldb) image lookup -va +``` ### 断点 -* 从 project 视图打开一个文件, 然后设置断点 +* 在项目视图中打开一个文件,并设置断点 ![breakpoint](images/extensions/debugging/ios/breakpoint.png) -## 注意 +## 注意事项 -### 检查二进制文件 UUID +### 检查二进制文件的 UUID -为了让调试器接受 `.dSYM` 文件夹, UUID 需要与可运行文件的 UUID 相匹配. 你可以这样检查 UUID: +为了让调试器接受 `.dSYM` 文件夹,UUID 需要与正在调试的可执行文件的 UUID 匹配。您可以像这样检查 UUID: - $ dwarfdump -u +```sh +$ dwarfdump -u +``` diff --git a/docs/zh/manuals/debugging-native-code.md b/docs/zh/manuals/debugging-native-code.md index c2662b83..ae59f1c8 100644 --- a/docs/zh/manuals/debugging-native-code.md +++ b/docs/zh/manuals/debugging-native-code.md @@ -1,23 +1,23 @@ --- title: Defold 中的原生代码调试 -brief: 本教程介绍了在 Defold 中调试原生代码的方法. +brief: 本手册解释了如何在 Defold 中调试原生代码。 --- # 原生代码调试 -Defold 几经测试鲜有崩溃情况出现. 但是崩溃这种事谁能保证永远避免, 尤其是游戏中还使用了原生扩展代码的情况下. 要是游戏崩溃或者原生代码出错请从下面几方面入手检查: +Defold 经过充分测试,在正常情况下应该很少崩溃。然而,无法保证它永远不会崩溃,特别是当您的游戏使用原生扩展时。如果您遇到崩溃或原生代码行为不符合预期的问题,有几种不同的解决方法: -* 使用调试器调试代码 -* 使用 print 函数检查代码 +* 使用调试器逐步执行代码 +* 使用打印调试 * 分析崩溃日志 -* 调用堆栈代码文件映射 +* 符号化调用堆栈 ## 使用调试器 -首先推荐使用 `调试器`. 使用它步进代码, 设置 `断点` 最重要的是游戏崩溃时会自动暂停. +最常见的方法是通过 `调试器` 运行代码。它允许您逐步执行代码,设置 `断点`,如果发生崩溃,它将停止执行。 -不同平台调试器有很多. +每个平台都有几种调试器。 * Visual studio - Windows * VSCode - Windows, macOS, Linux @@ -27,120 +27,136 @@ Defold 几经测试鲜有崩溃情况出现. 但是崩溃这种事谁能保证 * lldb / gdb - macOS, Linux, (Windows) * ios-deploy - macOS -每个工具可以调试的应用如下: +每个工具可以调试特定平台: -* Visual studio - Windows + platforms supporting gdbserver (比如 Linux/Android) -* VSCode - Windows, macOS (lldb), Linux (lldb/gdb) + platforms supporting gdbserver -* Xcode - macOS, iOS ([详见](/manuals/debugging-native-code-ios)) -* Android Studio - Android ([详见](/manuals/debugging-native-code-android)) +* Visual studio - Windows + 支持 gdbserver 的平台(例如 Linux/Android) +* VSCode - Windows, macOS (lldb), Linux (lldb/gdb) + 支持 gdbserver 的平台 +* Xcode - macOS, iOS ([了解更多](/manuals/debugging-native-code-ios)) +* Android Studio - Android ([了解更多](/manuals/debugging-native-code-android)) * WinDBG - Windows * lldb/gdb - macOS, Linux, (iOS) -* ios-deploy - iOS (via lldb) +* ios-deploy - iOS (通过 lldb) -## 使用 print 函数 +## 使用打印调试 -调试最简单的方法就是使用 [print 函数](http://en.wikipedia.org/wiki/Debugging#Techniques). 位于 [dmLog 命名空间](/ref/stable/dmLog/) 下的 print 函数可以用来检查变量值或者用来检查程序执行流程. 它可以在 *控制台* 视图和 [游戏日志](/manuals/debugging-game-and-system-logs) 中输出数据. +调试原生代码的最简单方法是使用 [打印调试](http://en.wikipedia.org/wiki/Debugging#Techniques)。使用 [`dmLog` 命名空间](/ref/stable/dmLog/) 中的函数来观察变量或指示执行流程。使用任何日志函数都会在编辑器的 *控制台* 视图和 [游戏日志](/manuals/debugging-game-and-system-logs) 中打印输出。 -## 崩溃日志分析 +## 分析崩溃日志 -崩溃时, Defold 引擎保存了一个 `_crash` 日志文件. 其中包含了系统信息与崩溃信息. 其存放位置参考 [游戏日志输出](/manuals/debugging-game-and-system-logs) (不同设备, 系统, 位置不同). +如果 Defold 引擎发生硬崩溃,它会保存一个 `_crash` 文件。崩溃文件将包含有关系统以及崩溃的信息。[游戏日志输出](/manuals/debugging-game-and-system-logs) 将写入崩溃文件所在的位置(它根据操作系统、设备和应用程序而变化)。 -可以使用 [崩溃模块](https://www.defold.com/ref/crash/) 帮助分析这个文件. 推荐你阅读, 收集信息, 打印信息到控制台, 然后把信息发送到 [第三方分析服务](/tags/stars/analytics/) 上去. +您可以使用 [崩溃模块](https://www.defold.com/ref/crash/) 在后续会话中读取此文件。建议您读取文件,收集信息,将其打印到控制台,然后将其发送到支持收集崩溃日志的 [分析服务](/tags/stars/analytics/)。 ::: important -在 Windows 上还有个 `_crash.dmp` 文件被创建. 这个文件在调试崩溃时很有用. +在 Windows 上,还会生成一个 `_crash.dmp` 文件。此文件在调试崩溃时很有用。 ::: -### 从设备上获取崩溃日志 +### 从设备获取崩溃日志 -手机上的崩溃日志可以下载到本地以便查看. +如果崩溃发生在移动设备上,您可以选择将崩溃文件下载到您自己的计算机并在本地解析它。 #### Android -如果应用是 [可调式的](/manuals/project-settings/#Android), 就可以使用 [Android Debug Bridge (ADB) 工具](https://developer.android.com/studio/command-line/adb.html) 和 `adb shell` 命令得到崩溃日志: +如果应用是 [可调试的](/manuals/project-settings/#android),您可以使用 [Android Debug Bridge (ADB) 工具](https://developer.android.com/studio/command-line/adb.html) 和 `adb shell` 命令获取崩溃日志: -``` +```sh $ adb shell "run-as com.defold.example sh -c 'cat /data/data/com.defold.example/files/_crash'" > ./_crash ``` #### iOS -在 iTunes 里, 可以下载 app 容器. +在 iTunes 中,您可以查看/下载应用程序容器。 -在 `Xcode -> Devices` 窗口中也能获取到崩溃日志. +在 `Xcode -> Devices` 窗口中,您也可以选择崩溃日志。 -## 调用堆栈代码文件映射 +## 符号化调用堆栈 -从 `_crash` 文件或者 [日志文件](/manuals/debugging-game-and-system-logs), 都可以进行代码文件映射. 即把调用堆栈里的每个地址映射到文件名和代码行, 利于寻找代码的问题. +如果您从 `_crash` 文件或 [日志文件](/manuals/debugging-game-and-system-logs) 获取调用堆栈,您可以对其进行符号化。这意味着将调用堆栈中的每个地址转换为文件名和行号,这反过来有助于找出根本原因。 -注意引擎版本要选择正确. 不然映射会错乱. 使用 [bob](https://www.defold.com/manuals/bob/) 编译时命令行加入 [--with-symbols](https://www.defold.com/manuals/bob/) 或者在编辑器打包对话框里点选 "Generate debug symbols": +重要的是,您必须将正确的引擎与调用堆栈匹配,否则很可能会让您调试错误的内容!使用 [`--with-symbols`](https://www.defold.com/manuals/bob/) 标志与 [bob](https://www.defold.com/manuals/bob/) 捆绑,或者从编辑器的捆绑对话框中选中 "Generate debug symbols" 复选框: -* iOS - 在 `build/arm64-ios` 下的 `dmengine.dSYM.zip` 中包含有 iOS 编译用 debug symbols. -* macOS - 在 `build/x86_64-macos` 下的 `dmengine.dSYM.zip` 中包含有 macOS 编译用 debug symbols. -* Android - 在打包输出目录 `projecttitle.apk.symbols/lib/` 下包含有各架构编译用 debug symbols. -* Linux - 可执行文件本身包含 debug symbols. -* Windows - 在 `build/x86_64-win32` 下的 `dmengine.pdb` 中包含有 Windows 编译用 debug symbols. -* HTML5 - 在 `build/js-web` 或 `build/wasm-web` 下的 `dmengine.js.symbols` 中包含有 HTML5 编译用 debug symbols. +* iOS - `build/arm64-ios` 中的 `dmengine.dSYM.zip` 文件夹包含 iOS 构建的调试符号。 +* macOS - `build/x86_64-macos` 中的 `dmengine.dSYM.zip` 文件夹包含 macOS 构建的调试符号。 +* Android - `projecttitle.apk.symbols/lib/` 捆绑输出文件夹包含目标架构的调试符号。 +* Linux - 可执行文件包含调试符号。 +* Windows - `build/x86_64-win32` 中的 `dmengine.pdb` 文件包含 Windows 构建的调试符号。 +* HTML5 - `build/js-web` 或 `build/wasm-web` 中的 `dmengine.js.symbols` 文件包含 HTML5 构建的调试符号。 ::: important -对于游戏的每个发布版本一定要保留一套对应的调试数据. 不然的话原生扩展上线以后出错误就没法调试! 为了方便查看调用堆栈, 也要保存好对应的游戏引擎. +非常重要的一点是,您必须为您发布的每个公共版本保存调试符号,并且您知道调试符号属于哪个版本。如果您没有调试符号,您将无法调试任何原生崩溃!此外,您应该保留引擎的未剥离版本。这样可以最好地对调用堆栈进行符号化。 ::: -### 把 symbols 上传到 Google Play -可以 [上传 debug symbols 到 Google Play](https://developer.android.com/studio/build/shrink-code#android_gradle_plugin_version_40_or_earlier_and_other_build_systems) 以便让 Google Play 上的崩溃日志显示可读的调用堆栈. 详情请见 [原生代码调试教程](/manuals/debugging-native-code). +### 将符号上传到 Google Play +您可以 [将调试符号上传到 Google Play](https://developer.android.com/studio/build/shrink-code#android_gradle_plugin_version_40_or_earlier_and_other_build_systems),以便在 Google Play 中记录的任何崩溃都将显示符号化的调用堆栈。将 `projecttitle.apk.symbols/lib/` 捆绑输出文件夹的内容压缩。该文件夹包含一个或多个具有架构名称的子文件夹,如 `arm64-v8a` 和 `armeabi-v7a`。 -### Android调用堆栈映射 +### 符号化 Android 调用堆栈 -1. 从编译文件夹下找到引擎文件 +1. 从您的构建文件夹中获取引擎 +```sh $ ls /build//[lib]dmengine[.exe|.so] +``` -1. 解压引擎: +2. 解压到一个文件夹: +```sh $ unzip dmengine.apk -d dmengine_1_2_105 +``` -1. 找到地址 +3. 查找调用堆栈地址 - 例如下面这个文件 + 例如,在未符号化的调用堆栈中,它可能看起来像这样 `#00 pc 00257224 libmy_game_name.so` - 其中 *00257224* 就是地址 + 其中 *`00257224`* 是地址 -1. 映射地址 +4. 解析地址 +```sh $ arm-linux-androideabi-addr2line -C -f -e dmengine_1_2_105/lib/armeabi-v7a/libdmengine.so _address_ +``` -注意: 要是从 [Android 日志](/manuals/debugging-game-and-system-logs) 获取的调用堆栈数据, 可能需要使用 [ndk-stack](https://developer.android.com/ndk/guides/ndk-stack.html) 进行地址解析 +注意:如果您从 [Android 日志](/manuals/debugging-game-and-system-logs) 获取堆栈跟踪,您可能可以使用 [ndk-stack](https://developer.android.com/ndk/guides/ndk-stack.html) 对其进行符号化 -### iOS 调用堆栈映射 +### 符号化 iOS 调用堆栈 -1. 如果使用了原生扩展, 服务器会为你提供映射数据 (.dSYM) 文件 (使用 bob.jar 连同 `--with-symbols` 参数) +1. 如果您正在使用原生扩展,服务器可以为您提供符号(.dSYM)(将 `--with-symbols` 传递给 bob.jar) +```sh $ unzip /build/arm64-darwin/build.zip - # 文件会被解压到 Contents/Resources/DWARF/dmengine + # 它将产生一个 Contents/Resources/DWARF/dmengine +``` -1. 如果没用原生扩展, 直接下载映射文件: +2. 如果您没有使用原生扩展,下载原始符号: +```sh $ wget http://d.defold.com/archive//engine/arm64-darwin/dmengine.dSYM +``` -1. 地址映射 +3. 使用加载地址进行符号化 - 不能直接使用堆栈里的地址 (比如载入地址 0x0) + 出于某种原因,简单地放入调用堆栈中的地址不起作用(即加载地址 0x0) +```sh $ atos -arch arm64 -o Contents/Resources/DWARF/dmengine 0x1492c4 +``` - # 也不能作为参数加入载入地址 + # 直接指定加载地址也不起作用 +```sh $ atos -arch arm64 -o MyApp.dSYM/Contents/Resources/DWARF/MyApp -l0x100000000 0x1492c4 +``` - 二者相加才可以: + 将加载地址添加到地址中起作用: +```sh $ atos -arch arm64 -o MyApp.dSYM/Contents/Resources/DWARF/MyApp 0x1001492c4 dmCrash::OnCrash(int) (in MyApp) (backtrace_execinfo.cpp:27) +``` diff --git a/docs/zh/manuals/debugging.md b/docs/zh/manuals/debugging.md index b4278cb6..3fec33d7 100644 --- a/docs/zh/manuals/debugging.md +++ b/docs/zh/manuals/debugging.md @@ -1,11 +1,11 @@ --- title: Defold 调试 -brief: 本教程介绍了 Defold 的内置调试工具. +brief: 本手册解释了 Defold 中提供的调试功能。 --- # 调试 -Defold 内置集成式 Lua 调试器与检视工具. 加上内置的 [分析工具](/manuals/profiling) 一起提供了强大的游戏调试除错性能分析功能. 万一游戏崩溃了 Defold 还能提供游戏引擎崩溃日志. +Defold 包含一个带有检查功能的集成式 Lua 调试器。与内置的 [分析工具](/manuals/profiling) 相结合,它是一个强大的工具,可以帮助您找出游戏逻辑中错误的原因或帮助分析性能问题。在引擎本身崩溃的罕见情况下,Defold 还提供崩溃日志。 -* 参考 [调试游戏逻辑](/manuals/debugging-game-logic). -* 参考 [调试原生代码](/manuals/debugging-native-code). +* 了解有关 [调试游戏逻辑](/manuals/debugging-game-logic) 的更多信息。 +* 了解有关 [调试原生代码](/manuals/debugging-native-code) 的更多信息。 diff --git a/docs/zh/manuals/design.md b/docs/zh/manuals/design.md index 7b30d9b7..67367832 100644 --- a/docs/zh/manuals/design.md +++ b/docs/zh/manuals/design.md @@ -1,24 +1,24 @@ --- -title: Defold 设计理念 -brief: Defold 设计幕后理念 +title: Defold 的设计 +brief: Defold 设计背后的理念 --- -# Defold 的设计理念 +# Defold 的设计 -Defold 为以下目的而创建: +Defold 是为以下目标而创建的: -- 为游戏团队提供专业的迭代开发平台. -- 简单明了, 提供可视化解决方案和游戏常用功能与开发流程. -- 提供极轻量级的游戏开发平台. -- 提供高性能运行环境. -- 真正跨平台. +- 成为一个完整的、专业的、交钥匙式的游戏团队生产平台。 +- 简单明了,为常见的游戏开发架构和工作流程问题提供明确的解决方案。 +- 成为一个 blazing fast 的开发平台,非常适合迭代式游戏开发。 +- 在运行时具有高性能。 +- 真正的多平台。 -Defold编辑器与引擎就是为了实现上述目的而精心打造的. 如果你从其他平台转来, 可能会有点不习惯, 比如: +编辑器和引擎的设计经过精心打造,以实现这些目标。如果您有其他平台的经验,我们的一些设计决策可能与您习惯的不同,例如: -- 资源树与各种命名需要静态指定. 开始可能麻烦, 随着项目深入你就会觉得很方便. -- 我们鼓励简单封装的实体间互相传递消息. -- 没有面向对象的思想. -- API都是异步的. -- 渲染流程是可程式化可自定义的. -- 所有资源文件都是直白文本文件, 为了最大化适应 Git merge , 也为了方便用外部工具编辑. -- 资源可以实时修改然后在运行中的游戏里热重载, 这样特别方便开发迭代和做测试. +- 我们要求静态声明资源树和所有命名。这需要您付出一些初始努力,但从长远来看,极大地帮助了开发过程。 +- 我们鼓励简单封装实体之间的消息传递。 +- 没有面向对象的继承。 +- 我们的 API 是异步的。 +- 渲染管道是由代码驱动的,并且完全可定制。 +- 我们所有的资源文件都是简单的纯文本格式,针对 Git 合并以及使用外部工具导入和处理进行了最佳结构化。 +- 资源可以被更改并热重载到正在运行的游戏中,从而实现极快的迭代和实验。 diff --git a/docs/zh/manuals/dev-app.md b/docs/zh/manuals/dev-app.md index bea20dd4..f228d355 100644 --- a/docs/zh/manuals/dev-app.md +++ b/docs/zh/manuals/dev-app.md @@ -1,67 +1,67 @@ --- -title: 在设备上运行开发用app -brief: 本教程介绍了如何在设备上安装开发用app以方便开发流程. +title: 在设备上运行开发应用 +brief: 本手册解释了如何在设备上放置开发应用,以便在设备上进行迭代开发。 --- -# 移动端开发用app +# 移动开发应用 -开发用app 让你可以通过wifi把内容推送到设备上. 这样进行修改测试的时候就不用反复打包安装了. 只需要在设备上安装开发用app, 打开app然后在编辑器里选择设备作为推送目标即可. +开发应用允许您通过wifi将内容推送到它。这将大大减少迭代时间,因为您不必在每次希望测试更改时都进行打包和安装。您在设备上安装开发应用,启动应用,然后从编辑器中选择设备作为构建目标。 -## 安装开发用app +## 安装开发应用 -Debug 模式下编译的任何 iOS 或 Android 应用都可以作为开发用app. 事实上, 我们推荐这么做因为开发用app包含正确的项目配置而且拥有和开发时使用的相同的 [原生扩展](/manuals/extensions/). +任何以Debug模式打包的iOS或Android应用程序都可以作为开发应用。事实上,这是推荐的解决方案,因为开发应用将具有正确的项目设置,并使用与您正在处理的项目相同的[原生扩展](/manuals/extensions/)。 -从 Defold 1.4.0 版本开始可以给项目打空的 debug 包. 使用这个选项可以创建带原生扩展的应用版本, 适合于教程里提到的开发迭代. +从Defold 1.4.0开始,可以打包项目的Debug变体而不包含任何内容。使用此选项创建带有原生扩展的应用程序版本,适用于本手册中描述的迭代开发。 ![content less bundle](images/dev-app/contentless-bundle.png) -### Installing on iOS +### 在iOS上安装 -依照 [iOS 教程介绍的步骤](/manuals/ios/#creating-an-ios-application-bundle) 打包 iOS 应用. 记得 variant 要选择 Debug! +按照[iOS手册中的说明](/manuals/ios/#creating-an-ios-application-bundle)为iOS打包。确保选择Debug作为变体! -### Installing on Android +### 在Android上安装 -依照 [Android 教程介绍的步骤](https://defold.com/manuals/android/#creating-an-android-application-bundle) 打包 Android 应用. +按照[Android手册中的说明](https://defold.com/manuals/android/#creating-an-android-application-bundle)为Android打包。 -## 启动游戏 +## 启动您的游戏 -要在设备上启动游戏, 应用与编辑器之间需要互联, 可以使用 wifi 也可以使用 USB 线缆 (见下文). +要在设备上启动游戏,开发应用和编辑器必须能够通过相同的wifi网络或使用USB(见下文)进行连接。 -1. 确保编辑器处于运行中. -2. 在设备上启动开发用app. -3. 在编辑器的 Project ▸ Targets 中选择设备. -4. 选择 Project ▸ Build 运行游戏. 如果用网络连接的话可能需要等一小会儿. -5. 游戏运行时, 就可以照常使用 [热重载](/manuals/hot-reload/) 功能了. +1. 确保编辑器已启动并正在运行。 +2. 在设备上启动开发应用。 +3. 在编辑器中的Project ▸ Targets下选择您的设备。 +4. 选择Project ▸ Build来运行游戏。游戏启动可能需要一段时间,因为游戏内容通过网络流式传输到设备。 +5. 游戏运行时,您可以照常使用[热重载](/manuals/hot-reload/)。 -### 在 Windows 上使用 USB 连接 iOS 设备 +### 在Windows上使用USB连接iOS设备 -要在 Windows 上使用 USB 连接运行于 iOS 设备上的app, 首先 [安装 iTunes](https://www.apple.com/lae/itunes/download/). 安装完之后还需从iOS设备的设置菜单里 [开启 Personal Hotspot](https://support.apple.com/en-us/HT204023). 如果跳出 "Trust This Computer?" 则选择 Trust. 这样设备就会出现在 Project ▸ Targets 列表中了. +在Windows上通过USB连接到运行在iOS设备上的开发应用时,首先需要[安装iTunes](https://www.apple.com/lae/itunes/download/)。安装iTunes后,您还需要从设置菜单中[在iOS设备上启用个人热点](https://support.apple.com/en-us/HT204023)。如果您看到提示点击"Trust This Computer?"的警报,请点击Trust。当开发应用运行时,设备现在应该会显示在Project ▸ Targets下。 -### 在 Linux 上使用 USB 连接 iOS 设备 +### 在Linux上使用USB连接iOS设备 -Linux 上同样开启 Personal Hotspot 然后 "Trust This Computer". +在Linux上,当使用USB连接时,您需要从设置菜单中在设备上启用个人热点。如果您看到提示点击"Trust This Computer?"的警报,请点击Trust。当开发应用运行时,设备现在应该会显示在Project ▸ Targets下。 -### 在 macOS 上使用 USB 连接 iOS 设备 +### 在macOS上使用USB连接iOS设备 -当设备与 macOS 通过 USB 连线时, 新版本 iOS 能自动开启连接, Project ▸ Targets 会自动显示出设备. +在较新的iOS版本上,当在macOS上使用USB连接时,设备会自动在设备和计算机之间打开一个新的以太网接口。当开发应用运行时,设备应该会显示在Project ▸ Targets下。 -老iOS设备还是同样开启 Personal Hotspot 然后 "Trust This Computer". +在较旧的iOS版本上,当在macOS上使用USB连接时,您需要从设置菜单中在设备上启用个人热点。如果您看到提示点击"Trust This Computer?"的警报,请点击Trust。当开发应用运行时,设备现在应该会显示在Project ▸ Targets下。 -### 在 macOS 上使用 USB 连接 Android 设备 +### 在macOS上使用USB连接Android设备 -当设备处于 USB 共享模式时, 可以通过 USB 连接设备与 macOS. 在 macOS 上需要安装 [HoRNDIS](https://joshuawise.com/horndis#available_versions) 这类的第三方驱动程序. 当 HoRNDIS 安装好后还需要在 Security & Privacy 设置里允许其运行. 设备上开启 USB 共享模式后就会出现在 Project ▸ Targets 列表中了. +在macOS上,当设备处于USB网络共享模式时,可以通过USB连接到运行在Android设备上的开发应用。在macOS上,您需要安装第三方驱动程序,如[HoRNDIS](https://joshuawise.com/horndis#available_versions)。安装HoRNDIS后,您还需要通过安全性与隐私设置允许它运行。一旦启用USB网络共享,当开发应用运行时,设备将显示在Project ▸ Targets下。 -### 在 Windows 或 Linux 上使用 USB 连接 Android 设备 +### 在Windows或Linux上使用USB连接Android设备 -同样在设备上开启 USB 共享模式后就会出现在 Project ▸ Targets 列表中了. +在Windows和Linux上,当设备处于USB网络共享模式时,可以通过USB连接到运行在Android设备上的开发应用。一旦启用USB网络共享,当开发应用运行时,设备将显示在Project ▸ Targets下。 ## 故障排除 -无法下载应用 -: 确保你的设备 UDID 包含在手机应用签名 provisioning 中. +无法下载应用程序 +: 确保您的设备UDID包含在用于签署应用程序的移动配置中。 -Targets 菜单没有设备 -: 确保设备于计算机处于相同 wifi 网络之下. 确保使用 Debug 模式编译开发用app. +您的设备没有出现在Targets菜单中 +: 确保您的设备连接到与计算机相同的wifi网络。确保开发应用是以Debug模式构建的。 -弹出消息说版本不匹配 -: 这是由于更新了编辑器没更新应用. 用新编辑器重新编译安装应用即可. +游戏没有启动,并显示有关版本不匹配的消息 +: 当您将编辑器升级到最新版本时会发生这种情况。您需要构建并安装新版本。 diff --git a/docs/zh/manuals/editor-preferences.md b/docs/zh/manuals/editor-preferences.md index 9a4f312e..2dc8152c 100644 --- a/docs/zh/manuals/editor-preferences.md +++ b/docs/zh/manuals/editor-preferences.md @@ -1,68 +1,80 @@ --- -title: 编辑器配置 -brief: 可以通过设置窗口修改编辑器配置. +title: 编辑器首选项 +brief: 您可以从首选项窗口修改编辑器的设置。 --- -# 编辑器配置 +# 编辑器首选项 -可以通过设置窗口修改编辑器配置. 选择菜单栏 File -> Preferences 即可打开设置窗口. +您可以从首选项窗口修改编辑器的设置。首选项窗口通过 File -> Preferences 菜单打开。 -## General +## 通用 ![](images/editor/preferences_general.png) Load External Changes on App Focus -: 当编辑器获得焦点时扫描外部文件改变. +: 当编辑器获得焦点时启用外部更改扫描。 Open Bundle Target Folder -: 打包结束后自动打开输出文件夹. +: 启用打包过程完成后打开目标包文件夹。 Enable Texture Compression -: 开启编译 [纹理压缩](/manuals/texture-profiles). +: 为编辑器进行的所有构建启用[纹理压缩](/manuals/texture-profiles)。 Escape Quits Game -: 用 Esc 键关闭正在运行的编译好的游戏. +: 使用 Esc 键关闭正在运行的游戏构建。 Track Active Tab in Asset Browser -: 在 *编辑器* 面板编辑的文件自动在资源浏览器 (也叫 *Asset* 面板) 中选中. +: 在*编辑器*面板中选定标签页中编辑的文件将在资源浏览器(也称为*资源*面板)中被选中。 -Path to custom keymap -: [自定义快捷键](/manuals/editor-keyboard-shortcuts) 配置文件的绝对路径. +Lint Code on Build +: 构建项目时启用[代码检查](/manuals/writing-code/#linting-configuration)。此选项默认启用,但如果大型项目的代码检查耗时过长,可以禁用。 +Engine Arguments +: 当编辑器构建和运行时,将传递给dmengine可执行文件的参数。 +每行使用一个参数。例如: +``` +--config=bootstrap.main_collection=/my dir/1.collectionc +--verbose +--graphics-adapter=vulkan +``` -## Code + +## 代码 ![](images/editor/preferences_code.png) Custom Editor -: 自定义编辑器的绝对路径. 在 macOS 上应指向 .app 内的可执行程序 (比如 `/Applications/Atom.app/Contents/MacOS/Atom`). +: 外部编辑器的绝对路径。在macOS上,它应该是.app内部可执行文件的路径(例如 `/Applications/Atom.app/Contents/MacOS/Atom`)。 Open File -: 自定义编辑器开启时要打开的文件的表达式. 其中 `{file}` 在开启时会被真实文件名代替. +: 自定义编辑器用于指定要打开哪个文件的模式。模式 `{file}` 将被要打开的文件名替换。 Open File at Line -: 自定义编辑器开启时要打开的文件以及指定光标放置的行数. 表达式 `{file}` 在开启时会被真是文件名代替, 而 `{line}` 被行号代替. +: 自定义编辑器用于指定要打开哪个文件以及在哪个行号打开的模式。模式 `{file}` 将被要打开的文件名替换,`{line}` 将被行号替换。 Code editor font -: 代码编辑器里要使用的系统上已安装的字体名称. +: 在代码编辑器中使用的系统安装字体名称。 + +Zoom on Scroll +: 在代码编辑器中滚动时按住Cmd/Ctrl按钮是否更改字体大小。 -### 使用 Visual Studio Code 打开脚本文件 +### 在Visual Studio Code中打开脚本文件 ![](images/editor/preferences_vscode.png) -要从 Defold 编辑器里直接用 Visual Studio Code 打开脚本文件, 必须配置下列可执行文件地址: +要从Defold编辑器直接在Visual Studio Code中打开脚本文件,必须通过指定可执行文件的路径来设置以下设置: - MacOS: `/Applications/Visual Studio Code.app/Contents/MacOS/Electron` - Linux: `/usr/bin/code` - Windows: `C:\Program Files\Microsoft VS Code\Code.exe` - 配置打开指定文件和指定行号的参数: +设置这些参数以打开特定文件和行: -- 打开文件: `. {file}` -- 打开到行: `. -g {file}:{line}` +- Open File: `. {file}` +- Open File at Line: `. -g {file}:{line}` -其中 `.` 符号代表打开整个项目, 而不是指定文件. +这里的 `.` 字符是必需的,用于打开整个工作区,而不是单个文件。 ## 扩展 @@ -70,17 +82,33 @@ Code editor font ![](images/editor/preferences_extensions.png) Build Server -: 编译包含 [原生扩展](/manuals/extensions) 项目时使用的编译服务器的 URL. 可以在编译服务器的请求 URL 中加入用户名和验证令牌. 使用格式举例: `username:token@build.defold.com`. 在使用用户自己的编译服务器并开启认证时, 任天堂 Switch 编译需要这种用户认证 ([更多信息详见编译服务器文档](https://github.com/defold/extender/blob/dev/README_SECURITY.md)). 其中用户名和密码可以用系统环境变量 `DM_EXTENDER_USERNAME` 和 `DM_EXTENDER_PASSWORD` 来设置. +: 构建包含[原生扩展](/manuals/extensions)的项目时使用的构建服务器的URL。可以向URL添加用户名和访问令牌以进行构建服务器的身份验证访问。使用以下表示法指定用户名和访问令牌:`username:token@build.defold.com`。Nintendo Switch构建以及运行启用了身份验证的自己的构建服务器实例时需要身份验证访问([请参阅构建服务器文档](https://github.com/defold/extender/blob/dev/README_SECURITY.md)了解更多信息)。用户名和密码也可以设置为系统环境变量`DM_EXTENDER_USERNAME`和`DM_EXTENDER_PASSWORD`。 + +Build Server Username +: 用于身份验证的用户名。 + +Build Server Password +: 用于身份验证的密码,将加密存储在首选项文件中。 Build Server Headers -: 编译原生扩展时向服务器发送的额外的 header. 在使用 CloudFlare 服务或类似服务编译扩展时是很必要的. +: 构建原生扩展时发送到构建服务器的额外标头。对于使用CloudFlare服务或类似服务的扩展器很重要。 ## 工具 ![](images/editor/preferences_tools.png) ADB path -: 配置 [ADB](https://developer.android.com/tools/adb) 命令行工具的路径. 如果系统中安装了 ADB, 则 Defold 编辑器会使用它来安装和运行 Android APK 包到指定设备. 默认情况下, 编辑器会检查 ADB 是否安装在了默认位置, 如果需要手动指定路径则需配置该选项. +: 安装在此系统上的[ADB](https://developer.android.com/tools/adb)命令行工具的路径。如果系统上安装了ADB,Defold编辑器将使用它将打包的Android APK安装并运行到连接的Android设备上。默认情况下,编辑器检查ADB是否安装在已知位置,因此只有当ADB安装在自定义位置时才需要指定路径。 ios-deploy path -: 配置 [ios-deploy](https://github.com/ios-control/ios-deploy) 命令行工具的路径 (仅适用于 macOS). 与 ADB 路径类似, Defold 编辑器会使用该工具安装和运行 iOS 包到连接好的 iPhone 上. 默认情况下, 编辑器会检查 ios-deploy 是否安装在了默认位置, 如果需要手动指定路径则需配置该选项. \ No newline at end of file +: 安装在此系统上的[ios-deploy](https://github.com/ios-control/ios-deploy)命令行工具的路径(仅与macOS相关)。与ADB路径类似,Defold编辑器将使用此工具将打包的iOS应用程序安装并运行到连接的iPhone上。默认情况下,编辑器检查ios-deploy是否安装在已知位置,因此只有当您使用自定义安装的ios-deploy时才需要指定路径。 + +## 键映射 + +![](images/editor/preferences_keymap.png) + +您可以配置编辑器快捷键,既可以添加自定义快捷键,也可以删除内置快捷键。在快捷键表格中的单个命令上使用上下文菜单来编辑快捷键,或双击/按Enter打开新的快捷键弹出窗口。 + +一些快捷键可能有警告:它们使用橙色显示。将鼠标悬停在快捷键上以查看警告。典型警告包括: +- 可输入快捷键:所选快捷键可在文本输入中输入。确保该命令在代码编辑/文本输入上下文中处于关闭状态。 +- 冲突:同一快捷键分配给多个不同的命令。确保在调用快捷键时最多启用一个命令,否则编辑器将以未定义的方式执行分配的命令之一。 \ No newline at end of file diff --git a/docs/zh/manuals/editor-scripts-ui.md b/docs/zh/manuals/editor-scripts-ui.md new file mode 100644 index 00000000..fb4aeead --- /dev/null +++ b/docs/zh/manuals/editor-scripts-ui.md @@ -0,0 +1,370 @@ +--- +title: "编辑器脚本:UI" +brief: 本手册解释了如何使用Lua在编辑器中创建UI元素 +--- + +# 编辑器脚本和UI + +本手册解释了如何使用用Lua编写的编辑器脚本在编辑器中创建交互式UI元素。要开始使用编辑器脚本,请参阅[编辑器脚本手册](/manuals/editor-scripts)。您可以找到完整的编辑器API参考[这里](/ref/stable/editor-lua/)。目前,只能创建交互式对话框,尽管我们希望将来将UI脚本支持扩展到编辑器的其余部分。 + +## Hello world + +所有与UI相关的功能都存在于`editor.ui`模块中。这是一个带有自定义UI的编辑器脚本的最简单示例,可以帮助您入门: +```lua +local M = {} + +function M.get_commands() + return { + { + label = "Do with confirmation", + locations = {"View"}, + run = function() + local result = editor.ui.show_dialog(editor.ui.dialog({ + title = "Perform action?", + buttons = { + editor.ui.dialog_button({ + text = "Cancel", + cancel = true, + result = false + }), + editor.ui.dialog_button({ + text = "Perform", + default = true, + result = true + }) + } + })) + print('Perform action:', result) + end + } + } +end + +return M + +``` + +此代码片段定义了一个**View → Do with confirmation**命令。当您执行它时,您将看到以下对话框: + +![Hello world dialog](images/editor_scripts/perform_action_dialog.png) + +最后,在按Enter(或点击`Perform`按钮)后,您将在编辑器控制台中看到以下行: +``` +Perform action: true +``` + +## 基本概念 + +### 组件 + +编辑器提供了各种UI**组件**,可以组合这些组件来创建所需的UI。按照惯例,所有组件都使用一个称为**props**的表进行配置。组件本身不是表,而是编辑器用于创建UI的**不可变用户数据**。 + +### Props + +**Props**是定义组件输入的表。Props应被视为不可变的:原地修改props表不会导致组件重新渲染,但使用不同的表会。当组件实例接收到与前一个表浅不相等的props表时,UI会更新。 + +### 对齐 + +当组件在UI中被分配一些边界时,它将消耗整个空间,但这并不意味着组件的可见部分会拉伸。相反,可见部分将占据它所需的空间,然后它将在分配的边界内对齐。因此,大多数内置组件定义了`alignment`属性。 + +例如,考虑这个标签组件: +```lua +editor.ui.label({ + text = "Hello", + alignment = editor.ui.ALIGNMENT.RIGHT +}) +``` +可见部分是`Hello`文本,它在分配的组件边界内对齐: + +![Alignment](images/editor_scripts/alignment.png) + +## 内置组件 + +编辑器定义了各种可以一起使用来构建UI的内置组件。组件大致可以分为3类:布局、数据展示和输入。 + +### 布局组件 + +布局组件用于将其他组件彼此相邻放置。主要的布局组件是**`horizontal`**、**`vertical`**和**`grid`**。这些组件还定义了**padding**和**spacing**等属性,其中padding是从分配边界边缘到内容的空白空间,而spacing是子组件之间的空白空间: + +![Padding and Spacing](images/editor_scripts/padding_and_spacing.png) + +编辑器定义了`small`、`medium`和`large`padding和spacing常量。当涉及到spacing时,`small`用于单个UI元素的不同子元素之间的间距,`medium`用于单个UI元素之间的间距,而`large`是元素组之间的间距。默认spacing是`medium`。`large`的padding值表示从窗口边缘到内容的padding,`medium`是从重要UI元素边缘的padding,而`small`是上下文菜单和工具提示等小UI元素边缘的padding(尚未实现)。 + +**`horizontal`**容器将其子组件一个接一个地水平放置,始终使每个子组件的高度填充可用空间。默认情况下,每个子组件的宽度保持最小,但可以通过在子组件上将`grow`属性设置为`true`来使其占用尽可能多的空间。 + +**`vertical`**容器与水平容器类似,但是轴切换了。 + +最后,**`grid`**是一个容器组件,将其子组件布置在2D网格中,就像表格一样。网格中的`grow`设置适用于行或列,因此它不是在子组件上设置,而是在列配置表上设置。此外,网格中的子组件可以配置为使用`row_span`和`column_span`属性跨越多行或多列。网格对于创建多输入表单很有用: +```lua +editor.ui.grid({ + padding = editor.ui.PADDING.LARGE, -- 在对话框边缘添加padding + columns = {{}, {grow = true}}, -- 使第2列增长 + children = { + { + editor.ui.label({ + text = "Level Name", + alignment = editor.ui.ALIGNMENT.RIGHT + }), + editor.ui.string_field({}) + }, + { + editor.ui.label({ + text = "Author", + alignment = editor.ui.ALIGNMENT.RIGHT + }), + editor.ui.string_field({}) + } + } +}) +``` +上面的代码将产生以下对话框表单: + +![New Level Dialog](images/editor_scripts/new_level_dialog.png) + +### 数据展示组件 + +编辑器定义了4个数据展示组件: +- **`label`** — 文本标签,旨在与表单输入一起使用。 +- **`icon`** — 图标;目前,它只能用于呈现一小组预定义图标,但我们打算将来允许更多图标。 +- **`heading`** — 文本元素,旨在呈现例如表单或对话框中的标题行文本。`editor.ui.HEADING_STYLE`枚举定义了各种标题样式,包括HTML的`H1`-`H6`标题,以及编辑器特定的`DIALOG`和`FORM`。 +- **`paragraph`** — 文本元素,旨在呈现一段文本。与`label`的主要区别是段落支持自动换行:如果分配的边界在水平方向上太小,文本将换行,如果无法适应视图,可能会用`"..."`缩短。 + +### 输入组件 + +输入组件是为用户与UI交互而设计的。所有输入组件都支持`enabled`属性来控制交互是否启用,并定义各种回调属性,在交互时通知编辑器脚本。 + +如果您创建静态UI,只需定义简单修改局部变量的回调就足够了。对于动态UI和更高级的交互,请参阅[响应式](#reactivity)。 + +例如,可以像这样创建一个简单的静态新建文件对话框: +```lua +-- 初始文件名,将被对话框替换 +local file_name = "" +local create_file = editor.ui.show_dialog(editor.ui.dialog({ + title = "Create New File", + content = editor.ui.horizontal({ + padding = editor.ui.PADDING.LARGE, + spacing = editor.ui.SPACING.MEDIUM, + children = { + editor.ui.label({ + text = "New File Name", + alignment = editor.ui.ALIGNMENT.CENTER + }), + editor.ui.string_field({ + grow = true, + text = file_name, + -- 输入回调: + on_value_changed = function(new_text) + file_name = new_text + end + }) + } + }), + buttons = { + editor.ui.dialog_button({ text = "Cancel", cancel = true, result = false }), + editor.ui.dialog_button({ text = "Create File", default = true, result = true }) + } +})) +if create_file then + print("create", file_name) +end +``` +这是内置输入组件的列表: +- **`string_field`**、**`integer_field`**和**`number_field`**是单行文本字段的变体,允许编辑字符串、整数和数字。 +- **`select_box`**用于通过下拉控件从预定义的选项数组中选择一个选项。 +- **`check_box`**是一个带有`on_value_changed`回调的布尔输入字段 +- **`button`**带有在按钮按下时调用的`on_press`回调。 +- **`external_file_field`**是一个用于选择计算机上文件路径的组件。它由一个文本字段和一个打开文件选择对话框的按钮组成。 +- **`resource_field`**是一个用于选择项目中资源的组件。 + +除按钮外的所有组件都允许设置一个`issue`属性,显示与组件相关的问题(`editor.ui.ISSUE_SEVERITY.ERROR`或`editor.ui.ISSUE_SEVERITY.WARNING`),例如: +```lua +issue = {severity = editor.ui.ISSUE_SEVERITY.WARNING, message = "This value is deprecated"} +``` +当指定issue时,它会改变输入组件的外观,并添加带有问题消息的工具提示。 + +这是所有输入及其问题变体的演示: + +![Inputs](images/editor_scripts/inputs_demo.png) + +### 对话框相关组件 + +要显示对话框,您需要使用`editor.ui.show_dialog`函数。它需要一个**`dialog`**组件,该组件定义了Defold对话框的主要结构:`title`、`header`、`content`和`buttons`。对话框组件有点特殊:您不能将其用作另一个组件的子组件,因为它代表一个窗口,而不是UI元素。`header`和`content`是常规组件。 + +对话框按钮也很特殊:它们是使用**`dialog_button`**组件创建的。与常规按钮不同,对话框按钮没有`on_pressed`回调。相反,它们定义了一个`result`属性,该属性将在对话框关闭时由`editor.ui.show_dialog`函数返回。对话框按钮还定义了`cancel`和`default`布尔属性:带有`cancel`属性的按钮在用户按Escape或使用OS关闭按钮关闭对话框时触发,而`default`按钮在用户按Enter时触发。对话框按钮可以同时将`cancel`和`default`属性设置为`true`。 + +### 实用组件 + +此外,编辑器还定义了一些实用组件: +- **`separator`**是一条细线,用于分隔内容块 +- **`scroll`**是一个包装组件,当包装的组件不适合分配的空间时显示滚动条 + +## 响应式 + +由于组件是**不可变用户数据**,因此在创建后无法更改它们。那么如何使UI随时间变化呢?答案是:**响应式组件**。 + +::: sidenote +编辑器脚本UI的灵感来自[React](https://react.dev/)库,因此了解响应式UI和React hooks将有所帮助。 +::: + +在最简单的术语中,响应式组件是一个带有Lua函数的组件,该函数接收数据(props)并返回视图(另一个组件)。响应式组件函数可以使用**hooks**:`editor.ui`模块中的特殊函数,为您的组件添加响应式功能。按照惯例,所有hooks的名称都以`use_`开头。 + +要创建响应式组件,请使用`editor.ui.component()`函数。 + +让我们看这个示例——一个新建文件对话框,只有当输入的文件名不为空时才允许创建文件: + +```lua +-- 1. dialog是一个响应式组件 +local dialog = editor.ui.component(function(props) + -- 2. 组件定义了一个本地状态(文件名),默认为空字符串 + local name, set_name = editor.ui.use_state("") + + return editor.ui.dialog({ + title = props.title, + content = editor.ui.vertical({ + padding = editor.ui.PADDING.LARGE, + children = { + editor.ui.string_field({ + value = name, + -- 3. 输入+Enter更新本地状态 + on_value_changed = set_name + }) + } + }), + buttons = { + editor.ui.dialog_button({ + text = "Cancel", + cancel = true + }), + editor.ui.dialog_button({ + text = "Create File", + -- 4. 当名称存在时启用创建 + enabled = name ~= "", + default = true, + -- 5. 结果是名称 + result = name + }) + } + }) +end) + +-- 6. show_dialog将返回非空文件名或在取消时返回nil +local file_name = editor.ui.show_dialog(dialog({ title = "New File Name" })) +if file_name then + print("create " .. file_name) +else + print("cancelled") +end +``` + +当您执行运行此代码的菜单命令时,编辑器将显示一个在开始时禁用`"Create File"`对话框的对话框,但是当您输入名称并按Enter时,它将变为启用状态: + +![New File Dialog](images/editor_scripts/reactive_new_file_dialog.png) + +那么,它是如何工作的呢?在第一次渲染时,`use_state` hook创建一个与组件关联的本地状态,并返回它以及状态的setter。当调用setter函数时,它会安排组件重新渲染。在随后的重新渲染中,组件函数再次被调用,`use_state`返回更新的状态。然后,组件函数返回的新视图组件与旧组件进行差异比较,并在检测到更改的地方更新UI。 + +这种响应式方法大大简化了构建交互式UI并使其保持同步:而不是在用户输入时显式更新所有受影响的UI组件,视图被定义为输入(props和本地状态)的纯函数,编辑器自己处理所有更新。 + +### 响应式规则 + +编辑器期望响应式函数组件表现良好才能正常工作: + +1. 组件函数必须是纯函数。不保证何时或多久调用一次组件函数。所有副作用都应该在渲染之外,例如在回调中 +2. Props和本地状态必须是不可变的。不要改变props。如果您的本地状态是一个表,不要原地修改它,而是在状态需要更改时创建一个新表并将其传递给setter。 +3. 组件函数必须在每次调用时以相同的顺序调用相同的hooks。不要在循环中、条件块中、提前返回后等调用hooks。最佳实践是在组件函数的开头调用hooks,在任何其他代码之前。 +4. 只从组件函数调用hooks。Hooks在响应式组件的上下文中工作,因此只允许在组件函数中(或由组件函数直接调用的另一个函数中)调用它们。 + +### Hooks + +::: sidenote +如果您熟悉[React](https://react.dev/),您会注意到编辑器中的hooks在hook依赖项方面具有稍微不同的语义。 +::: + +编辑器定义了2个hooks:**`use_memo`**和**`use_state`**。 + +### **`use_state`** + +本地状态可以通过两种方式创建:使用默认值或使用初始化器函数: +```lua +-- 默认值 +local enabled, set_enabled = editor.ui.use_state(true) +-- 初始化器函数+参数 +local id, set_id = editor.ui.use_state(string.lower, props.name) +``` +类似地,可以使用新值或更新器函数调用setter: +```lua +-- 更新器函数 +local function increment_by(n, by) + return n + by +end + +local counter = editor.ui.component(function(props) + local count, set_count = editor.ui.use_state(0) + + return editor.ui.horizontal({ + spacing = editor.ui.SPACING.SMALL, + children = { + editor.ui.label({ + text = tostring(count), + alignment = editor.ui.ALIGNMENT.LEFT, + grow = true + }), + editor.ui.text_button({ + text = "+1", + on_pressed = function() set_count(increment_by, 1) end + }), + editor.ui.text_button({ + text = "+5", + on_pressed = function() set_count(increment_by, 5) end + }) + } + }) +end) +``` + +最后,状态可以被**重置**。当`editor.ui.use_state()`的任何参数更改时,状态会重置,使用`==`进行检查。因此,您不能使用字面量表或字面量初始化器函数作为`use_state` hook的参数:这会导致每次重新渲染时状态重置。举例说明: +```lua +-- ❌ 错误:字面量表初始化器在每次重新渲染时导致状态重置 +local user, set_user = editor.ui.use_state({ first_name = props.first_name, last_name = props.last_name}) + +-- ✅ 正确:在组件函数外部使用初始化器函数创建表状态 +local function create_user(first_name, last_name) + return { first_name = first_name, last_name = last_name} +end +-- ...稍后,在组件函数中: +local user, set_user = editor.ui.use_state(create_user, props.first_name, props.last_name) + + +-- ❌ 错误:字面量初始化器函数在每次重新渲染时导致状态重置 +local id, set_id = editor.ui.use_state(function() return string.lower(props.name) end) + +-- ✅ 正确:使用引用的初始化器函数创建状态 +local id, set_id = editor.ui.use_state(string.lower, props.name) +``` + +### **`use_memo`** + +您可以使用`use_memo` hook来提高性能。通常在渲染函数中执行一些计算,例如检查用户输入是否有效。`use_memo` hook可用于检查计算函数的参数是否更改比调用计算函数更便宜的情况。hook将在第一次渲染时调用计算函数,如果`use_memo`的所有参数都未更改,则在随后的重新渲染中重用计算值: +```lua +-- 组件函数外的验证函数 +local function validate_password(password) + if #password < 8 then + return false, "Password must be at least 8 characters long." + elseif not password:match("%l") then + return false, "Password must include at least one lowercase letter." + elseif not password:match("%u") then + return false, "Password must include at least one uppercase letter." + elseif not password:match("%d") then + return false, "Password must include at least one number." + else + return true, "Password is valid." + end +end + +-- ...稍后,在组件函数中 +local username, set_username = editor.ui.use_state('') +local password, set_password = editor.ui.use_state('') +local valid, message = editor.ui.use_memo(validate_password, password) +``` +在这个例子中,密码验证将在每次密码更改时运行(例如在密码字段中输入时),但在用户名更改时不会运行。 + +`use_memo`的另一个用例是创建然后在输入组件上使用的回调,或者当本地创建的函数用作另一个组件的prop值时——这可以防止不必要的重新渲染。 \ No newline at end of file diff --git a/docs/zh/manuals/editor-scripts.md b/docs/zh/manuals/editor-scripts.md index 625c37af..002e789d 100644 --- a/docs/zh/manuals/editor-scripts.md +++ b/docs/zh/manuals/editor-scripts.md @@ -1,26 +1,27 @@ --- title: 编辑器脚本 -brief: 本教程介绍了如何使用 Lua 扩展编辑器功能 +brief: 本手册解释了如何使用 Lua 扩展编辑器功能 --- # 编辑器脚本 -只需使用: `.editor_script` 扩展名的 Lua 脚本就可以创建自定义菜单项和编辑器生命周期回调. 使用这种方法, 你可以调整编辑器创建适合自己的开发流. +您可以使用具有特殊扩展名的 Lua 文件创建自定义菜单项和编辑器生命周期钩子:`.editor_script`。使用此系统,您可以调整编辑器以增强您的开发工作流程。 -## 编辑器脚本运行环境 +## 编辑器脚本运行时 -编辑器脚本运行于编辑器中, 在一个Java虚拟机下的Lua虚拟机下运行. 所有脚本共享一个环境, 也就是说它们能彼此访问. 你可以导入Lua模块, 就像 `.script` 文件一样, 但是编辑器内lua运行版本不同, 所以要注意代码兼容性. 编辑器使用 Lua 版本 5.2.x, 具体来说就是 [luaj](https://github.com/luaj/luaj) 运行时, 目前只有这个运行时能运行在Java虚拟机下. 除了这些, 还有一些限制: -- 没有 `debug` 和 `coroutine` 包; -- 没有 `os.execute` — 我们在 [actions](#actions) 部分提供了更有效安全的方法; -- 没有 `os.tmpname` 和 `io.tmpfile` — 目前编辑器可存取文件仅限于项目文件夹内的文件; -- 目前没有 `os.rename`, 以后可能加入; -- 没有 `os.exit` 和 `os.setlocale`. +编辑器脚本在编辑器内部运行,在 Java 虚拟机模拟的 Lua 虚拟机中运行。所有脚本共享同一个环境,这意味着它们可以相互交互。您可以导入 Lua 模块,就像使用 `.script` 文件一样,但编辑器内部运行的 Lua 版本不同,因此请确保您的共享代码是兼容的。编辑器使用 Lua 版本 5.2.x,更具体地说是 [luaj](https://github.com/luaj/luaj) 运行时,这目前是在 JVM 上运行 Lua 的唯一可行解决方案。除此之外,还有一些限制: +- 没有 `debug` 包; +- 没有 `os.execute`,尽管我们提供了类似的 `editor.execute()`; +- 没有 `os.tmpname` 和 `io.tmpfile` — 目前编辑器脚本只能访问项目目录内的文件; +- 目前没有 `os.rename`,尽管我们希望添加它; +- 没有 `os.exit` 和 `os.setlocale`。 +- 在编辑器需要脚本立即响应的上下文中,不允许使用一些长时间运行的函数,详见[执行模式](#execution-modes)。 -用编辑器脚本定义的编辑器扩展会在打开项目时加载. 获取依赖库时, 扩展会重新加载, 因为依赖库里有可能有扩展脚本存在. 重新加载时, 不会改变当前扩展脚本, 因为此时也许你正在编辑它们. 要完全重新加载, 可以使用 Project → Reload 编辑器命令. +所有在编辑器脚本中定义的编辑器扩展在您打开项目时都会加载。当您获取库时,扩展会重新加载,因为您依赖的库中可能有新的编辑器脚本。在此重新加载期间,不会获取您自己的编辑器脚本的更改,因为您可能正在更改它们。要同时重新加载它们,您应该运行 **Project → Reload Editor Scripts** 命令。 -## `.editor_script` 构成 +## `.editor_script` 的构成 -每个编辑器脚本需要返回一个模块, 如下: +每个编辑器脚本都应该返回一个模块,如下所示: ```lua local M = {} @@ -32,31 +33,257 @@ function M.get_language_servers() -- TODO - 定义语言服务器 end +function M.get_prefs_schema() + -- TODO - 定义首选项 +end + return M ``` -然后编辑器会收集项目中和共享库里的所有的编辑器脚本, 把它们加载到Lua虚拟机中并在需要的时候调用它们 (详情请见 [commands](#commands) 和 [lifecycle hooks](#lifecycle-hooks) 部分). +然后编辑器会收集项目和库中定义的所有编辑器脚本,将它们加载到单个 Lua 虚拟机中,并在需要时调用它们(更多内容请参见[命令](#commands)和[生命周期钩子](#lifecycle-hooks)部分)。 ## 编辑器 API -可以使用API中 `editor` 包与编辑器进行交互: -- `editor.platform` —字符串, 在Windows上是 `"x86_64-win32"`, 在macOS上是 `"x86_64-macos"`, 在Linux上是 `"x86_64-linux"`. -- `editor.get(node_id, property)` — 得到编辑器里某些节点的值. 编辑器里的节点是可变实体, 比如脚本或者集合文件, 集合中的游戏对象, 作为资源加载的 json 文件, 等等. `node_id` 是由编辑器发往编辑器脚本的一个 userdata. 或者, 可以用资源路径代替节点 id, 比如 `"/main/game.script"`. `property` 是一个字符串. 目前支持以下属性: - - `"path"` — 基于项目文件夹对 *resources* 的相对路径 — 资源即代表文件. 有效值举例: `"/main/game.script"` - - `"text"` — 可编辑文本资源文件 (比如脚本文件或者 json). 有效值举例: `"function init(self)\nend"`. 注意这里跟用 `io.open()` 读取文件不同, 文本资源可以只编辑不保存, 这些编辑仅在访问 `"text"` 属性时有效. - - 在大纲试图做点选操作时, 有些属性可以在属性面板显示出来. 可以显示的属性有: - - strings - - booleans - - numbers - - vec2/vec3/vec4 - - resources - - 注意这些属性有的不是只读的, 而且基于上下文有些可能不可用, 所以要在读取之前执行 `editor.can_get`, 设置之前执行 `editor.can_set`. 属性面板里用鼠标悬停在属性名上会显示一个信息提示标明该属性在编辑器脚本里是如何命名的. 资源属性赋值为 `""` 代表 nil 值. -- `editor.can_get(node_id, property)` — 检查属性是否可读, 确保 `editor.get()` 不会报错 -- `editor.can_set(node_id, property)` — 检查属性是否可写, 确保设置操作不会报错 -- `editor.create_directory(resource_path)` — 新建文件夹, 及其所有父文件夹 -- `editor.delete_directory(resource_path)` — 删除文件夹, 及其所有子文件夹和文件. - -## Command +您可以使用定义此 API 的 `editor` 包与编辑器进行交互: +- `editor.platform` — 字符串,Windows 上为 `"x86_64-win32"`,macOS 上为 `"x86_64-macos"`,Linux 上为 `"x86_64-linux"`。 +- `editor.version` — 字符串,Defold 的版本名称,例如 `"1.4.8"` +- `editor.engine_sha1` — 字符串,Defold 引擎的 SHA1 +- `editor.editor_sha1` — 字符串,Defold 编辑器的 SHA1 +- `editor.get(node_id, property)` — 获取编辑器内某个节点的值。编辑器中的节点是各种实体,例如脚本或集合文件,集合内的游戏对象,作为资源加载的 json 文件等。`node_id` 是由编辑器传递给编辑器脚本的 userdata。或者,您可以使用资源路径代替节点 id,例如 `"/main/game.script"`。`property` 是一个字符串。目前支持以下属性: + - `"path"` — *资源* 的项目文件夹文件路径 — 作为文件或目录存在的实体。返回值示例:`"/main/game.script"` + - `"children"` — 目录资源的子资源路径列表 + - `"text"` — 可编辑为文本的资源文本内容(例如脚本文件或 json)。返回值示例:`"function init(self)\nend"`。请注意,这与使用 `io.open()` 读取文件不同,因为您可以在不保存文件的情况下编辑文件,这些编辑仅在访问 `"text"` 属性时可用。 + - 对于图集:`images`(图集中图像的编辑器节点列表)和 `animations`(动画节点列表) + - 对于图集动画:`images`(与图集中的 `images` 相同) + - 对于瓦片地图:`layers`(瓦片地图中图层的编辑器节点列表) + - 对于瓦片地图图层:`tiles`(瓦片的无限 2D 网格),详见 `tilemap.tiles.*` + - 对于粒子效果:`emitters`(发射器编辑器节点列表)和 `modifiers`(修改器编辑器节点列表) + - 对于粒子效果发射器:`modifiers`(修改器编辑器节点列表) + - 对于碰撞对象:`shapes`(碰撞形状编辑器节点列表) + - 对于 GUI 文件:`layers`(图层编辑器节点列表) + - 当您在大纲视图中选择某些内容时,属性视图中显示的一些属性。这些类型的大纲属性支持: + - `strings` + - `booleans` + - `numbers` + - `vec2`/`vec3`/`vec4` + - `resources` + - `curves` + 请注意,其中一些属性可能是只读的,有些在不同上下文中可能不可用,因此您应该在读取它们之前使用 `editor.can_get`,在让编辑器设置它们之前使用 `editor.can_set`。将鼠标悬停在属性视图中的属性名称上,可以看到一个工具提示,其中包含有关该属性在编辑器脚本中如何命名的信息。您可以通过提供 `""` 值将资源属性设置为 `nil`。 +- `editor.can_get(node_id, property)` — 检查您是否可以获取此属性,以便 `editor.get()` 不会抛出错误。 +- `editor.can_set(node_id, property)` — 检查带有此属性的 `editor.tx.set()` 事务步骤不会抛出错误。 +- `editor.create_directory(resource_path)` — 如果目录不存在,则创建目录,以及所有不存在的父目录。 +- `editor.create_resources(resources)` — 创建 1 个或多个资源,可以从模板创建或使用自定义内容创建 +- `editor.delete_directory(resource_path)` — 如果目录存在,则删除目录,以及所有存在的子目录和文件。 +- `editor.execute(cmd, [...args], [options])` — 运行 shell 命令,可选择捕获其输出。 +- `editor.save()` — 将所有未保存的更改持久化到磁盘。 +- `editor.transact(txs)` — 使用 1 个或多个由 `editor.tx.*` 函数创建的事务步骤修改编辑器内存状态。 +- `editor.ui.*` — 各种与 UI 相关的函数,请参见[UI 手册](/manuals/editor-scripts-ui)。 +- `editor.prefs.*` — 与编辑器首选项交互的函数,请参见[首选项](#preferences)。 + +您可以在[此处](https://defold.com/ref/alpha/editor/)找到完整的编辑器 API 参考。 + +## 命令 + +如果编辑器脚本模块定义了 `get_commands` 函数,它将在扩展重新加载时被调用,返回的命令将在编辑器菜单栏或资源和大纲视图的上下文菜单中可用。例如: +```lua +local M = {} + +function M.get_commands() + return { + { + label = "Remove Comments", + locations = {"Edit", "Assets"}, + query = { + selection = {type = "resource", cardinality = "one"} + }, + active = function(opts) + local path = editor.get(opts.selection, "path") + return ends_with(path, ".lua") or ends_with(path, ".script") + end, + run = function(opts) + local text = editor.get(opts.selection, "text") + editor.transact({ + editor.tx.set(opts.selection, "text", strip_comments(text)) + }) + end + }, + { + label = "Minify JSON", + locations = {"Assets"}, + query = { + selection = {type = "resource", cardinality = "one"} + }, + active = function(opts) + return ends_with(editor.get(opts.selection, "path"), ".json") + end, + run = function(opts) + local path = editor.get(opts.selection, "path") + editor.execute("./scripts/minify-json.sh", path:sub(2)) + end + } + } +end + +return M +``` +编辑器期望 `get_commands()` 返回一个表数组,每个表描述一个单独的命令。命令描述包含: + +- `label`(必需)— 将显示给用户的菜单项上的文本 +- `locations`(必需)— 包含 `"Edit"`、`"View"`、`"Project"`、`"Debug"`、`"Assets"`、`"Bundle"`、`"Scene"` 或 `"Outline"` 的数组,描述此命令应该可用的位置。`"Edit"`、`"View"`、`"Project"` 和 `"Debug"` 表示顶部的菜单栏,`"Assets"` 表示资源视图中的上下文菜单,`"Outline"` 表示大纲视图中的上下文菜单,`"Bundle"` 表示 **Project → Bundle** 子菜单。 +- `query` — 命令向编辑器询问相关信息并定义其操作数据的方式。对于 `query` 表中的每个键,`active` 和 `run` 回调接收的 `opts` 表中将有相应的键。支持的键: + - `selection` 表示当有选中内容时此命令有效,并且它对该选择进行操作。 + - `type` 是命令感兴趣的选中节点类型,目前允许这些类型: + - `"resource"` — 在资源和大纲视图中,资源是具有相应文件的选中项。在菜单栏(Edit 或 View)中,资源是当前打开的文件; + - `"outline"` — 可以在大纲视图中显示的内容。在大纲视图中是选中项,在菜单栏中是当前打开的文件; + - `"scene"` — 可以渲染到场景中的内容。 + - `cardinality` 定义应该有多少个选中项。如果是 `"one"`,传递给命令回调的选择将是单个节点 id。如果是 `"many"`,传递给命令回调的选择将是一个或多个节点 id 的数组。 + - `argument` — 命令参数。目前,只有 `"Bundle"` 位置中的命令接收参数,当明确选择打包命令时为 `true`,在重新打包时为 `false`。 +- `id` - 命令标识符字符串,例如用于在 `prefs` 中持久化最后使用的打包命令 +- `active` - 一个回调函数,用于检查命令是否处于活动状态,预期返回布尔值。如果 `locations` 包括 `"Assets"`、`"Scene"` 或 `"Outline"`,在显示上下文菜单时将调用 `active`。如果位置包括 `"Edit"` 或 `"View"`,将在每次用户交互(例如键盘输入或鼠标点击)时调用 active,因此请确保 `active` 相对较快。 +- `run` - 当用户选择菜单项时执行的回调。 + +## 事务 + +事务是修改编辑器状态的方法。它们是原子的,这意味着它们要么全部成功,要么全部失败。事务由一个或多个步骤组成,每个步骤都是对编辑器状态的单个修改。您可以使用 `editor.tx.*` 函数创建事务步骤。以下是可用的事务步骤: + +- `editor.tx.set(node_id, property, value)` — 设置节点的属性值。 +- `editor.tx.create(resource_path, template_path, [data])` — 创建新资源。如果指定了 `template_path`,则从模板创建资源。`data` 是一个可选表,包含要传递给模板的数据。 +- `editor.tx.delete(resource_path)` — 删除资源。 +- `editor.tx.rename(resource_path, new_name)` — 重命名资源。 +- `editor.tx.move(resource_path, new_path)` — 移动资源。 +- `editor.tx.copy(resource_path, new_path)` — 复制资源。 +- `editor.tx.duplicate(resource_path, [new_name])` — 复制资源。如果指定了 `new_name`,则使用该名称作为新资源的名称。 +- `editor.tx.set_resource_property(resource_path, property, value)` — 设置资源的属性值。 +- `editor.tx.set_resource_properties(resource_path, properties)` — 设置资源的多个属性值。 +- `editor.tx.set_game_object_property(game_object_id, property, value)` — 设置游戏对象的属性值。 +- `editor.tx.set_game_object_properties(game_object_id, properties)` — 设置游戏对象的多个属性值。 +- `editor.tx.set_component_property(component_id, property, value)` — 设置组件的属性值。 +- `editor.tx.set_component_properties(component_id, properties)` — 设置组件的多个属性值。 +- `editor.tx.set_input_binding_property(input_binding_id, property, value)` — 设置输入绑定的属性值。 +- `editor.tx.set_input_binding_properties(input_binding_id, properties)` — 设置输入绑定的多个属性值。 +- `editor.tx.set_input_binding_game_object_property(input_binding_id, game_object_id, property, value)` — 设置输入绑定游戏对象的属性值。 +- `editor.tx.set_input_binding_game_object_properties(input_binding_id, game_object_id, properties)` — 设置输入绑定游戏对象的多个属性值。 +- `editor.tx.set_input_binding_component_property(input_binding_id, component_id, property, value)` — 设置输入绑定组件的属性值。 +- `editor.tx.set_input_binding_component_properties(input_binding_id, component_id, properties)` — 设置输入绑定组件的多个属性值。 +- `editor.tx.set_input_binding_input_property(input_binding_id, input_id, property, value)` — 设置输入绑定输入的属性值。 +- `editor.tx.set_input_binding_input_properties(input_binding_id, input_id, properties)` — 设置输入绑定输入的多个属性值。 +- `editor.tx.set_input_binding_input_game_object_property(input_binding_id, input_id, game_object_id, property, value)` — 设置输入绑定输入游戏对象的属性值。 +- `editor.tx.set_input_binding_input_game_object_properties(input_binding_id, input_id, game_object_id, properties)` — 设置输入绑定输入游戏对象的多个属性值。 +- `editor.tx.set_input_binding_input_component_property(input_binding_id, input_id, component_id, property, value)` — 设置输入绑定输入组件的属性值。 +- `editor.tx.set_input_binding_input_component_properties(input_binding_id, input_id, component_id, properties)` — 设置输入绑定输入组件的多个属性值。 +- `editor.tx.set_tilemap_layer_property(tilemap_id, layer_id, property, value)` — 设置瓦片地图图层的属性值。 +- `editor.tx.set_tilemap_layer_properties(tilemap_id, layer_id, properties)` — 设置瓦片地图图层的多个属性值。 +- `editor.tx.set_tilemap_tile_property(tilemap_id, layer_id, x, y, property, value)` — 设置瓦片地图瓦片的属性值。 +- `editor.tx.set_tilemap_tile_properties(tilemap_id, layer_id, x, y, properties)` — 设置瓦片地图瓦片的多个属性值。 +- `editor.tx.set_particlefx_emitter_property(particlefx_id, emitter_id, property, value)` — 设置粒子效果发射器的属性值。 +- `editor.tx.set_particlefx_emitter_properties(particlefx_id, emitter_id, properties)` — 设置粒子效果发射器的多个属性值。 +- `editor.tx.set_particlefx_modifier_property(particlefx_id, emitter_id, modifier_id, property, value)` — 设置粒子效果修改器的属性值。 +- `editor.tx.set_particlefx_modifier_properties(particlefx_id, emitter_id, modifier_id, properties)` — 设置粒子效果修改器的多个属性值。 +- `editor.tx.set_collisionobject_shape_property(collisionobject_id, shape_id, property, value)` — 设置碰撞对象形状的属性值。 +- `editor.tx.set_collisionobject_shape_properties(collisionobject_id, shape_id, properties)` — 设置碰撞对象形状的多个属性值。 +- `editor.tx.set_gui_layer_property(gui_id, layer_id, property, value)` — 设置 GUI 图层的属性值。 +- `editor.tx.set_gui_layer_properties(gui_id, layer_id, properties)` — 设置 GUI 图层的多个属性值。 +- `editor.tx.set_gui_node_property(gui_id, node_id, property, value)` — 设置 GUI 节点的属性值。 +- `editor.tx.set_gui_node_properties(gui_id, node_id, properties)` — 设置 GUI 节点的多个属性值。 + +> **注意:** 事务系统已弃用,请使用编辑器 API 函数代替。 + +要执行事务,请使用 `editor.transact(txs)` 函数,其中 `txs` 是事务步骤的列表。例如: +```lua +local txs = {} +txs[#txs + 1] = editor.tx.set(node_id, "position", vmath.vector3(100, 100, 0)) +txs[#txs + 1] = editor.tx.set(node_id, "rotation", vmath.quat_rotation_z(math.rad(45))) +editor.transact(txs) +``` + +## 生命周期钩子 + +您可以通过在 `hooks.editor_script` 文件中定义函数来响应编辑器中的各种事件: + +```lua +function on_build_started() + print("Build started") +end + +function on_build_completed() + print("Build completed") +end + +function on_build_failed() + print("Build failed") +end + +function on_editor_started() + print("Editor started") +end + +function on_editor_exiting() + print("Editor exiting") +end + +function on_editor_shutdown() + print("Editor shutdown") +end +``` + +可用的事件: +- `on_build_started()` — 当构建开始时调用。 +- `on_build_completed()` — 当构建完成时调用。 +- `on_build_failed()` — 当构建失败时调用。 +- `on_editor_started()` — 当编辑器启动时调用。 +- `on_editor_exiting()` — 当编辑器即将退出时调用。 +- `on_editor_shutdown()` — 当编辑器关闭时调用。 + +## 执行模式 + +编辑器脚本支持两种执行模式: + +### 即时模式 + +在即时模式下,脚本会立即执行,并阻塞编辑器直到完成。这是默认模式。 + +### 长时间运行模式 + +在长时间运行模式下,脚本会在后台执行,不会阻塞编辑器。要启用长时间运行模式,请在脚本开头添加以下代码: + +```lua +editor.set_async(true) +``` + +长时间运行模式有以下限制: +- 不能使用 `editor.transact()` 函数。 +- 不能使用 `editor.get()` 函数。 +- 不能使用 `editor.set()` 函数。 +- 不能使用 `editor.create()` 函数。 +- 不能使用 `editor.delete()` 函数。 +- 不能使用 `editor.reorder()` 函数。 +- 不能使用 `editor.add()` 函数。 +- 不能使用 `editor.remove()` 函数。 +- 不能使用 `editor.clear()` 函数。 +- 不能使用 `editor.execute()` 函数。 +- 不能使用 `editor.prefs()` 函数。 +- 不能使用 `editor.message()` 函数。 +- 不能使用 `editor.confirm()` 函数。 +- 不能使用 `editor.input()` 函数。 +- 不能使用 `editor.select()` 函数。 +- 不能使用 `editor.open()` 函数。 +- 不能使用 `editor.save()` 函数。 +- 不能使用 `editor.close()` 函数。 +- 不能使用 `editor.reload()` 函数。 +- 不能使用 `editor.exit()` 函数。 + +## 首选项 + +您可以使用 `editor.prefs()` 函数来访问和修改编辑器首选项: + +```lua +-- 获取首选项 +local value = editor.prefs("key") + +-- 设置首选项 +editor.prefs("key", value) +``` + +首选项是持久化的,即使编辑器关闭后也会保留。 如果编辑器脚本模块定义了 `get_commands` 函数, 它会在扩展重载时被调用, 返回的命令可以在编辑器菜单栏或者资源和大纲视图的右键菜单里使用. 例如: ```lua @@ -212,11 +439,73 @@ return M 更简单的办法是使用原生扩展插件系统. 首先在库目录创建 `ext.manifest` 文件, 然后在 `ext.manifest` 文件所在文件夹里创建 `plugins/bin/${platform}`. 该文件夹下的文件会被自动提取到 `/build/plugins/${extension-path}/plugins/bin/${platform}` 目录下, 可以在编辑器脚本中引用它们. +## HTTP 服务器 + +每个运行的编辑器实例都有一个 HTTP 服务器在运行。服务器可以通过编辑器脚本进行扩展。要扩展编辑器 HTTP 服务器,您需要添加 `get_http_server_routes` 编辑器脚本函数 — 它应该返回额外的路由: +```lua +print("My route: " .. http.server.url .. "/my-extension") + +function M.get_http_server_routes() + return { + http.server.route("/my-extension", "GET", function(request) + return http.server.response(200, "Hello world!") + end) + } +end +``` +重新加载编辑器脚本后,您将在控制台中看到以下输出:`My route: http://0.0.0.0:12345/my-extension`。如果您在浏览器中打开此链接,您将看到您的 `"Hello world!"` 消息。 + +输入的 `request` 参数是一个包含请求信息的简单 Lua 表。它包含诸如 `path`(以 `/` 开头的 URL 路径段)、请求 `method`(例如 `"GET"`)、`headers`(带有小写标题名称的表)以及可选的 `query`(查询字符串)和 `body`(如果路由定义了如何解释正文)等键。例如,如果您想创建一个接受 JSON 正文的路由,您可以使用 `"json"` 转换器参数定义它: +```lua +http.server.route("/my-extension/echo-request", "POST", "json", function(request) + return http.server.json_response(request) +end) +``` +您可以使用 `curl` 和 `jq` 在命令行中测试此端点: +```sh +curl 'http://0.0.0.0:12345/my-extension/echo-request?q=1' -X POST --data '{"input": "json"}' | jq +{ + "path": "/my-extension/echo-request", + "method": "POST", + "query": "q=1", + "headers": { + "host": "0.0.0.0:12345", + "content-type": "application/x-www-form-urlencoded", + "accept": "*/*", + "user-agent": "curl/8.7.1", + "content-length": "17" + }, + "body": { + "input": "json" + } +} +``` +路由路径支持可以从请求路径中提取并作为请求的一部分提供给处理函数的模式,例如: +```lua +http.server.route("/my-extension/setting/{category}.{key}", function(request) + return http.server.response(200, tostring(editor.get("/game.project", request.category .. "." .. request.key))) +end) +``` +现在,如果您打开例如 `http://0.0.0.0:12345/my-extension/setting/project.title`,您将看到从 `/game.project` 文件中获取的游戏标题。 + +除了单段路径模式外,您还可以使用 `{*name}` 语法匹配 URL 路径的其余部分。例如,这是一个简单的文件服务器端点,它从项目根目录提供文件: +```lua +http.server.route("/my-extension/files/{*file}", function(request) + local attrs = editor.external_file_attributes(request.file) + if attrs.is_file then + return http.server.external_file_response(request.file) + else + return 404 + end +end) +``` +现在,在浏览器中打开例如 `http://0.0.0.0:12345/my-extension/files/main/main.collection` 将显示 `main/main.collection` 文件的内容。 + ## Language servers -编辑器支持 [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) 的小子集. 我们计划以后全面支持 LSP 特性, 但是目前只支持显示编辑文件的代码审查 (比如 lints). +编辑器支持 [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) 的子集。虽然我们旨在未来扩展编辑器对 LSP 功能的支持,但目前它只能在编辑的文件中显示诊断(即 lints)并提供补全。 -要定义 language server, 需要设置编辑器脚本的 `get_language_servers` 函数如下: +要定义语言服务器,您需要像这样编辑编辑器脚本的 `get_language_servers` 函数: ```lua function M.get_language_servers() @@ -235,9 +524,131 @@ function M.get_language_servers() } end ``` -编辑器会使用指定 `command` 启动 language server, 使用服务器进程的标准输入和输出进行通信. +编辑器将使用指定的 `command` 启动语言服务器,使用服务器进程的标准输入和输出进行通信。 + +语言服务器定义表可以指定: +- `languages`(必需)— 服务器感兴趣的语言列表,如[此处](https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers)所定义(文件扩展名也可以工作); +- `command`(必需)- 命令及其参数的数组 +- `watched_files` - 带有 `pattern` 键(glob)的表数组,将触发服务器的[监视文件更改](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeWatchedFiles)通知。 + +## 首选项 + +编辑器脚本可以定义和使用首选项 — 存储在用户计算机上的持久化、未提交的数据。这些首选项具有三个关键特性: +- 类型化:每个首选项都有一个模式定义,包括数据类型和其他元数据,如默认值 +- 作用域:首选项的作用域可以是每个项目或每个用户 +- 嵌套:每个首选项键是一个点分隔的字符串,其中第一个路径段标识一个编辑器脚本,其余部分 + +所有首选项必须通过定义其模式来注册: +```lua +function M.get_prefs_schema() + return { + ["my_json_formatter.jq_path"] = editor.prefs.schema.string(), + ["my_json_formatter.indent.size"] = editor.prefs.schema.integer({default = 2, scope = editor.prefs.SCOPE.PROJECT}), + ["my_json_formatter.indent.type"] = editor.prefs.schema.enum({values = {"spaces", "tabs"}, scope = editor.prefs.SCOPE.PROJECT}), + } +end +``` +重新加载此类编辑器脚本后,编辑器将注册此模式。然后编辑器脚本可以获取和设置首选项,例如: +```lua +-- 获取特定首选项 +editor.prefs.get("my_json_formatter.indent.type") +-- 返回: "spaces" + +-- 获取整个首选项组 +editor.prefs.get("my_json_formatter") +-- 返回: +-- { +-- jq_path = "", +-- indent = { +-- size = 2, +-- type = "spaces" +-- } +-- } + +-- 一次设置多个嵌套首选项 +editor.prefs.set("my_json_formatter.indent", { + type = "tabs", + size = 1 +}) +``` + +## 执行模式 + +编辑器脚本运行时使用两种执行模式,这些模式对编辑器脚本基本上是透明的:**即时**和**长时间运行**。 + +**即时**模式用于编辑器需要尽快从脚本接收响应的情况。例如,菜单命令的 `active` 回调在即时模式下执行,因为这些检查是在编辑器 UI 线程上响应与编辑器的用户交互而执行的,并且应该在同一帧内更新 UI。 + +**长时间运行**模式用于编辑器不需要脚本即时响应的情况。例如,菜单命令的 `run` 回调在**长时间运行**模式下执行,允许脚本有更多时间来完成其工作。 + +编辑器脚本可以使用的一些函数可能需要很长时间才能运行。例如,`editor.execute("git", "status", {reload_resources=false, out="capture"})` 在足够大的项目上可能需要长达一秒钟的时间。为了保持编辑器的响应性和性能,在编辑器需要即时响应的情况下,不允许使用可能耗时的函数。尝试在即时上下文中使用此类函数将导致错误:`Cannot use long-running editor function in immediate context`。要解决此错误,请避免在即时上下文中使用此类函数。 + +以下函数被认为是长时间运行的,不能在即时模式下使用: +- `editor.create_directory()`、`editor.create_resources()`、`editor.delete_directory()`、`editor.save()`、`os.remove()` 和 `file:write()`:这些函数修改磁盘上的文件,导致编辑器将其内存中的资源树与磁盘状态同步,这在大型项目中可能需要几秒钟。 +- `editor.execute()`:执行 shell 命令可能需要不可预测的时间。 +- `editor.transact()`:对广泛引用的节点的大型事务可能需要数百毫秒,这对于 UI 响应性来说太慢了。 + +以下代码执行上下文使用即时模式: +- 菜单命令的 `active` 回调:编辑器需要在同一 UI 帧内从脚本接收响应。 +- 编辑器脚本的顶层:我们不期望重新加载编辑器脚本的行为有任何副作用。 + +## Actions + +::: sidenote +以前,编辑器以阻塞方式与 Lua VM 交互,因此编辑器脚本有一个硬性要求,即不能阻塞,因为某些交互必须从编辑器 UI 线程完成。因此,例如没有 `editor.execute()` 和 `editor.transact()`。执行脚本和更改编辑器状态是通过从钩子和命令 `run` 处理程序返回一个 "actions" 数组来触发的。 + +现在编辑器以非阻塞方式与 Lua VM 交互,因此不再需要这些操作:使用像 `editor.execute()` 这样的函数更方便、简洁和强大。这些操作现在已**弃用**,尽管我们没有计划删除它们。 +::: + +编辑器可以从命令的 `run` 函数或 `/hooks.editor_script` 的钩子函数返回一个操作数组。然后这些操作将由编辑器执行。 + +操作是描述编辑器应该做什么的表。每个操作都有一个 `action` 键。操作有两种类型:可撤销和不可撤销。 + +### 可撤销操作 + +::: sidenote +优先使用 `editor.transact()`。 +::: + +可撤销操作在执行后可以撤销。如果一个命令返回多个可撤销操作,它们将一起执行,并一起撤销。如果可以,您应该使用可撤销操作。它们的缺点是它们更受限制。 + +现有的可撤销操作: +- `"set"` — 将编辑器中节点的属性设置为某个值。例如: + ```lua + { + action = "set", + node_id = opts.selection, + property = "text", + value = "current time is " .. os.date() + } + ``` + `"set"` 操作需要以下键: + - `node_id` — 节点 id userdata。或者,您可以在这里使用资源路径代替从编辑器接收的节点 id,例如 `"/main/game.script"`; + - `property` — 要设置的节点属性,例如 `"text"`; + - `value` — 属性的新值。对于 `"text"` 属性,它应该是一个字符串。 + +### 不可撤销操作 + +::: sidenote +优先使用 `editor.execute()`。 +::: + +不可撤销操作会清除撤销历史记录,因此如果您想撤销此类操作,您将不得不使用其他方法,例如版本控制。 + +现有的不可撤销操作: +- `"shell"` — 执行 shell 脚本。例如: + ```lua + { + action = "shell", + command = { + "./scripts/minify-json.sh", + editor.get(opts.selection, "path"):sub(2) -- trim leading "/" + } + } + ``` + `"shell"` 操作需要 `command` 键,它是命令及其参数的数组。 + +### 混合操作和副作用 + +您可以混合可撤销和不可撤销操作。操作是按顺序执行的,因此根据操作的顺序,您将最终失去撤销该命令部分的能力。 -Language server 定义表可以指定: -- `languages` (必要) — 服务器支持的语言列表, 详见 [这里](https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers) (支持文件扩展名); -- `command` (必要) - 命令及其参数列表 -- `watched_files` - 一组带有 `pattern` 键 (a glob) 的表, 用来激活服务器的 [监视文件更改](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeWatchedFiles) 通知功能. +除了从期望它们的函数返回操作外,您可以直接使用 `io.open()` 读写文件。这将触发资源重新加载,从而清除撤销历史记录。 diff --git a/docs/zh/manuals/editor-templates.md b/docs/zh/manuals/editor-templates.md index 45da1e50..dd2b6479 100644 --- a/docs/zh/manuals/editor-templates.md +++ b/docs/zh/manuals/editor-templates.md @@ -1,23 +1,23 @@ --- -title: 项目模板 -brief: 在编辑器新建项目窗口中可以加入自定义模板. +title: 编辑器模板 +brief: 您可以在新建项目窗口中添加自己的自定义项目模板。 --- -# 项目模板 +# 编辑器模板 -在编辑器新建项目窗口中可以加入自定义模板.: +您可以在新建项目窗口中添加自己的自定义项目模板: ![custom project templates](images/editor/custom_project_templates.png) -要在窗口中添加自定义项目模板页需要在系统用户目录的 `.defold` 文件夹下加入 `welcome.edn` 文件: +为了添加一个或多个带有自定义项目模板的新标签页,您需要在用户主目录的 `.defold` 文件夹中添加一个 `welcome.edn` 文件: -* 找到系统用户目录下 `.defold`文件夹的位置: - * Windows `C:\Users\**Your Username**\.defold` - * macOS `/Users/**Your Username**/.defold` - * Linux `~/.defold` -* 在 `.defold` 文件夹下新建 `welcome.edn` 文件. +* 在您的用户主目录中创建一个名为 `.defold` 的文件夹。 + * 在 Windows 上:`C:\Users\**您的用户名**\.defold` + * 在 macOS 上:`/Users/**您的用户名**/.defold` + * 在 Linux 上:`~/.defold` +* 在 `.defold` 文件夹中创建一个 `welcome.edn` 文件。 -`welcome.edn` 文件使用的是可扩展数据注解格式. 例如: +`welcome.edn` 文件使用可扩展数据表示法(Extensible Data Notation)格式。例如: ``` {:new-project @@ -39,8 +39,8 @@ brief: 在编辑器新建项目窗口中可以加入自定义模板. } ``` -这样就建立好了如上图的模板列表. +这将创建如上截图所示的模板列表。 ::: sidenote -模板图片只能使用 [编辑器自带的图片](https://github.com/defold/defold/tree/dev/editor/resources/welcome/images). +您只能使用[随编辑器一起提供的模板图片](https://github.com/defold/defold/tree/dev/editor/resources/welcome/images)。 ::: diff --git a/docs/zh/manuals/editor.md b/docs/zh/manuals/editor.md index 07efb6dc..b7ab4cc5 100644 --- a/docs/zh/manuals/editor.md +++ b/docs/zh/manuals/editor.md @@ -1,27 +1,27 @@ --- title: 编辑器概述 -brief: 本教程介绍了 Defold 编辑器的外观和用法, 以及切换导航方法. +brief: 本手册提供了 Defold 编辑器的外观和工作方式的概述,以及如何在其中导航。 --- # 编辑器概述 -编辑器在布局排版, 切换导航上设计得尽量让游戏开发更有效率. 编辑各个视图里的文件时都会有适当的编辑器自动弹出以方便使用. +编辑器允许您以高效的方式浏览和操作游戏项目中的所有文件。编辑文件会调出合适的编辑器,并在单独的视图中显示文件的所有相关信息。 -## 打开编辑器 +## 启动编辑器 -打开 Defold 编辑器, 首先呈现的是一个项目选择和新建窗口. 通过点选上面的按钮可以: +当您运行 Defold 编辑器时,会看到一个项目选择和创建界面。点击选择您想要执行的操作: Home -: 显示最近编辑的项目以便快速打开. 这是默认视图. +: 点击显示您最近打开的项目,以便您可以快速访问它们。这是默认视图。 New Project -: 创建新的项目, 然后会让你 (从 *Template* 窗口) 选择是否需要基于模板创建新项目, 还可以选择是否要参考 (*Tutorial* 窗口) 里的教程, 或者学习 (*Sample* 窗口) 里的示例项目. +: 如果您想创建一个新的 Defold 项目,请点击此选项,然后选择您是否希望基于基本模板创建项目(来自*From Template*标签页),是否想要按照教程操作(*From Tutorial*标签页),或者尝试其中一个示例项目(*From Sample*标签页)。 ![new project](images/editor/new_project.png) - 新建项目完成后所有项目文件都保存在了本地硬盘上. + 当您创建一个新项目时,它会存储在您的本地驱动器上,您所做的任何编辑都会在本地保存。 -详情请见 [项目设立教程](https://www.defold.com/manuals/project-setup/). +您可以在[项目设置手册](https://www.defold.com/manuals/project-setup/)中了解有关不同选项的更多信息。 ## 编辑器面板 @@ -36,23 +36,26 @@ Defold 编辑器被划分为许多面板, 或称视图, 以展示和编辑数据 - 拖放 文件可以向项目中添加资源或者移动资源到想要的位置. - 右键单击 会弹出 _上下文菜单_ , 可以用来进行新建文件/文件夹, 重命名, 删除, 查看文件依赖等操作. -*Editor* 面板 +### 编辑器面板 +中心视图显示当前打开的文件在该文件类型的编辑器中。所有可视化编辑器都允许您更改摄像机视图: -: 中间的视图显示当前打开的文件. 所有可视视图都可以进行如下操作: +- 平移:Alt + 鼠标左键。 +- 缩放:Alt + 右键(三键鼠标)或 Ctrl + 鼠标键(单键鼠标)。如果您的鼠标有滚轮,可以使用它来缩放。 +- 3D旋转:Ctrl + 鼠标左键。 -- 平移: Alt + 鼠标左键. -- 缩放: Alt + 鼠标右键 或者使用鼠标滚轮. -- 旋转: Ctrl + 鼠标左键. - -场景视图右上角的工具栏里也有这些功能按钮: *平移*, *旋转* 和 *缩放*, 以及 *摄像机透视* 和 *可见性过滤*. +在场景视图的右上角有一个工具栏,您可以在其中找到对象操作工具:*移动*、*旋转*和*缩放*,以及*2D模式*、*摄像机透视*和*可见性过滤器*。 ![toolbar](images/editor/toolbar.png) -*Outline* 面板 -: 这个视图以一个树形结构展示当前打开文件的内容. 大纲树的内容与场景视图内容是一一对应的, 可以方便操作: - - 单击 选中一个物体. 按住 ShiftOption 键可以进行多选. - - 拖放 移动物体. 在集合里把一个游戏对象拖放到另一个游戏对象上可以建立父子关系. - - 右键单击 弹出 _上下文菜单_ 以便进行添加, 删除选中的物体等操作. +### 大纲面板 +此视图显示当前正在编辑的文件内容,但是以分层树结构的形式。大纲反映了编辑器视图,允许您对项目执行操作: + - 单击选择一个项目。按住ShiftOption键可以扩展选择。 + - 拖放移动项目。将游戏对象拖放到集合中的另一个游戏对象上可以使其成为子对象。 + - 右键单击打开_上下文菜单_,您可以从中添加项目、删除选中的项目等。 + +可以通过单击列表中元素右侧的小眼睛图标来切换游戏对象和可视化组件的可见性(Defold 1.9.8及更新版本)。 + +![toolbar](images/editor/outline.png) *Properties* 面板 : 这个视图显示出当前选中物体的属性, 比如位置, 旋转, 动画等等. @@ -120,18 +123,20 @@ Defold 编辑器被划分为许多面板, 或称视图, 以展示和编辑数据 ![Visibility filters](images/editor/visibilityfilters.png) -## 新建文件 +## 创建新的项目文件 -新建资源文件有两种方法, 通过点选菜单栏 File ▸ New... 按钮, 或者使用上下文菜单: +要创建新的资源文件,可以选择文件 ▸ 新建...,然后从菜单中选择文件类型,或者使用上下文菜单: -在 *资源* 浏览器目标位置 右键单击 , 选择 New... ▸ [file type] 按钮: +在*资源*浏览器中的目标位置右键单击,然后选择新建... ▸ [文件类型]: ![create file](images/editor/create_file.png) -为新文件取一个有意义的名字. 完整文件名包括类型扩展名会显示在 *路径* 对话框内: +为新文件键入合适的名称。包含文件类型后缀的完整文件名显示在对话框的*路径*下: ![create file name](images/editor/create_file_name.png) +可以为每个项目指定自定义模板。为此,请在项目根目录中创建一个名为`templates`的新文件夹,并添加名为`default.*`的新文件,并带有所需的扩展名,例如`/templates/default.gui`或`/templates/default.script`。此外,如果在这些文件中使用了`{{NAME}}`标记,它将被文件创建窗口中指定的文件名替换。 + ## 向项目添加资源文件 要向项目添加资源 (图片, 声音, 模型等) 文件, 只需把文件拖放到 *资源* 浏览器里适当的位置上. 这样做实际上是把文件系统中的资源文件 _拷贝_ 到项目中来. 详情请见 [导入资源教程](/manuals/importing-assets/). @@ -155,18 +160,34 @@ Defold 编辑器被划分为许多面板, 或称视图, 以展示和编辑数据 键盘快捷键及自定义方法详见 [键盘快捷键教程](/manuals/editor-keyboard-shortcuts). ## 编辑器日志 -使用编辑器时如果遇到了麻烦可以 [向我们汇报](/manuals/getting-help/#获得帮助), 并且连同编辑器日志一起上报. 编辑器日志存放路径如下: +如果您在使用编辑器时遇到问题并需要[报告问题](/manuals/getting-help/#getting-help),最好提供编辑器本身的日志文件。编辑器日志文件可以在以下位置找到: - * Windows: `C:\Users\ **Your Username** \AppData\Local\Defold` - * macOS: `/Users/ **Your Username** /Library/Application Support/` 或 `~/Library/Application Support/Defold` - * Linux: `~/.Defold` + * Windows: `C:\Users\ **您的用户名** \AppData\Local\Defold` + * macOS: `/Users/ **您的用户名** /Library/Application Support/` 或 `~/Library/Application Support/Defold` + * Linux: `$XDG_STATE_HOME/Defold` 或 `~/.local/state/Defold` -如果用命令行启动编辑器那么日志会显示在控制台上. 例如从 macOS 终端启动 Defold 编辑器: +如果编辑器是从终端/命令提示符启动的,您也可以在编辑器运行时访问编辑器日志。要从终端在macOS上启动编辑器: ``` $ > ./path/to/Defold.app/Contents/MacOS/Defold ``` +## 编辑器服务器 + +当编辑器打开项目时,它会在随机端口上启动一个Web服务器。该服务器可用于从其他应用程序与编辑器交互。从1.11.0版本开始,端口被写入`.internal/editor.port`文件中。 + +此外,从1.11.0版本开始,编辑器可执行文件有一个命令行选项`--port`(或`-p`),允许在启动时指定端口,例如: +```shell +# 在Windows上 +.\Defold.exe --port 8181 + +# 在Linux上: +./Defold --port 8181 + +# 在macOS上: +./Defold.app/Contents/MacOS/Defold --port 8181 +``` + -## 常見問題 +## 常见问题 :[Editor FAQ](../shared/editor-faq.md) diff --git a/docs/zh/manuals/extender-docker-images.md b/docs/zh/manuals/extender-docker-images.md new file mode 100644 index 00000000..3211e853 --- /dev/null +++ b/docs/zh/manuals/extender-docker-images.md @@ -0,0 +1,46 @@ +--- +title: 可用的Docker镜像 +brief: 文档描述了可用的Docker镜像和使用它们的Defold版本 +--- + +# 可用的Docker镜像 +以下是公共注册表中所有可用的Docker镜像列表。这些镜像可用于在不再支持旧SDK的环境中运行Extender。 + +|SDK |镜像标签 |平台名称(在Extender配置中) |使用该镜像的Defold版本 | +|------------------|---------------------------------------------------------------------------------------------------------|-----------------------------|-----------------------| +|Linux latest |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-linux-env:latest` |`linux-latest` |所有Defold版本 | +|Android NDK25 |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-android-ndk25-env:latest` |`android-ndk25` |从1.4.3开始 | +|Emscripten 2.0.11 |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-emsdk-2011-env:latest` |`emsdk-2011` |直到1.7.0 | +|Emscripten 3.1.55 |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-emsdk-3155-env:latest` |`emsdk-3155` |[1.8.0-1.9.3] | +|Emscripten 3.1.65 |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-emsdk-3165-env:latest` |`emsdk-3165` |从1.9.4开始 | +|Winsdk 2019 |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-winsdk-2019-env:latest` |`winsdk-2019` |直到1.6.1 | +|Winsdk 2022 |`europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-winsdk-2022-env:latest` |`winsdk-2022` |从1.6.2开始 | + +# 如何使用旧的Docker镜像 +要使用旧环境,您应该按照以下步骤操作: +1. 修改Extender存储库中的`docker-compose.yml`文件[链接](https://github.com/defold/extender/blob/dev/server/docker/docker-compose.yml)。需要添加一个带有必要Docker镜像的服务定义。例如,如果我们想使用包含Emscripten 2.0.11的Docker镜像,我们需要添加以下服务定义: + ```yml + emscripten_2011-dev: + image: europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-emsdk-2011-env:latest + extends: + file: common-services.yml + service: remote_builder + profiles: + - all + - web + networks: + default: + aliases: + - emsdk-2011 + ``` + 重要字段包括: + * **profiles** - 触发服务启动的配置文件列表。配置文件名称通过`--profile `参数传递给`docker compose`命令。 + * **networks** - Docker容器应使用的网络列表。运行Extender使用名为`default`的网络。重要的是设置服务网络别名(它将在后续的Extender配置中使用)。 +2. 在[`application-local-dev-app.yml`](https://github.com/defold/extender/blob/dev/server/configs/application-local-dev-app.yml)的`extender.remote-builder.platforms`部分添加远程构建器的定义。在我们的示例中,它将如下所示: + ```yml + emsdk-2011: + url: http://emsdk-2011:9000 + instanceId: emsdk-2011 + ``` + URL应采用以下格式:`http://:9000`,其中`service_network_alias` - 来自步骤1的网络别名。9000 - Extender的标准端口(如果您使用自定义Extender配置,可能会有所不同)。 +3. 按照[如何使用预配置构件运行本地Extender](/manuals/extender-local-setup#how-to-run-local-extender-with-preconfigured-artifacts)中的描述运行本地Extender。 \ No newline at end of file diff --git a/docs/zh/manuals/extender-local-setup.md b/docs/zh/manuals/extender-local-setup.md new file mode 100644 index 00000000..0dcb0ad7 --- /dev/null +++ b/docs/zh/manuals/extender-local-setup.md @@ -0,0 +1,133 @@ +--- +title: 设置本地构建服务器 +brief: 本手册描述如何设置和运行本地构建服务器 +--- + +# 构建服务器本地设置 + +运行本地构建服务器(也称为'Extender')有两种方式: +1. 使用预配置构件运行本地构建服务器。 +2. 使用本地构建的构件运行本地构建服务器。 + +## 如何使用预配置构件运行本地Extender + +在运行本地云构建器之前,您需要安装以下软件: + +* [Docker](https://www.docker.com/) - Docker是一组平台即服务产品,使用操作系统级虚拟化来交付称为容器的软件包。要在本地开发机器上运行云构建器,您需要安装[Docker Desktop](https://www.docker.com/products/docker-desktop/) +* Google Cloud CLI - Google Cloud CLI是一组用于创建和管理Google Cloud资源的工具。这些工具可以[直接从Google安装](https://cloud.google.com/sdk/docs/install)或从包管理器(如Brew、Chocolatey或Snap)安装。 +* 您还需要一个Google账户来下载包含平台特定构建服务器的容器。 + +安装上述软件后,请按照以下步骤安装和运行Defold云构建器: + +**Windows用户注意**:使用git bash终端执行以下命令。 + +1. __授权Google Cloud并创建应用程序默认凭据__ - 下载Docker容器镜像时,您需要拥有Google账户,以便我们可以监控并确保公共容器注册表的公平使用,并暂时暂停过度下载镜像的账户。 + + ```sh + gcloud auth login + ``` +2. __配置Docker使用工件注册表__ - 需要将Docker配置为使用`gcloud`作为凭据助手,以便从`europe-west1-docker.pkg.dev`的公共容器注册表下载容器镜像。 + + ```sh + gcloud auth configure-docker europe-west1-docker.pkg.dev + ``` +3. __验证Docker和Google Cloud是否配置正确__ - 通过拉取所有构建服务器容器镜像使用的基础镜像,验证Docker和Google Cloud是否设置成功。在运行以下命令之前,请确保Docker Desktop正在运行: + ```sh + docker pull --platform linux/amd64 europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:latest + ``` +4. __克隆Extender仓库__ - 正确设置Docker和Google Cloud后,我们几乎可以启动服务器了。在启动服务器之前,我们需要克隆包含构建服务器的Git仓库: + ```sh + git clone https://github.com/defold/extender.git + cd extender + ``` +5. __下载预构建的jar文件__ - 下一步是下载预构建的服务器(`extender.jar`)和清单合并工具(`manifestmergetool.jar`): + ```sh + TMP_DIR=$(pwd)/server/_tmp + APPLICATION_DIR=$(pwd)/server/app + # 设置必要的Extender和清单合并工具版本 + # 版本可以在Github发布页面找到 https://github.com/defold/extender/releases + # 或者您可以拉取最新版本(见下面的代码示例) + EXTENDER_VERSION=2.6.5 + MANIFESTMERGETOOL_VERSION=1.3.0 + echo "将预构建的jar文件下载到 ${APPLICATION_DIR}" + rm -rf ${TMP_DIR} + mkdir -p ${TMP_DIR} + rm -rf ${APPLICATION_DIR} + mkdir -p ${APPLICATION_DIR} + + gcloud artifacts files download \ + --project=extender-426409 \ + --location=europe-west1 \ + --repository=extender-maven \ + --destination=${TMP_DIR} \ + com/defold/extender/server/${EXTENDER_VERSION}/server-${EXTENDER_VERSION}.jar + + gcloud artifacts files download \ + --project=extender-426409 \ + --location=europe-west1 \ + --repository=extender-maven \ + --destination=${TMP_DIR} \ + com/defold/extender/manifestmergetool/${MANIFESTMERGETOOL_VERSION}/manifestmergetool-${MANIFESTMERGETOOL_VERSION}.jar + + cp ${TMP_DIR}/$(ls ${TMP_DIR} | grep server-${EXTENDER_VERSION}.jar) ${APPLICATION_DIR}/extender.jar + cp ${TMP_DIR}/$(ls ${TMP_DIR} | grep manifestmergetool-${MANIFESTMERGETOOL_VERSION}.jar) ${APPLICATION_DIR}/manifestmergetool.jar + ``` +6. __启动服务器__ - 现在我们可以通过运行docker compose主命令来启动服务器: +```sh +docker compose -p extender -f server/docker/docker-compose.yml --profile up +``` +其中*profile*可以是: +* **all** - 为每个平台运行远程实例 +* **android** - 运行前端实例 + 用于构建Android版本的远程实例 +* **web** - 运行前端实例 + 用于构建Web版本的远程实例 +* **linux** - 运行前端实例 + 用于构建Linux版本的远程实例 +* **windows** - 运行前端实例 + 用于构建Windows版本的远程实例 +* **consoles** - 运行前端实例 + 用于构建Nintendo Switch/PS4/PS5版本的远程实例 +* **nintendo** - 运行前端实例 + 用于构建Nintendo Switch版本的远程实例 +* **playstation** - 运行前端实例 + 用于构建PS4/PS5版本的远程实例 +* **metrics** - 运行VictoriaMetrics + Grafana作为指标后端和可视化工具 +有关`docker compose`参数的更多信息,请参见 https://docs.docker.com/reference/cli/docker/compose/。 + +当docker compose启动后,您可以在编辑器的首选项中使用**http://localhost:9000**作为构建服务器地址,或者如果您使用Bob构建项目,则作为`--build-server`值。 + +可以将多个配置文件传递给命令行。例如: +```sh +docker compose -p extender -f server/docker/docker-compose.yml --profile android --profile web --profile windows up +``` +上面的示例运行前端、Android、Web、Windows实例。 + +要停止服务 - 如果docker compose以非分离模式运行,请按Ctrl+C,或者 +```sh +docker compose -p extender down +``` +如果docker compose是以分离模式运行的(例如,向`docker compose up`命令传递了'-d'标志)。 + +如果您想拉取最新版本的jar文件,可以使用以下命令确定最新版本 +```sh + EXTENDER_VERSION=$(gcloud artifacts versions list \ + --project=extender-426409 \ + --location=europe-west1 \ + --repository=extender-maven \ + --package="com.defold.extender:server" \ + --sort-by="~createTime" \ + --limit=1 \ + --format="value(name)") + + MANIFESTMERGETOOL_VERSION=$(gcloud artifacts versions list \ + --project=extender-426409 \ + --location=europe-west1 \ + --repository=extender-maven \ + --package="com.defold.extender:manifestmergetool" \ + --sort-by="~createTime" \ + --limit=1 \ + --format="value(name)") +``` + +### 那么macOS和iOS呢? + +macOS和iOS的构建是在真实的Apple硬件上完成的,使用在独立模式下运行的构建服务器,而不使用Docker。而是在机器上直接安装XCode、Java和其他必需的工具,构建服务器作为普通的Java进程运行。您可以在[GitHub上的构建服务器文档](https://github.com/defold/extender?tab=readme-ov-file#running-as-a-stand-alone-server-on-macos)中了解如何设置它。 + + +## 如何使用本地构建的构件运行本地Extender + +请遵循[GitHub上Extender仓库中的说明](https://github.com/defold/extender)来手动构建和运行本地构建服务器。 \ No newline at end of file diff --git a/docs/zh/manuals/extension-facebook.md b/docs/zh/manuals/extension-facebook.md index 2cd6c364..13539acc 100644 --- a/docs/zh/manuals/extension-facebook.md +++ b/docs/zh/manuals/extension-facebook.md @@ -1,139 +1,137 @@ --- -title: Defold Facebook 教程 -brief: 本教程介绍了如何在 Defold 游戏中设置和整合 Facebook 功能. +title: Defold Facebook 扩展 +brief: 本手册介绍了如何在 Defold 游戏中设置和整合 Facebook 功能. --- API 文档位于 [这里](https://defold.github.io/extension-facebook/). -## 注册成为 Facebook 开发者 # Facebook -Facebook API 作为 [原生扩展](/manuals/extensions/) 提供. 此扩展使得 iOS, Android 和 HTML5 游戏可以使用 Facebook 的在线交互功能. Defold Facebook 扩展对于 iOS, Android 和 HTML5 (基于 Facebook Canvas) 不同平台封装了同一的 Facebook API 函数库. 要在游戏中使用 Facebook 在线交互功能, 首先需要有一个 Facebook 账户. +Facebook API 作为 [原生扩展](/manuals/extensions/) 提供. 此扩展使得 iOS、Android 和 HTML5 游戏可以使用 Facebook 的在线交互功能. Defold Facebook 扩展对于 iOS、Android 和 HTML5(基于 Facebook Canvas)不同平台封装了统一的 Facebook API 函数库. 要在游戏中使用 Facebook 在线交互功能,首先需要有一个 Facebook 账户. ## 安装扩展 -要使用 Facebook 扩展, 需要在 *game.project* 文件中设置依赖. 最新稳定版的 URL 是: +要使用 Facebook 扩展,需要在 *game.project* 文件中设置依赖. 最新稳定版的 URL 是: ``` https://github.com/defold/extension-facebook/archive/master.zip ``` -推荐使用 [指定版本](https://github.com/defold/extension-facebook/releases) 的zip包链接作为依赖. +推荐使用 [指定版本](https://github.com/defold/extension-facebook/releases) 的 zip 包链接作为依赖。 -Facebook API 文档位于 [fb扩展主页](https://defold.github.io/extension-facebook/). +Facebook API 文档位于 [fb 扩展主页](https://defold.github.io/extension-facebook/)。 -## 注册为 Facebook 开发者 +## 注册成为 Facebook 开发者 -开发 Facebook 应用首先要注册成为 Facebook 开发者. 这样你的 Defold 游戏就能与 Facebook 应用交互了. +开发 Facebook 应用首先要注册成为 Facebook 开发者。这样你的 Defold 游戏就能与 Facebook 应用交互了。 * 访问 [Facebook 开发者网站](https://developers.facebook.com) 进行注册 -* 登入你的 Facebook 账户. -* 注册激活你的开发者身份. +* 登入你的 Facebook 账户。 +* 注册激活你的开发者身份。 -![Register as a developer](images/facebook/register_dev.png) -![ developer](images/facebook/register_verify.png) +![注册成为开发者](images/facebook/register_dev.png) +![验证开发者身份](images/facebook/register_verify.png) ## 创建 Facebook 应用 -下一步要创建一个 Facebook 应用. 右上角 My Apps 菜单列出了你的应用, 以及 Add a New App 按钮. +下一步要创建一个 Facebook 应用。右上角 My Apps 菜单列出了你的应用,以及 Add a New App 按钮。 -![Add new app](images/facebook/add_new_app_menu.png) +![添加新应用](images/facebook/add_new_app_menu.png) -然后是选择目标平台界面. 点击 *basic setup* 可以略过引导界面. +然后是选择目标平台界面。点击 *basic setup* 可以略过引导界面。 ::: sidenote -引导界面里绝大多数内容与开发 Defold 游戏无关. 而且, 一般不需要自己修改 *Info.plist* 或者 *AndroidManifest.xml* 文件. Defold 会自动完成. +引导界面里绝大多数内容与开发 Defold 游戏无关。而且,一般不需要自己修改 *Info.plist* 或者 *AndroidManifest.xml* 文件。Defold 会自动完成。 ::: -![Add new app platform](images/facebook/add_new_app_platform.png) +![添加新应用平台](images/facebook/add_new_app_platform.png) -在 dashboard 里可以随意添加, 删除应用或者更改平台设置. 接下来需要设置 *Display Name*, *Namespace* 和 *Category*. 这些也都可以在 dashboard 里设置. 设置完后, Facebook 会给你的应用创建唯一id. *App ID* 是不可修改的. +在 dashboard 里可以随意添加、删除应用或者更改平台设置。接下来需要设置 *Display Name*、*Namespace* 和 *Category*。这些也都可以在 dashboard 里设置。设置完后,Facebook 会给你的应用创建唯一 ID。*App ID* 是不可修改的。 -![New app id](images/facebook/new_app_id.png) +![新应用 ID](images/facebook/new_app_id.png) -![App dashboard settings](images/facebook/add_platform.png) +![应用仪表板设置](images/facebook/add_platform.png) -点击 *Settings* 面板. 这里显示了 *App ID*. 你要在 Defold 游戏的 [项目设置](/manuals/project-settings) 里输入这个id. 从 *资源面板* 里打开 *game.project* 文件, 滚动到 *Facebook* 部分, 在 `Appid` 这里填入 *App ID*. +点击 *Settings* 面板。这里显示了 *App ID*。你要在 Defold 游戏的 [项目设置](/manuals/project-settings) 里输入这个 ID。从 *资源面板* 里打开 *game.project* 文件,滚动到 *Facebook* 部分,在 `Appid` 这里填入 *App ID*。 -现在回到 Facebook 网站的 *Settings* 页面, 点击 *+ Add Platform* 来为应用设置目标平台. 针对每个平台都有不同的设置. +现在回到 Facebook 网站的 *Settings* 页面,点击 *+ Add Platform* 来为应用设置目标平台。针对每个平台都有不同的设置。 -![Select platform](images/facebook/select_platform.png) +![选择平台](images/facebook/select_platform.png) ## iOS -iOS 平台需要填写和 *game.project* 里一样的 `bundle_identifier`. +iOS 平台需要填写和 *game.project* 里一样的 `bundle_identifier`。 -![iOS settings](images/facebook/settings_ios.png) +![iOS 设置](images/facebook/settings_ios.png) ## Android -Android 平台需要填写 *Google Play Package Name*, 也就是 *game.project* 里的 *package* id. 还要在 *Key Hashes* 里填入证书的 Hashes. 可以使用 openssl 从 *certificate.pem* 文件生成 Hashes: +Android 平台需要填写 *Google Play Package Name*,也就是 *game.project* 里的 *package* ID。还要在 *Key Hashes* 里填入证书的 Hashes。可以使用 openssl 从 *certificate.pem* 文件生成 Hashes: ```sh $ cat certificate.pem | openssl x509 -outform der | openssl sha1 -binary | openssl base64 ``` -(关于签名文件详细信息参见 [制作 keystore](/manuals/android/#制作 keystore).) +(关于签名文件详细信息参见 [制作 keystore](/manuals/android/#制作 keystore)。) -![Android settings](images/facebook/settings_android.png) +![Android 设置](images/facebook/settings_android.png) ## Facebook Canvas -对于 HTML5 游戏, 过程略有不同. Facebook 需要直接在线访问你的游戏内容. 两个办法: +对于 HTML5 游戏,过程略有不同。Facebook 需要直接在线访问你的游戏内容。两个办法: -![Facebook Canvas settings](images/facebook/settings_canvas.png) +![Facebook Canvas 设置](images/facebook/settings_canvas.png) -1. 使用 Facebook 的 *Simple Application Hosting*. 点击 *Yes* 使用在线托管. 选择 *uploaded assets* 打开托管资源管理器. +1. 使用 Facebook 的 *Simple Application Hosting*。点击 *Yes* 使用在线托管。选择 *uploaded assets* 打开托管资源管理器。 - ![Simple hosting](images/facebook/simple_hosting.png) + ![简单托管](images/facebook/simple_hosting.png) - 托管类型选择 "HTML5 Bundle": + 托管类型选择 "HTML5 Bundle": - ![HTML5 bundle](images/facebook/html5_bundle.png) + ![HTML5 包](images/facebook/html5_bundle.png) - 把你的 HTML5 游戏包压缩为一个 .7z 或者 .zip 包上传至 Facebook. 点击 *Push to production* 即可在线发布. + 把你的 HTML5 游戏包压缩为一个 .7z 或者 .zip 包上传至 Facebook。点击 *Push to production* 即可在线发布。 -2. 另一个办法就是把你的 HTML5 游戏包托管到你自己选择的服务器上通过 HTTPS 访问. *Secure Canvas URL* 这里填写你的游戏地址. +2. 另一个办法就是把你的 HTML5 游戏包托管到你自己选择的服务器上通过 HTTPS 访问。*Secure Canvas URL* 这里填写你的游戏地址。 -至此 Facebook 就能通过 *Canvas Page* 发布你的游戏了. +至此 Facebook 就能通过 *Canvas Page* 发布你的游戏了。 ## 小测试 -可以通过创建一个小测试来看看扩展工作是否顺利. +可以通过创建一个小测试来看看扩展工作是否顺利。 -1. 新建游戏对象然后在上面添加一个脚本. -2. 脚本里加入如下代码 (需要 Facebook 扩展 v.2 版及以上): +1. 新建游戏对象然后在上面添加一个脚本。 +2. 脚本里加入如下代码(需要 Facebook 扩展 v.2 版及以上): ```lua local function get_me_callback(self, id, response) - -- The response table includes all the response data + -- 响应表包含所有响应数据 pprint(response) end local function fb_login(self, data) if data.status == facebook.STATE_OPEN then - -- Logged in ok. Let's try reading some "me" data through the - -- HTTP graph API. + -- 登录成功。让我们尝试通过 HTTP graph API 读取一些 "me" 数据。 local token = facebook.access_token() local url = "https://graph.facebook.com/me/?access_token=" .. token http.request(url, "GET", get_me_callback) elseif data.status == facebook.STATE_CLOSED_LOGIN_FAILED then - -- Do something to indicate that login failed + -- 执行一些操作以指示登录失败 end if data.error then - -- An error occurred + -- 发生错误 else - -- No error + -- 没有错误 end end function init(self) - -- Log in with read permissions. + -- 使用读取权限登录。 local permissions = { "public_profile", "email" } facebook.login_with_permissions(permissions, facebook.AUDIENCE_EVERYONE, fb_login) end ``` -直接运行, 控制台输出大概这样: +直接运行,控制台输出大概这样: ```txt DEBUG:SCRIPT: @@ -158,53 +156,53 @@ DEBUG:SCRIPT: } ``` -* 完整的 Defold Facebook API 文档在 [这里](https://defold.github.io/extension-facebook/). -* Facebook Graph API 在这里: https://developers.facebook.com/docs/graph-api +* 完整的 Defold Facebook API 文档在 [这里](https://defold.github.io/extension-facebook/)。 +* Facebook Graph API 在这里:https://developers.facebook.com/docs/graph-api ## Facebook Analytics -Facebook Analytics 可以对游戏相关信息汇总统计并提供适当建议, 比如开启游戏人数, 付款频率和其他各种信息. +Facebook Analytics 可以对游戏相关信息汇总统计并提供适当建议,比如开启游戏人数、付款频率和其他各种信息。 ### 配置 -使用 Facebook Analytics 之前你得创建好 Facebook 应用. 然后就是把分析功能整合进去: +使用 Facebook Analytics 之前你得创建好 Facebook 应用。然后就是把分析功能整合进去: -![Add Facebook Analytics](images/facebook/add_facebook_analytics.png) +![添加 Facebook Analytics](images/facebook/add_facebook_analytics.png) -分析功能也有很多配置选项. 详情请见: +分析功能也有很多配置选项。详情请见: -![Add Facebook Analytics](images/facebook/facebook_analytics_settings.png) +![Facebook Analytics 设置](images/facebook/facebook_analytics_settings.png) ### 使用 -分析功能配置好之后就可以通过发布分析事件来进行信息统计了: +分析功能配置好之后就可以通过发布分析事件来进行信息统计了: ```lua function init(self) - -- post a spent credits event + -- 发布消费积分事件 local params = { [facebook.PARAM_LEVEL] = 30, [facebook.PARAM_NUM_ITEMS] = 2 } facebook.post_event(facebook.EVENT_SPENT_CREDITS, 25, params) - -- post a custom event + -- 发布自定义事件 local level = 19 local params = { kills = 23, gold = 4, xp = 890 } facebook.post_event("level_completed", level, params) end ``` -各种事件和参数详情请见 [Facebook 扩展 API 文档](https://defold.github.io/extension-facebook/). 这些应该与 Facebook 的 [标准事件](https://developers.facebook.com/docs/analytics/send_data/events#standard) 和 [参数](https://developers.facebook.com/docs/analytics/send_data/events#parameter) 文档相一致. +各种事件和参数详情请见 [Facebook 扩展 API 文档](https://defold.github.io/extension-facebook/)。这些应该与 Facebook 的 [标准事件](https://developers.facebook.com/docs/analytics/send_data/events#standard) 和 [参数](https://developers.facebook.com/docs/analytics/send_data/events#parameter) 文档相一致。 -除了分析统计事件, Defold 使用 Facebook SDK 时也会自动产生一些事件, 比如装机次数启动次数之类的. 详情请见 Facebook 的 [自动日志事件](https://developers.facebook.com/docs/analytics/send_data/events#autologged) 文档. +除了分析统计事件,Defold 使用 Facebook SDK 时也会自动产生一些事件,比如装机次数、启动次数之类的。详情请见 Facebook 的 [自动日志事件](https://developers.facebook.com/docs/analytics/send_data/events#autologged) 文档。 -事件汇总后会在 Facebook Analytics dashboard 中显示出来, 点击 *View Analytics* 按钮查看分析信息: +事件汇总后会在 Facebook Analytics dashboard 中显示出来,点击 *View Analytics* 按钮查看分析信息: -![Add Facebook Analytics](images/facebook/facebook_analytics_open_dashboard.png) +![打开 Facebook Analytics](images/facebook/facebook_analytics_open_dashboard.png) -通过页面里 *Events* 选项可以看到各种事件: +通过页面里 *Events* 选项可以看到各种事件: -![Add Facebook Analytics](images/facebook/facebook_analytics_show_events.png) +![显示事件](images/facebook/facebook_analytics_show_events.png) ### Facebook 的事件分享 -你可以选择是否向 Facebook 分享你的事件数据. 通过 [`enable_event_usage()`](https://defold.github.io/extension-facebook/#enable_event_usage) 和 [`disable_event_usage()`](https://defold.github.io/extension-facebook/#disable_event_usage) 函数控制. 默认不分享. +你可以选择是否向 Facebook 分享你的事件数据。通过 [`enable_event_usage()`](https://defold.github.io/extension-facebook/#enable_event_usage) 和 [`disable_event_usage()`](https://defold.github.io/extension-facebook/#disable_event_usage) 函数控制。默认不分享。 diff --git a/docs/zh/manuals/extension-fbinstant.md b/docs/zh/manuals/extension-fbinstant.md index a36a04d4..7fd01912 100644 --- a/docs/zh/manuals/extension-fbinstant.md +++ b/docs/zh/manuals/extension-fbinstant.md @@ -1,39 +1,39 @@ --- title: Facebook Instant Games -brief: 本教程介绍了如何使用 Defold 创建 Facebook Instant Games. +brief: 本手册介绍了如何使用 Defold 创建 Facebook Instant Games。 --- # Facebook Instant Games -Instant Games 可以让玩家在 Facebook 平台上随时玩到游戏的新方式. 基于 HTML5 技术, 可以让玩家直接从新闻Feed或者聊天消息中打开游戏, 同时支持桌面和移动设备. +Instant Games 可以让玩家在 Facebook 平台上随时玩到游戏的新方式。基于 HTML5 技术,可以让玩家直接从新闻 Feed 或者聊天消息中打开游戏,同时支持桌面和移动设备。 ![InstantGames](images/instant-games/instantgames.png) ## 发布 Instant Games -在把你的 Defold 游戏发布到 Facebook 之前, 需要对游戏项目进行一些设定: +在把你的 Defold 游戏发布到 Facebook 之前,需要对游戏项目进行一些设定: -1. 在 [Facebook App Dashboard](https://developers.facebook.com/apps) 里创建 Instant Games 应用. 详情请见 [Instant Games 入门指南](https://developers.facebook.com/docs/games/instant-games/getting-started/game-setup). +1. 在 [Facebook App Dashboard](https://developers.facebook.com/apps) 里创建 Instant Games 应用。详情请见 [Instant Games 入门指南](https://developers.facebook.com/docs/games/instant-games/getting-started/game-setup)。 -2. 在你的 *game.project* 文件中添加 Instant Games 扩展依赖. 即把 "https://github.com/defold/extension-fbinstant/archive/master.zip" 添加到 *Dependencies* 属性中. +2. 在你的 *game.project* 文件中添加 Instant Games 扩展依赖。即把 "https://github.com/defold/extension-fbinstant/archive/master.zip" 添加到 *Dependencies* 属性中。 - ![Project settings](images/instant-games/game_project.png) + ![项目设置](images/instant-games/game_project.png) -3. 确保在你打包游戏生成的 "index.html" 文件包含了 Instant Games SDK. +3. 确保在你打包游戏生成的 "index.html" 文件包含了 Instant Games SDK。 ```html ``` - 关于打包设置详情请见 [HTML5 教程](/manuals/html5/#自定义HTML5打包). + 关于打包设置详情请见 [HTML5 教程](/manuals/html5/#自定义HTML5打包)。 -4. 都准备好之后, 打包 HTML5 游戏并上传至 Facebook. +4. 都准备好之后,打包 HTML5 游戏并上传至 Facebook。 ## API 使用 -Instant Games 扩展使用命名空间 `fbinstant.*` , 用 Lua API 对 Javascript SDK 进行了封装. 此扩展基本上是从 Javascript SDK 到 Lua API 一一对应的. +Instant Games 扩展使用命名空间 `fbinstant.*`,用 Lua API 对 Javascript SDK 进行了封装。此扩展基本上是从 Javascript SDK 到 Lua API 一一对应的。 -比如, 看以下 Javascript 代码: +比如,看以下 Javascript 代码: ```javascript FBInstant.initializeAsync().then(function() { @@ -44,7 +44,7 @@ FBInstant.initializeAsync().then(function() { }); ``` -相应的 Lua 代码是这样的: +相应的 Lua 代码是这样的: ```lua fbinstant.initialize(function(self, success) @@ -55,31 +55,31 @@ fbinstant.initialize(function(self, success) end) ``` -关于如何在Defold中使用 Instant Games SDK 详情请见 [API documentation](https://github.com/defold/extension-fbinstant/blob/master/README.md). +关于如何在 Defold 中使用 Instant Games SDK 详情请见 [API 文档](https://github.com/defold/extension-fbinstant/blob/master/README.md)。 ## 示例游戏 -有个 Defold 游戏 "Tic Tac Toe" 的 Instant Games 版本在 [官方 GitHub 代码库](https://github.com/defold/extension-fbinstant) 提供了完整的源码和素材. 可以作为学习资料或者素材来使用. +有个 Defold 游戏 "Tic Tac Toe" 的 Instant Games 版本在 [官方 GitHub 代码库](https://github.com/defold/extension-fbinstant) 提供了完整的源码和素材。可以作为学习资料或者素材来使用。 ![Tic Tac Toe](images/instant-games/tictactoe.png) ## 减小包体 -Facebook Instant Games [最佳实践](https://developers.facebook.com/docs/games/instant-games/best-practices) 建议游戏首次载入时间不得超过5秒. 这对 Defold 来说有点勉强, 好在还有一些方法能减小游戏体积: +Facebook Instant Games [最佳实践](https://developers.facebook.com/docs/games/instant-games/best-practices) 建议游戏首次载入时间不得超过 5 秒。这对 Defold 来说有点勉强,好在还有一些方法能减小游戏体积: 去掉未使用引擎特性 -: HTML5 版的标准 Defold 引擎用gzip压缩后小于 1.2MB, 但是可以通过去掉不必要的引擎特效来达到减少包体的效果. 在 "app.manifest" 文件中可以指定去掉引擎哪方面特性, 此文件在 *game.project* 文件的 [Native Extension 部分](/manuals/project-settings/#Native extension) 引用. +: HTML5 版的标准 Defold 引擎用 gzip 压缩后小于 1.2MB,但是可以通过去掉不必要的引擎特效来达到减少包体的效果。在 "app.manifest" 文件中可以指定去掉引擎哪方面特性,此文件在 *game.project* 文件的 [Native Extension 部分](/manuals/project-settings/#Native extension) 引用。 - 此功能尚处于测试阶段且没有说明文档. 这里: https://forum.defold.com/t/stripping-appmanifest-maker/16059 有一个可以自动生成 app.manifests 文件的工具. + 此功能尚处于测试阶段且没有说明文档。这里:https://forum.defold.com/t/stripping-appmanifest-maker/16059 有一个可以自动生成 app.manifests 文件的工具。 使用纹理压缩 -: 纹理压缩是有效减少包体和内存占用的方法. 关于纹理压缩详情请见 [Texture Profiles 教程](/manuals/texture-profiles/). +: 纹理压缩是有效减少包体和内存占用的方法。关于纹理压缩详情请见 [Texture Profiles 教程](/manuals/texture-profiles/)。 减少启动所需资源按需下载 -: 许多游戏把内容分成各个关卡或者各个篇章. 这种类型的游戏可以把内容放到玩家玩到的时候再下载. +: 许多游戏把内容分成各个关卡或者各个篇章。这种类型的游戏可以把内容放到玩家玩到的时候再下载。 -这种把内容分块, 保存到服务器上然后按需下载并缓存的方法可以有效减少 Instant Game 包体. 这种方法由 Defold 的 [热更新](/manuals/live-update/) 系统提供支持. +这种把内容分块,保存到服务器上然后按需下载并缓存的方法可以有效减少 Instant Game 包体。这种方法由 Defold 的 [热更新](/manuals/live-update/) 系统提供支持。 ## 已知问题 -关于 HTML5 和 Instant Games 的已知问题详见 [HTML5 教程已知问题部分](/manuals/html5/#已知问题和局限性). +关于 HTML5 和 Instant Games 的已知问题详见 [HTML5 教程已知问题部分](/manuals/html5/#已知问题和局限性)。 diff --git a/docs/zh/manuals/extension-googleplayinstant.md b/docs/zh/manuals/extension-googleplayinstant.md index 602e10a0..83136b7c 100644 --- a/docs/zh/manuals/extension-googleplayinstant.md +++ b/docs/zh/manuals/extension-googleplayinstant.md @@ -1,31 +1,31 @@ --- title: Google Play Instant -brief: 本教程介绍了使用 Defold 创建 Google Play Instant 游戏的方法. +brief: 本手册介绍了使用 Defold 创建 Google Play Instant 游戏的方法。 --- # Google Play Instant -Google Play Instant 可以让 Android 6.0+ 的设备运行无需安装的原生应用. +Google Play Instant 可以让 Android 6.0+ 的设备运行无需安装的原生应用。 -![GooglePlayInstant](images/gpi/gpi-try-now.png) +![Google Play Instant](images/gpi/gpi-try-now.png) ## 发布流程 -如果想让游戏作为 Google Play Instant 发布, 需要修改项目配置: +如果想让游戏作为 Google Play Instant 发布,需要修改项目配置: -1. 自定义 `AndroidManifest.xml` 文件加入 `` 属性: +1. 自定义 `AndroidManifest.xml` 文件加入 `` 属性: ```lua xmlns:dist="http://schemas.android.com/apk/distribution" android:targetSandboxVersion="2" ``` -后面紧跟描述项: +后面紧跟描述项: ```lua ``` -结果 AndroidManifest.xml 类似这样: +结果 AndroidManifest.xml 类似这样: ```lua ``` -2. 在 “game.project” 文件里添加 Google Instant Apps 扩展. 加入 “https://github.com/defold/extension-instantapp/archive/master.zip” 地址依赖或者指定 [特定版本](https://github.com/defold/extension-instantapp/releases) 的依赖. +2. 在 "game.project" 文件里添加 Google Instant Apps 扩展。加入 "https://github.com/defold/extension-instantapp/archive/master.zip" 地址依赖或者指定 [特定版本](https://github.com/defold/extension-instantapp/releases) 的依赖。 -![Project settings](images/gpi/game_project.png) +![项目设置](images/gpi/game_project.png) -3. 下载库文件: Project->Fetch Libraries -4. 打包 `aab` Project->Bundle->Android Application +3. 下载库文件:Project->Fetch Libraries +4. 打包 `aab`:Project->Bundle->Android Application 5. 上传 `aab` 至 Google Play Console 作为 Android Instant App 发布 ### 版本号 -注意 [关于版本号的建议](https://developer.android.com/topic/google-play-instant/getting-started/game-instant-app#version-codes): Instant 游戏版本号要小于可安装游戏的版本号. +注意 [关于版本号的建议](https://developer.android.com/topic/google-play-instant/getting-started/game-instant-app#version-codes):Instant 游戏版本号要小于可安装游戏的版本号。 -![Project settings](images/gpi/version_code.png) +![项目设置](images/gpi/version_code.png) ### android:targetSandboxVersion="2" -如果在可安装主游戏里设置 `android:targetSandboxVersion="2"` 就可以像 instant 游戏一样访问数据 (比如存取同一个文件). 但是这样一来主游戏程序就可能会受到一些限制. 详情请见 [官方文档](https://developer.android.com/guide/topics/manifest/manifest-element#targetSandboxVersion). +如果在可安装主游戏里设置 `android:targetSandboxVersion="2"` 就可以像 instant 游戏一样访问数据(比如存取同一个文件)。但是这样一来主游戏程序就可能会受到一些限制。详情请见 [官方文档](https://developer.android.com/guide/topics/manifest/manifest-element#targetSandboxVersion)。 ::: sidenote -游戏一旦安装, 它的目标沙箱值只能增加. 要想降级, 只能把游戏删除再覆盖安装 manifest 属性里目标沙箱值较小的版本. +游戏一旦安装,它的目标沙箱值只能增加。要想降级,只能把游戏删除再覆盖安装 manifest 属性里目标沙箱值较小的版本。 ::: -即使在可安装游戏和 instant 游戏里的 `android:targetSandboxVersion` 设置了不同的值, 仍然可以使用 `instantapp.set_cookie()` 和 `instantapp.get_cookie()` 来进行游戏版本间通信. +即使在可安装游戏和 instant 游戏里的 `android:targetSandboxVersion` 设置了不同的值,仍然可以使用 `instantapp.set_cookie()` 和 `instantapp.get_cookie()` 来进行游戏版本间通信。 ## API 使用 -Google Play Instant 扩展使用 `instantapp.*` 命名空间封装了 Java [PackageManagerCompat 方法](https://developers.google.com/android/reference/com/google/android/gms/instantapps/PackageManagerCompat) API 供 Lua 使用. +Google Play Instant 扩展使用 `instantapp.*` 命名空间封装了 Java [PackageManagerCompat 方法](https://developers.google.com/android/reference/com/google/android/gms/instantapps/PackageManagerCompat) API 供 Lua 使用。 -如果你要制作跨平台游戏最好在使用前检查 `instantapp` 模块是否存在, 因为它只存在于安卓游戏包中: +如果你要制作跨平台游戏最好在使用前检查 `instantapp` 模块是否存在,因为它只存在于安卓游戏包中: ```lua if instantapp then -- 调用 instantapp 方法 @@ -84,33 +84,33 @@ else end ``` -关于在 Defold 里使用 Google Instant API 的详细信息请见 [API 文档](https://github.com/defold/extension-instantapp/blob/master/README.md). +关于在 Defold 里使用 Google Instant API 的详细信息请见 [API 文档](https://github.com/defold/extension-instantapp/blob/master/README.md)。 ## 限制 -遵循 [Google Play Instant Technical Requirements](https://developer.android.com/topic/google-play-instant/game-tech-requirements) `apk` 大小不得大于 15 MB. 游戏大小优化详见 [这里](extension-fbinstant/#reducing-bundle-size). +遵循 [Google Play Instant Technical Requirements](https://developer.android.com/topic/google-play-instant/game-tech-requirements) `apk` 大小不得大于 15 MB。游戏大小优化详见 [这里](extension-fbinstant/#reducing-bundle-size)。 ## 测试 -![Testing Instant game](images/gpi/start_instant.png) +![测试 Instant 游戏](images/gpi/start_instant.png) -1. 下载 Android SDK 工具: +1. 下载 Android SDK 工具: - macOS: https://dl.google.com/android/repository/tools_r25.2.3-macosx.zip - Windows: https://dl.google.com/android/repository/tools_r25.2.3-windows.zip - Linux: https://dl.google.com/android/repository/tools_r25.2.3-linux.zip -2. 解压拷贝 `tools` 文件夹到 `android-sdk` 文件夹. -3. 安卓编译工具: +2. 解压拷贝 `tools` 文件夹到 `android-sdk` 文件夹。 +3. 安卓编译工具: ```console -./android-sdk/tools/bin/sdkmanager --verbose “build-tools;25.0.3” +./android-sdk/tools/bin/sdkmanager --verbose "build-tools;25.0.3" ``` -4. 安装 `extra-google-instantapps` 工具: +4. 安装 `extra-google-instantapps` 工具: ```console sh ./android-sdk/tools/android update sdk --no-ui --all --filter extra-google-instantapps ``` -5. 在设备上作为 Instant 游戏加载 `apk` 文件: +5. 在设备上作为 Instant 游戏加载 `apk` 文件: ```console android-sdk/extras/google/instantapps/ia run path_to_your_game.apk ``` -前4步可以合并为一个脚本: +前4步可以合并为一个脚本: ```console mkdir ~/android cd ~/android @@ -125,4 +125,4 @@ mv tools android-sdk/tools sh ./android-sdk/tools/android update sdk --no-ui --all --filter extra-google-instantapps ``` -设备调试详情请见 [调试教程](/manuals/debugging/#_debugging_on_mobile_devices). +设备调试详情请见 [调试教程](/manuals/debugging/#_debugging_on_mobile_devices)。 diff --git a/docs/zh/manuals/extension-gpgs.md b/docs/zh/manuals/extension-gpgs.md index f242446d..1035e4e4 100644 --- a/docs/zh/manuals/extension-gpgs.md +++ b/docs/zh/manuals/extension-gpgs.md @@ -1,21 +1,21 @@ --- title: Defold 中的 Google Play Game 服务 -brief: 本教程介绍了配置使用 Google Play Game 服务的方法 +brief: 本手册介绍了配置使用 Google Play Game 服务的方法 --- # Google Play Game Services -[Google Play Game Services](https://developers.google.com/games/services) 可以给你的安卓游戏增加用户认证, 云存储, 成就系统, 排行榜等各种功能. Google Play Game 服务在 Defold 中作为一个 [原生扩展](/manuals/extensions/). +[Google Play Game Services](https://developers.google.com/games/services) 可以给你的安卓游戏增加用户认证、云存储、成就系统、排行榜等各种功能。Google Play Game 服务在 Defold 中作为一个 [原生扩展](/manuals/extensions/)。 ## 安装扩展 -要使用 Google Play Game 服务扩展你需要在 *game.project* 文件中加入依赖. 最新稳定版地址: +要使用 Google Play Game 服务扩展你需要在 *game.project* 文件中加入依赖。最新稳定版地址: ``` https://github.com/defold/extension-gpgs/archive/master.zip ``` -推荐使用发布页的zip链接 [指定某个版本](https://github.com/defold/extension-gpgs/releases). +推荐使用发布页的zip链接 [指定某个版本](https://github.com/defold/extension-gpgs/releases)。 ## 使用扩展 -使用的介绍以及 API 文档位于 [GitHub 扩展项目网页](https://defold.github.io/extension-gpgs/). +使用的介绍以及 API 文档位于 [GitHub 扩展项目网页](https://defold.github.io/extension-gpgs/)。 diff --git a/docs/zh/manuals/extension-iap.md b/docs/zh/manuals/extension-iap.md index 000e3850..3846b9eb 100644 --- a/docs/zh/manuals/extension-iap.md +++ b/docs/zh/manuals/extension-iap.md @@ -1,14 +1,14 @@ --- title: Defold 中的内支付 -brief: 应用内支付 (也称内购买) 允许用户付费以获得额外的内容或功能. 本教程介绍了相关功能的 Defold API. +brief: 应用内支付(也称内购买)允许用户付费以获得额外的内容或功能。本手册介绍了相关功能的 Defold API。 --- # 应用内支付 Defold 提供了方便的 iOS Appstore "in-app purchases" 和 Google Play 及 Amazon "in-app billing" 系统接口. Facebook Canvas 的 "game payments" 用于 Facebook Canvas 游戏. 这些服务能让你销售: -* 标准应用内支付 (一次性购买) 的消耗品和非消耗品 -* 订阅 (自动循环购买) +* 标准应用内支付(一次性购买)的消耗品和非消耗品 +* 订阅(自动循环购买) ::: sidenote 目前 Defold 接口可以与 Apple 的 Storekit 完整交互. 对于 Google Play 和 Facebook Canvas, 接口是一致的, 这有助于代码跨平台. 但是不同平台需要的流程有所不同. 而且注意目前没有针对 macOS 系统上 Mac Appstore 的支付接口. @@ -16,83 +16,83 @@ Defold 提供了方便的 iOS Appstore "in-app purchases" 和 Google Play 及 Am 关于 Apple, Google, Amazon 和 Facebook 的详细文档参考: -* [In-App Purchase Programming Guide](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html). -* [Google Play In-app Billing documentation](http://developer.android.com/google/play/billing/index.html). -* [Amazon In-app Purchase documentation](https://developer.amazon.com/public/apis/earn/in-app-purchasing). -* [Facebook game payments documentation](https://developers.facebook.com/docs/payments). +* [In-App Purchase Programming Guide](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html) +* [Google Play In-app Billing documentation](http://developer.android.com/google/play/billing/index.html) +* [Amazon In-app Purchase documentation](https://developer.amazon.com/public/apis/earn/in-app-purchasing) +* [Facebook game payments documentation](https://developers.facebook.com/docs/payments) ## 安装扩展 -首先要在 *game.project* 文件中加入内支付扩展依赖. 最新稳定版本地址如下: +首先要在 *game.project* 文件中加入内支付扩展依赖。最新稳定版本地址如下: ``` https://github.com/defold/extension-iap/archive/master.zip ``` -推荐使用 [正式发布版](https://github.com/defold/extension-iap/releases) 的zip连接作为依赖引用. +推荐使用 [正式发布版](https://github.com/defold/extension-iap/releases) 的 zip 连接作为依赖引用。 -其 API 文档详见 [扩展项目首页](https://defold.github.io/extension-iap/). +其 API 文档详见 [扩展项目首页](https://defold.github.io/extension-iap/)。 ## 使用静态报告测试 Google Play Billing -Android 建议使用 Google Play 的静态报告来实现 IAP. 这样在正式发布之前就可以确保内支付工作正常. 测试用静态报告包括4个ID: +Android 建议使用 Google Play 的静态报告来实现 IAP。这样在正式发布之前就可以确保内支付工作正常。测试用静态报告包括 4 个 ID: `android.test.purchased` -: Google Play 报告模拟支付完成. 此报告包含一个 JSON 字符串, 内含模拟支付信息 (比如模拟用户ID等). +: Google Play 报告模拟支付完成。此报告包含一个 JSON 字符串,内含模拟支付信息(比如模拟用户 ID 等)。 `android.test.canceled` -: Google Play 报告模拟支付取消. 这种情况可能因为付款时出现错误, 比如信用卡无效, 或者用户付款前订单被取消. +: Google Play 报告模拟支付取消。这种情况可能因为付款时出现错误,比如信用卡无效,或者用户付款前订单被取消。 `android.test.refunded` -: Google Play 报告模拟退款完成. +: Google Play 报告模拟退款完成。 `android.test.item_unavailable` -: Google Play 报告app可购买物品列表里找不到用户购买的项目. +: Google Play 报告 app 可购买物品列表里找不到用户购买的项目。 ## 内支付准备流程 -iOS 和 Android 内支付的准备流程差不多: +iOS 和 Android 内支付的准备流程差不多: -1. 注册为 Apple 或 Google Play 开发者. -2. 为目标平台进行项目配置. 参考 [iOS 开发教程](/manuals/ios) 与 [Android 开发教程](/manuals/android). -3. 为应用商店进行测试配置: +1. 注册为 Apple 或 Google Play 开发者。 +2. 为目标平台进行项目配置。参考 [iOS 开发教程](/manuals/ios) 与 [Android 开发教程](/manuals/android)。 +3. 为应用商店进行测试配置: - - Android 上, 使用 [Google Play Developer Console](https://play.google.com/apps/publish/). - - iOS 上, 使用 [iTunes Connect](https://itunesconnect.apple.com/). 注意你的 App ID (于 https://developer.apple.com 上的 "成员中心" 配置) 要开启 "In-App Purchase" 选项. + - Android 上,使用 [Google Play Developer Console](https://play.google.com/apps/publish/)。 + - iOS 上,使用 [iTunes Connect](https://itunesconnect.apple.com/)。注意你的 App ID(于 https://developer.apple.com 上的 "成员中心" 配置)要开启 "In-App Purchase" 选项。 ![iTunes Connect and Google Play Dev Console](images/iap/itunes_connect_google_play.png) -4. 对于 Google Play, 你需要 _上传并发布_ 测试版 *.apk* 文件. 对于 iTunes Connect, 直到应用审批之前你 _不用上传_ 应用文件给 iTunes Connect. 加入上传了未完成的应用文件给 iTunes Connect, Apple 会拒绝该应用. +4. 对于 Google Play,你需要 _上传并发布_ 测试版 *.apk* 文件。对于 iTunes Connect,直到应用审批之前你 _不用上传_ 应用文件给 iTunes Connect。加入上传了未完成的应用文件给 iTunes Connect,Apple 会拒绝该应用。 -5. 设置应用内消费品. +5. 设置应用内消费品。 ![iTunes 消费品](images/iap/itunes_products.png) ![Google Play 消费品](images/iap/google_play_products.png) -6. 设置测试用账户. - - 在 iTunes Connect 页面 *用户与规则* 中可以设置专门用于 _沙盒环境_ 的模拟支付用户. 要给应用做开发者证书签名然后在测试设备上登录 Appstore 沙盒账户进行测试. - - 在 Google Play Developer Console 上, 选择 *设置 > 账户详情* 的 License Testing 部分设置测试用户 email. 多个 email 用逗号分隔. 然后测试者可以登录并购买消费品而不会真的扣款. - - 在 Google Play 上, 还需要为测试者们建立 Google Group. Google 使用组来管理应用测试组成员. 在 *Alpha Testing* 页面的 *Manage list of testers* 设置测试者 Google Group. 应用上线前必须通过 Alpha 测试. +6. 设置测试用账户。 + - 在 iTunes Connect 页面 *用户与规则* 中可以设置专门用于 _沙盒环境_ 的模拟支付用户。要给应用做开发者证书签名然后在测试设备上登录 Appstore 沙盒账户进行测试。 + - 在 Google Play Developer Console 上,选择 *设置 > 账户详情* 的 License Testing 部分设置测试用户 email。多个 email 用逗号分隔。然后测试者可以登录并购买消费品而不会真的扣款。 + - 在 Google Play 上,还需要为测试者们建立 Google Group。Google 使用组来管理应用测试组成员。在 *Alpha Testing* 页面的 *Manage list of testers* 设置测试者 Google Group。应用上线前必须通过 Alpha 测试。 ![Alpha testers](images/iap/alpha_testers.png) -Facebook 内支付准备流程: +Facebook 内支付准备流程: -1. 注册为 Facebook 开发者. 登录 [Facebook for developers](https://developers.facebook.com/), 在 "My Apps" 下 "Register as a developer", 完成注册步骤. -2. Facebook 要求支持同步和异步两种支付方式. 详情请见 [Payment overview](https://developers.facebook.com/docs/payments/overview) -3. 建立应用托管和回调服务器: - * 建立托管应用的加密 canvas 地址. 详情请见 [Games on Facebook](https://developers.facebook.com/docs/games/gamesonfacebook/hosting). - * 建立回调服务器. 详情请见 [Setting up your callback server](https://developers.facebook.com/docs/payments/realtimeupdates#yourcallbackserver). -4. 配置应用. 详情请见 [Facebook Developer Dashboard](https://developers.facebook.com/quickstarts/?platform=canvas). -5. 加入测试用户. 在应用面板的 "Canvas Payments" 部分进行设置. -6. 建立应用消费品. 详情请见 [Defining products](https://developers.facebook.com/docs/payments/implementation-guide/defining-products/). +1. 注册为 Facebook 开发者。登录 [Facebook for developers](https://developers.facebook.com/),在 "My Apps" 下 "Register as a developer",完成注册步骤。 +2. Facebook 要求支持同步和异步两种支付方式。详情请见 [Payment overview](https://developers.facebook.com/docs/payments/overview) +3. 建立应用托管和回调服务器: + * 建立托管应用的加密 canvas 地址。详情请见 [Games on Facebook](https://developers.facebook.com/docs/games/gamesonfacebook/hosting)。 + * 建立回调服务器。详情请见 [Setting up your callback server](https://developers.facebook.com/docs/payments/realtimeupdates#yourcallbackserver)。 +4. 配置应用。详情请见 [Facebook Developer Dashboard](https://developers.facebook.com/quickstarts/?platform=canvas)。 +5. 加入测试用户。在应用面板的 "Canvas Payments" 部分进行设置。 +6. 建立应用消费品。详情请见 [Defining products](https://developers.facebook.com/docs/payments/implementation-guide/defining-products/)。 ## 异步的内支付 API -内支付 API 是异步的, 也就是说应用发送信息给服务器, 然后能继续运行. 等到从服务器传回的信息时, 一个 _回调_ 函数会被调用, 从中可以根据回调数据进行各种处理工作. +内支付 API 是异步的,也就是说应用发送信息给服务器,然后能继续运行。等到从服务器传回的信息时,一个 _回调_ 函数会被调用,从中可以根据回调数据进行各种处理工作。 -获得消费品列表: +获得消费品列表: ```lua local COINS_ID = "com.defold.examples.coins" @@ -113,12 +113,12 @@ local function product_list(self, products, error) end function init(self) - -- 初始化消费品列表 (对于 Google Play 来说一次能获取 20 个) + -- 初始化消费品列表(对于 Google Play 来说一次能获取 20 个) iap.list({ COINS_ID, LOGO_ID }, product_list) end ``` -对于正式交易, 首先要注册交易结果监听器, 然后在玩家购买时调用内支付函数: +对于正式交易,首先要注册交易结果监听器,然后在玩家购买时调用内支付函数: ```lua local function iap_listener(self, transaction, error) @@ -141,7 +141,7 @@ end function on_message(self, message_id, message, sender) ... - -- 注册内支付结果监听器. + -- 注册内支付结果监听器。 iap.set_listener(iap_listener) -- 买一个金币... iap.buy(COINS_ID) @@ -149,7 +149,7 @@ function on_message(self, message_id, message, sender) end ``` -操作系统会自动弹出支付界面. 要是处于测试/沙盒环境下, 界面中会明确标明. +操作系统会自动弹出支付界面。要是处于测试/沙盒环境下,界面中会明确标明。 ![Confirm purchase](images/iap/ios_confirm_purchase.png) @@ -159,44 +159,43 @@ end ## 实时付款 -大多数付款系统都是实时的. 支付完成时应用会收到一个消息, TRANS_STATE_PURCHASED. 这是交易的最终状态, 表明对于这笔交易不会再有其他消息了. +大多数付款系统都是实时的。支付完成时应用会收到一个消息,TRANS_STATE_PURCHASED。这是交易的最终状态,表明对于这笔交易不会再有其他消息了。 ## 非实时付款 -有的付款系统需要非实时的支持. 也就是说你的应用只在付款开始时收到一个消息. 为了验证付款完成与否, 需要你的服务器 (或者客户端) 与付款系统进行更多的交流. -这种情况下支付完成时应用会收到一个 TRANS_STATE_UNVERIFIED 消息 (而不是 TRANS_STATE_PURCHASED). 这也是交易的最终状态, 表明对于这笔交易不会再有其他消息了. +有的付款系统需要非实时的支持。也就是说你的应用只在付款开始时收到一个消息。为了验证付款完成与否,需要你的服务器(或者客户端)与付款系统进行更多的交流。 +这种情况下支付完成时应用会收到一个 TRANS_STATE_UNVERIFIED 消息(而不是 TRANS_STATE_PURCHASED)。这也是交易的最终状态,表明对于这笔交易不会再有其他消息了。 ## 购买交付 -玩家购买消费品之后, 应用有义务告知玩家支付已完成 (比如服务器验证支付成功之后). -内支付支持 auto-completion, 也就是自动产生支付完成的消息 (默认设置). 也可以在项目配置里关闭 auto-completion. 支付完成时手动调用 `iap.finish()`, 来产生完成消息. +玩家购买消费品之后,应用有义务告知玩家支付已完成(比如服务器验证支付成功之后)。 +内支付支持 auto-completion,也就是自动产生支付完成的消息(默认设置)。也可以在项目配置里关闭 auto-completion。支付完成时手动调用 `iap.finish()`,来产生完成消息。 ### 消耗品与非消耗品 -Google Play 应用商店只支持消耗品. 必须使用非消耗品的话就关闭 auto-completion 并且不要调用完成函数. 这样在 `iap.set_listener()` 被调用时, 消费品购买总是保持着购买中的状态. +Google Play 应用商店只支持消耗品。必须使用非消耗品的话就关闭 auto-completion 并且不要调用完成函数。这样在 `iap.set_listener()` 被调用时,消费品购买总是保持着购买中的状态。 -Apple App Store 支持非消耗品, 也就是说购买完成时才向玩家提供消费品交付. 这种情况下 auto-completion 可以开启也可以关闭 (比如你需要自己验证支付成功) 之后调用 `iap.finish()`. +Apple App Store 支持非消耗品,也就是说购买完成时才向玩家提供消费品交付。这种情况下 auto-completion 可以开启也可以关闭(比如你需要自己验证支付成功)之后调用 `iap.finish()`。 ## 支付收据 -收据是一堆加密数据, 可以把它发送给 App Store 来验证付款是否真正完成. 这是用开发者服务器验证付款必须使用的东西. +收据是一堆加密数据,可以把它发送给 App Store 来验证付款是否真正完成。这是用开发者服务器验证付款必须使用的东西。 ## 除错 - Android `iap.list()` 返回 "failed to fetch product" -: 你需要上传 *.apk* 文件到 Google Play Developer Console 的 alpha 或者 beta 频道. 还要注意确保你的设配上 _日期和时间_ 是正确的. +: 你需要上传 *.apk* 文件到 Google Play Developer Console 的 alpha 或者 beta 频道。还要注意确保你的设配上 _日期和时间_ 是正确的。 Android (Google Play) `iap.list()` 只返回 20 个消费品 -: Google 有 [每次请求返回最多 20 个消费品的限制](https://github.com/googlesamples/android-play-billing/blob/master/TrivialDrive/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl#L62). 可以多次调用 `iap.list()` 然后合并返回结果以实现20个以上的消费品列表. +: Google 有 [每次请求返回最多 20 个消费品的限制](https://github.com/googlesamples/android-play-billing/blob/master/TrivialDrive/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl#L62)。可以多次调用 `iap.list()` 然后合并返回结果以实现 20 个以上的消费品列表。 iOS `iap.list()` 返回空值 -: 确保你的账户是 iOS 付款应用账户, 并且签署好一切所需文件. 没有经过认证的话, 你的 iOS 应用内支付 (甚至测试支付) 操作不会正确执行. +: 确保你的账户是 iOS 付款应用账户,并且签署好一切所需文件。没有经过认证的话,你的 iOS 应用内支付(甚至测试支付)操作不会正确执行。 - 检查 "Member Center" 上的应用ID是否开启了 in-app purchases (检查 "Member Center" 里 "Certificates, Identifiers & Profiles" 的 "Enabled Services:" 项) 并且该应用 (或者调试版应用) 已用供应商档案进行了签名而且签名没过期. + 检查 "Member Center" 上的应用 ID 是否开启了 in-app purchases(检查 "Member Center" 里 "Certificates, Identifiers & Profiles" 的 "Enabled Services:" 项)并且该应用(或者调试版应用)已用供应商档案进行了签名而且签名没过期。 - 内支付消费品信息传到沙盒环境需要几小时时间, 请耐心等待. + 内支付消费品信息传到沙盒环境需要几小时时间,请耐心等待。 iOS `iap.list()` 产生错误日志 "Unexpected callback set" -: `iap.list()` 调用不可以嵌套. 从 `iap.list()` 调用 `iap.list()` 就会产生这个错误日志. +: `iap.list()` 调用不可以嵌套。从 `iap.list()` 调用 `iap.list()` 就会产生这个错误日志。 在 iOS 上, "price_string" 里有个 '~' 字符 : '~' 是未找到字体的字符占位符. 调用 `iap.list()` 返回的数据使用 _不间断空白_ (`\u00a0`) 分割价格和货币符号并填充 "price_string" 变量. 使用 GUI 组件渲染的话, 需要在字体 *extra_characters* 属性里设置所需字符. 在 macOS 上按 Option + SPACE 就是不间断空白. 详情请见 http://en.wikipedia.org/wiki/Non-breaking_space. diff --git a/docs/zh/manuals/extension-push.md b/docs/zh/manuals/extension-push.md index e99606d1..4a23a492 100644 --- a/docs/zh/manuals/extension-push.md +++ b/docs/zh/manuals/extension-push.md @@ -1,29 +1,29 @@ --- title: Defold 的 iOS 和 Android 推送通知 -brief: 本教程介绍了如何配置和实现游戏中的本地和远程 iOS 与 Android 平台的推送通知. +brief: 本手册介绍了如何配置和实现游戏中的本地和远程 iOS 与 Android 平台的推送通知。 --- # 推送通知 -推送通知作为 [原生扩展](/manuals/extensions/) 在 iOS 和 Android 设备上用来给玩家提示修改和更新之类的信息. iOS 和 Android 实现方法基本一样但是也有一些平台相关的东西需要注意. +推送通知作为 [原生扩展](/manuals/extensions/) 在 iOS 和 Android 设备上用来给玩家提示修改和更新之类的信息。iOS 和 Android 实现方法基本一样但是也有一些平台相关的东西需要注意。 -推送通知为了找到从服务器到目标设备的通路, 需要得到应用的一些信息. 最复杂的部分包括用于校验客户端接收通知的合法性的应用安全信息. 还需要用于 Apple 或者 Google 校验你发送通知合法性的服务器安全信息. 最后, 推送通知的时候, 你需要知道到用户设备的唯一路径. 为此你需要指定设备的唯一 token (用来分辨用户). +推送通知为了找到从服务器到目标设备的通路,需要得到应用的一些信息。最复杂的部分包括用于校验客户端接收通知的合法性的应用安全信息。还需要用于 Apple 或者 Google 校验你发送通知合法性的服务器安全信息。最后,推送通知的时候,你需要知道到用户设备的唯一路径。为此你需要指定设备的唯一 token(用来分辨用户)。 ## 安装扩展 -使用之前要在 *game.project* 文件里配置扩展依赖. 最新稳定版依赖的 URL 是: +使用之前要在 *game.project* 文件里配置扩展依赖。最新稳定版依赖的 URL 是: ``` https://github.com/defold/extension-push/archive/master.zip ``` -推荐使用 [指定版本](https://github.com/defold/extension-push/releases) 的zip包链接作为依赖. +推荐使用 [指定版本](https://github.com/defold/extension-push/releases) 的 zip 包链接作为依赖。 ``` https://github.com/defold/extension-firebase-core/archive/master.zip ``` -API 文档在 [推送通知扩展项目](https://defold.github.io/extension-push/) 页上. +API 文档在 [推送通知扩展项目](https://defold.github.io/extension-push/) 页上。 ## iOS 配置 @@ -31,13 +31,13 @@ API 文档在 [推送通知扩展项目](https://defold.github.io/extension-push 为了更好地使用 Apple Push Notification 服务, 最好先熟读 [Apple 的官方文档](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html). ::: -iOS 上, 推送通知需要做以下准备: +iOS 上,推送通知需要做以下准备: -* 要为 App ID 开启推送通知. -* 需要包含此 App ID 的应用提供商档案. -* 还需要一个 SSL 证书用来把数据从消息服务应用发送到 Apple 服务器. +* 要为 App ID 开启推送通知。 +* 需要包含此 App ID 的应用提供商档案。 +* 还需要一个 SSL 证书用来把数据从消息服务应用发送到 Apple 服务器。 -准备就绪后登录到 [Apple 开发者中心](https://developer.apple.com/membercenter). 找到 AppID 打开通知推送服务. +准备就绪后登录到 [Apple 开发者中心](https://developer.apple.com/membercenter)。找到 AppID 打开通知推送服务。 ![AppID push notifications](images/push/push_ios_app_id.png) @@ -45,20 +45,20 @@ iOS 上, 推送通知需要做以下准备: ![APN SSL certificate](images/push/push_ios_certificate.png) -从你的服务器往通知推送服务器发送数据时需要这个证书. 开发调试时, 可以先在本地安装这个证书然后运行 [APNS-Pusher](https://github.com/KnuffApp/APNS-Pusher) 或者 [NWPusher](https://github.com/noodlewerk/NWPusher) 之类的测试工具. +从你的服务器往通知推送服务器发送数据时需要这个证书。开发调试时,可以先在本地安装这个证书然后运行 [APNS-Pusher](https://github.com/KnuffApp/APNS-Pusher) 或者 [NWPusher](https://github.com/noodlewerk/NWPusher) 之类的测试工具。 -记得创建包含要推送通知 AppID 的供应商档案, 并且在档案中设置好开发设备. 可以在 "Member Center" 页面或者 Xcode 中进行这些操作. +记得创建包含要推送通知 AppID 的供应商档案,并且在档案中设置好开发设备。可以在 "Member Center" 页面或者 Xcode 中进行这些操作。 ![Provisioning profile](images/push/push_ios_provisioning_profile.png) -注意 Apple 的沙盒服务器可能需要一段时间更新信息, 通知推送功能也许不能马上使用. 要有耐心. +注意 Apple 的沙盒服务器可能需要一段时间更新信息,通知推送功能也许不能马上使用。要有耐心。 推送通知测试代码如下: ```lua local function push_listener(self, payload, origin) - -- 通知到达这里. + -- 通知到达这里。 pprint(payload) end @@ -80,7 +80,7 @@ function on_message(self, message_id, message) for i = 1,#token do t = t .. string.format("%02x", string.byte(token, i)) end - -- 打印设备token + -- 打印设备 token print(t) else -- 错误 @@ -91,7 +91,7 @@ function on_message(self, message_id, message) elseif message_id == hash("push_android") then push.register(nil, function (self, token, error) if token then - -- 打印设备token + -- 打印设备 token print(token) else -- 错误 @@ -103,17 +103,17 @@ function on_message(self, message_id, message) end ``` -如果一切顺利通知监听器会被注册然后打印出设备的 token: +如果一切顺利通知监听器会被注册然后打印出设备的 token: ```txt DEBUG:SCRIPT: 1f8ba7869b84b10df69a07aa623cd7f55f62bca22cef61b51fedac643ec61ad8 ``` -如果运行的是测试应用, 你现在就可以尝试使用设备token和通知服务SSL证书向你的设备推送通知了. +如果运行的是测试应用,你现在就可以尝试使用设备 token 和通知服务 SSL 证书向你的设备推送通知了。 ![Pusher test](images/push/push_ios_pusher.png) -通知发送之后会被测试应用接收, 进入 `push_listener()` 函数: +通知发送之后会被测试应用接收,进入 `push_listener()` 函数: ```txt DEBUG:SCRIPT: @@ -126,11 +126,11 @@ DEBUG:SCRIPT: } ``` -同时 iOS 桌面会提示通知: +同时 iOS 桌面会提示通知: ![iOS notification](images/push/push_ios_notification.png) -如果要在图标上显示计数角标, 可以使用 `push.set_badge_count()` 函数. +如果要在图标上显示计数角标,可以使用 `push.set_badge_count()` 函数。 ## Android 配置 @@ -138,35 +138,35 @@ DEBUG:SCRIPT: 建议首先阅读 Firebase Cloud Messaging 服务文档. 文档在这里 https://firebase.google.com/docs/cloud-messaging/ ::: -Android 上, 推送通知需要做以下准备: +Android 上,推送通知需要做以下准备: -* Firebase CM Sender ID 和应用 ID. 这些id会打包到应用中去. -* 用于使用 Firebase 服务的服务器 API Key. +* Firebase CM Sender ID 和应用 ID。这些 id 会打包到应用中去。 +* 用于使用 Firebase 服务的服务器 API Key。 ::: sidenote -如果你的应用使用了 Google Cloud Messaging 平台, 现在需要导入 Firebase Cloud Messaging 平台中. 把应用从 Google dashboard 导入到 Firebase 即可. 导入后 *sender id* 保持不变, 你的 *game.project* 文件需要更新使用 `Firebase Application ID` (见下文). +如果你的应用使用了 Google Cloud Messaging 平台,现在需要导入 Firebase Cloud Messaging 平台中。把应用从 Google dashboard 导入到 Firebase 即可。导入后 *sender id* 保持不变,你的 *game.project* 文件需要更新使用 `Firebase Application ID`(见下文)。 ::: -基本配置很简单. 登录 [https://console.firebase.google.com](https://console.firebase.google.com), 选择或者新建应用. 然后进入 *Grow* 的 *Cloud Messaging* 页面. +基本配置很简单。登录 [https://console.firebase.google.com](https://console.firebase.google.com),选择或者新建应用。然后进入 *Grow* 的 *Cloud Messaging* 页面。 ![Adding Cloud Messaging to a Firebase project](images/push/push_fcm_add_cm.png) -点击 Android 图标开始配置通知推送. +点击 Android 图标开始配置通知推送。 ![Configure Cloud Messaging for Android](images/push/push_fcm_configure_android.png) -根据提示, 输入 Defold 游戏项目使用的包名. +根据提示,输入 Defold 游戏项目使用的包名。 ![Android cloud services info](images/push/push_fcm_register.png) -下载 `google-services.json`, 我们需要这个文件里面的一些信息. +下载 `google-services.json`,我们需要这个文件里面的一些信息。 ![Google Cloud Messaging sender ID](images/push/push_fcm_download_json.png) -下面两步可以跳过, *Add Firebase SDK* 和 *Run your app to verify installation*. SDK 已打包进 Defold, 无需手动添加. +下面两步可以跳过,*Add Firebase SDK* 和 *Run your app to verify installation*。SDK 已打包进 Defold,无需手动添加。 -用文本编辑器打开 `google-services.json` 文件, 找到 *`project_number`* 和 *`mobilesdk_app_id`*. 把这俩id考入 *game.project* 文件, 对应 *android* 部分, `Gcm Sender Id` (`project_number`) 和 `Fcm Application Id` (`mobilesdk_app_id`). +用文本编辑器打开 `google-services.json` 文件,找到 *`project_number`* 和 *`mobilesdk_app_id`*。把这俩 id 考入 *game.project* 文件,对应 *android* 部分,`Gcm Sender Id`(`project_number`)和 `Fcm Application Id`(`mobilesdk_app_id`)。 ``` { @@ -189,13 +189,13 @@ Android 上, 推送通知需要做以下准备: ![Settings applied to game.project](images/push/push_fcm_game_project.png) -至此客户端准备就绪. [上面测试ios时用的代码](#above-code) 对 Android 同样有效. 运行并记录设备 token. +至此客户端准备就绪。[上面测试 ios 时用的代码](#above-code) 对 Android 同样有效。运行并记录设备 token。 ```txt DEBUG:SCRIPT: APA91bHkcKm0QHAMUCEQ_Dlpq2gzset6vh0cz46kDDV6230C5rFivyWZMCxGXcjxRDKg1PK4z1kWg3xnUVqSDiO_4_RiG8b8HeYJfaoW1ho4ukWYXjq5RE0Sy-JTyrhqRusUP_BxRTcE ``` -发送通知之前需要一个 Firebase 服务认证 key. 这个 key 位于 Firebase dashboard 的 *Settings* 的 *Cloud Messaging* 页面中. +发送通知之前需要一个 Firebase 服务认证 key。这个 key 位于 Firebase dashboard 的 *Settings* 的 *Cloud Messaging* 页面中。 ![Server Key location](images/push/push_fcm_server_key.png) diff --git a/docs/zh/manuals/extension-websocket.md b/docs/zh/manuals/extension-websocket.md index 6b8de20e..07f8865a 100644 --- a/docs/zh/manuals/extension-websocket.md +++ b/docs/zh/manuals/extension-websocket.md @@ -1,7 +1,7 @@ --- title: WebSocket 连接 -brief: 本教程介绍了 websocket 连接的使用方法. +brief: 本手册介绍了 WebSocket 连接的使用方法。 --- ## WebSocket 连接 -Defold 没有内置 WebSocket 连接功能. 建议使用 [Defold-WebSocket 扩展程序](https://github.com/britzl/defold-websocket) 实现该功能. +Defold 没有内置 WebSocket 连接功能。建议使用 [Defold-WebSocket 扩展程序](https://github.com/britzl/defold-websocket) 实现该功能。 diff --git a/docs/zh/manuals/extension-webview.md b/docs/zh/manuals/extension-webview.md index 9c1011be..03028ea1 100644 --- a/docs/zh/manuals/extension-webview.md +++ b/docs/zh/manuals/extension-webview.md @@ -1,99 +1,99 @@ --- title: Defold 的 WebViews -brief: WebViews 可以在你的手机上显示一个网页层. 同时支持在后台运行用户定义的js代码. 本教程介绍了 Defold 的官方 WebView 扩展, API 和功能. +brief: WebViews 可以在你的手机上显示一个网页层。同时支持在后台运行用户定义的 js 代码。本手册介绍了 Defold 的官方 WebView 扩展、API 和功能。 --- # WebViews -WebView 提供了一套特殊的 API 用来在手机上显示一个网页层. 首先让我们来实现一个简单的 webview. -然后我们会讨论如何使用一套简单的控制按钮控制这个 webview. +WebView 提供了一套特殊的 API 用来在手机上显示一个网页层。首先让我们来实现一个简单的 webview。 +然后我们会讨论如何使用一套简单的控制按钮控制这个 webview。 ## 安装扩展 -在你的 *game.project* 文件中设置 webview 依赖. -最新版本位于如下 URL: +在你的 *game.project* 文件中设置 webview 依赖。 +最新版本位于如下 URL: ``` https://github.com/defold/extension-webview/archive/master.zip ``` -API文档位于 [扩展首页](https://defold.github.io/extension-webview/). +API 文档位于 [扩展首页](https://defold.github.io/extension-webview/)。 ## 打开 webview -使用 `webview.create` 就能创建一个网页层, 并且返回一个唯一id. 下文中我们把这个 ID 称为 `webview_id`, -多个 `webview` 之间进行交互时会用到这个id. 也就是说创建和维护多个 webview 是可行的. +使用 `webview.create` 就能创建一个网页层,并且返回一个唯一 id。下文中我们把这个 ID 称为 `webview_id`, +多个 `webview` 之间进行交互时会用到这个 id。也就是说创建和维护多个 webview 是可行的。 -`webview.create` 带着一个函数参数, 待会儿我们再来仔细看看这个回调, 现在先给它留空. +`webview.create` 带着一个函数参数,待会儿我们再来仔细看看这个回调,现在先给它留空。 ```lua local webview_id = webview.create(function() -- 目前无代码 end) ``` -默认状况下新建的 webview 都是不可见的, 因为它还没有加载任何内容. 其主要功能就是显示网页, 现在就来加载个超酷的网页吧! +默认状况下新建的 webview 都是不可见的,因为它还没有加载任何内容。其主要功能就是显示网页,现在就来加载个超酷的网页吧! -调用 `webview.open` 函数, 第一个参数就是上文的那个 `webview_id`, 第二个参数是要打开的URL. +调用 `webview.open` 函数,第一个参数就是上文的那个 `webview_id`,第二个参数是要打开的 URL。 ```lua local request_id = webview.open(webview_id, "http://www.defold.com") --广告无处不在 ``` -这个函数返回网页请求的id, 这个id我们稍后也会用到. +这个函数返回网页请求的 id,这个 id 我们稍后也会用到。 -如果一切顺利你将看到神奇的网页占满了屏幕, 就是 Defold 官方首页. +如果一切顺利你将看到神奇的网页占满了屏幕,就是 Defold 官方首页。 ::: sidenote -要在 iOS 里访问的网页必须遵循在 `Info.plist` 文件中的 `NSAppTransportSecurity` 里面设置好的键值对. +要在 iOS 里访问的网页必须遵循在 `Info.plist` 文件中的 `NSAppTransportSecurity` 里面设置好的键值对。 ``` NSAllowsArbitraryLoads ``` -开发的时候可以随便设置, 但是发布到 App Store 之后就只能通过使用 `NSExceptionDomains` 键代替. 这有点超出本教程讨论范围, -详情可以参考 [Apple 开发者文档](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35). +开发的时候可以随便设置,但是发布到 App Store 之后就只能通过使用 `NSExceptionDomains` 键代替。这有点超出本手册讨论范围, +详情可以参考 [Apple 开发者文档](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)。 ::: ## 回调函数 -顺利打开网页固然是好, 但要是 URL 无效或者出现其他不可预知的问题时要给用户显示错误信息怎么办? 或者要在用户离开网页时做些什么该怎么办? -幸运的是 webview 扩展程序具备了报错, 导航, 载入成功等等事件回调功能. 只需在调用 `webview.create` 时传入回调函数作为参数即可. +顺利打开网页固然是好,但要是 URL 无效或者出现其他不可预知的问题时要给用户显示错误信息怎么办?或者要在用户离开网页时做些什么该怎么办? +幸运的是 webview 扩展程序具备了报错、导航、载入成功等等事件回调功能。只需在调用 `webview.create` 时传入回调函数作为参数即可。 -回调函数特征如下: +回调函数特征如下: ```lua function callback(self, webview_id, request_id, type, data) ``` -* **`self`** - 回调函数所处的脚本引用. -* **`webview_id`** - 回调函数可以做到多个webview共用, 这个参数引用的是回调事件发生的那个webview的id. -* **`request_id`** - 同样 `webview.open` 也可以共用一个回调回调函数, 这个参数引用的是回调事件发生的那个载入请求的id. -* **`type`** - 事件的类型, 枚举类型, 下文 *回调类型* 章节会详细探讨. -* **`data`** - 不同时间连带的各种数据信息. +* **`self`** - 回调函数所处的脚本引用。 +* **`webview_id`** - 回调函数可以做到多个 webview 共用,这个参数引用的是回调事件发生的那个 webview 的 id。 +* **`request_id`** - 同样 `webview.open` 也可以共用一个回调回调函数,这个参数引用的是回调事件发生的那个载入请求的 id。 +* **`type`** - 事件的类型,枚举类型,下文 *回调类型* 章节会详细探讨。 +* **`data`** - 不同时间连带的各种数据信息。 #### 回调类型 回调的 `type` 参数可以被设置为以下枚举之一: -* **`webview.CALLBACK_RESULT_URL_LOADING`** - 在 webview 内导航时, 载入事件被触发. 调用 `webview.open` 或者 -用户点击页面上的超级链接都会造成事件触发. 事件的处理结果决定了载入请求是否被允许. 如果返回 `false` 则载入终止, 其他值则是允许载入. -被允许的链接也会记录在回调函数 `data` 表的 `url` 项里. -* **`webview.CALLBACK_RESULT_URL_ERROR`** 和 **`webview.CALLBACK_RESULT_EVAL_ERROR`** -一旦载入出错, 或者 -执行JavaScript脚本出错 (下文 *运行JavaScript脚本* 章节会详细探讨), 错误事件就会被触发. `data` 表的 `result` 项用一个字符串记录了错误的详细内容. -* **`webview.CALLBACK_RESULT_URL_OK`** 和 **`webview.CALLBACK_RESULT_EVAL_OK`** - 一旦载入成功, 或者执行JavaScript脚本成功, -OK事件就会被触发. 对于成功执行的JavaScript程序, 其执行结果会被保存在 `data` 表的 `result` 项之中. +* **`webview.CALLBACK_RESULT_URL_LOADING`** - 在 webview 内导航时,载入事件被触发。调用 `webview.open` 或者 +用户点击页面上的超级链接都会造成事件触发。事件的处理结果决定了载入请求是否被允许。如果返回 `false` 则载入终止,其他值则是允许载入。 +被允许的链接也会记录在回调函数 `data` 表的 `url` 项里。 +* **`webview.CALLBACK_RESULT_URL_ERROR`** 和 **`webview.CALLBACK_RESULT_EVAL_ERROR`** - 一旦载入出错,或者 +执行 JavaScript 脚本出错(下文 *运行 JavaScript 脚本* 章节会详细探讨),错误事件就会被触发。`data` 表的 `result` 项用一个字符串记录了错误的详细内容。 +* **`webview.CALLBACK_RESULT_URL_OK`** 和 **`webview.CALLBACK_RESULT_EVAL_OK`** - 一旦载入成功,或者执行 JavaScript 脚本成功, +OK 事件就会被触发。对于成功执行的 JavaScript 程序,其执行结果会被保存在 `data` 表的 `result` 项之中。 -事件触发介绍完了, 现在我们来看一个复杂点的例子. +事件触发介绍完了,现在我们来看一个复杂点的例子。 -比如我们要向玩家展示一个反馈页面, 玩家通过一个 HTML 表格发送反馈意见. 我们要在载入不成功的时候要提示友好的错误信息并关闭webview而不是把白屏留给玩家. -接下来我们尽量不让玩家离开这个反馈页面. +比如我们要向玩家展示一个反馈页面,玩家通过一个 HTML 表格发送反馈意见。我们要在载入不成功的时候要提示友好的错误信息并关闭 webview 而不是把白屏留给玩家。 +接下来我们尽量不让玩家离开这个反馈页面。 -升级版的 `webview.create` 调用和回调被设计成下面这样: +升级版的 `webview.create` 调用和回调被设计成下面这样: ```lua local player_feedback_url = "https://example.com/my_game_name/customer_feedback" local function webview_callback(self, webview_id, request_id, type, data) if type == webview.CALLBACK_RESULT_URL_ERROR then - -- 遇到错误! - -- 关闭 webview 然后显示提示文本! + -- 遇到错误! + -- 关闭 webview 然后显示提示文本! webview.destroy(webview_id) label.set_text("#label", "Player feedback not available at this moment.") elseif type == webview.CALLBACK_RESULT_URL_LOADING then - -- 不让玩家离开这个页面. + -- 不让玩家离开这个页面。 if data.url ~= player_feedback_url then return false end @@ -104,23 +104,23 @@ local feedback_webview = webview.create(webview_callback) webview.open(feedback_webview, player_feedback_url) ``` -一个反馈页面展示就完成了. 网页自己做, 打开后大概像这个样子: +一个反馈页面展示就完成了。网页自己做,打开后大概像这个样子: ![Player feedback web page](images/webview/webview_player_feedback1.png) -注意: 我们的例子里有一个指向 google.com 的超级链接, 目的是试试玩家点别的链接出不出的去. --牛虻 +注意:我们的例子里有一个指向 google.com 的超级链接,目的是试试玩家点别的链接出不出的去。——牛虻 -强制反馈功能完成! 但是怎么提交反馈呢? 也许玩家不想提交反馈呢, 怎么返回游戏? 再说, 全屏的网页真的合适吗? 后面的游戏界面全都被挡住了啊! -我们会在下文的 *运行 JavaScript 脚本* 和 *可视定位控制面板* 章节继续探讨. +强制反馈功能完成!但是怎么提交反馈呢?也许玩家不想提交反馈呢,怎么返回游戏?再说,全屏的网页真的合适吗?后面的游戏界面全都被挡住了啊! +我们会在下文的 *运行 JavaScript 脚本* 和 *可视定位控制面板* 章节继续探讨。 ## 载入展示自己的 HTML 网页 -继续深入探讨之前, 我们来动手做一个可以用来交互的 HTML 简单网页. +继续深入探讨之前,我们来动手做一个可以用来交互的 HTML 简单网页。 -通过调用 `webview.open_raw` 函数我们可以让webview直接解析加载 HTML 文件源码. 这样即使没有服务器, 网络环境恶劣的情况下, 都可以载入我们的网页. +通过调用 `webview.open_raw` 函数我们可以让 webview 直接解析加载 HTML 文件源码。这样即使没有服务器,网络环境恶劣的情况下,都可以载入我们的网页。 -`webview.open_raw` 的第一个参数和 `webview.open` 一样是 `webview_id`. 第二个参数换成网页源代码引用. +`webview.open_raw` 的第一个参数和 `webview.open` 一样是 `webview_id`。第二个参数换成网页源代码引用。 -下面来看一个把网页代码直接写在脚本里的例子: +下面来看一个把网页代码直接写在脚本里的例子: ```lua local feedback_html = [[ @@ -155,14 +155,14 @@ local webview_id = webview.create(webview_callback) webview.open_raw(webview_id, feedback_html) ``` -效果与前面的示例类似, 不同的是这次我们可以随时任意修改网页代码. -**注意:** 本例中 `webview_callback` 函数与前例类似故忽略. +效果与前面的示例类似,不同的是这次我们可以随时任意修改网页代码。 +**注意:** 本例中 `webview_callback` 函数与前例类似故忽略。 -众所周知一个 HTML 源码包含所有代码和资源是不明智的, 最好把 JavaScript 和 CSS 等资源分门别类保存为文件, 这种情况下 -可以使用 [`sys.load_resource`](https://www.defold.com/ref/sys/#sys.load_resource:filename) 进行资源文件的预加载. -而且这样做还有一个好处就是可以随时打开网页浏览器查看网页效果. +众所周知一个 HTML 源码包含所有代码和资源是不明智的,最好把 JavaScript 和 CSS 等资源分门别类保存为文件,这种情况下 +可以使用 [`sys.load_resource`](https://www.defold.com/ref/sys/#sys.load_resource:filename) 进行资源文件的预加载。 +而且这样做还有一个好处就是可以随时打开网页浏览器查看网页效果。 -新建一个文件夹 (`custom_resources`) 和一个 HTML 文件 (`feedback.html`). 然后用 `feedback_html` 变量引用这些资源. +新建一个文件夹(`custom_resources`)和一个 HTML 文件(`feedback.html`)。然后用 `feedback_html` 变量引用这些资源。 ```lua local feedback_html = sys.load_resource("/custom_resources/feedback.html") @@ -171,48 +171,48 @@ webview.open_raw(webview_id, feedback_html) ``` ## 可视性和位置控制 -现在来解决网页占据全屏问题. +现在来解决网页占据全屏问题。 -为了更方便的互动, 我们要把网页放在游戏屏幕上半部分. 可以使用 `webview.set_position` 函数设置webview的位置和宽高. -设置 `-1` 代表在这个轴向上占据全部屏幕. +为了更方便的互动,我们要把网页放在游戏屏幕上半部分。可以使用 `webview.set_position` 函数设置 webview 的位置和宽高。 +设置 `-1` 代表在这个轴向上占据全部屏幕。 ```lua local webview_id = webview.create(webview_callback) --- 位置: 屏幕左上角 (0, 0) --- 尺寸: 全屏款, 500像素高 +-- 位置:屏幕左上角 (0, 0) +-- 尺寸:全屏宽,500 像素高 webview.set_position(webview_id, 0, 0, -1, 500) ``` ![Resized feedback page](images/webview/webview_player_feedback2.png) -遇到性能不理想的设备, 网页从加载到显示可能会白屏很长时间. 这个体验很不好, 所以加载完成之前我们先把webview隐藏起来. 同时我们还要提示玩家资源正在加载. +遇到性能不理想的设备,网页从加载到显示可能会白屏很长时间。这个体验很不好,所以加载完成之前我们先把 webview 隐藏起来。同时我们还要提示玩家资源正在加载。 -这里用到 `webview.open_raw` (`webview.open` 同样) 的第三个参数, 传入一个表, 然后设置 `hidden` 键为 `true`. -默认是 `false`, 代表像前面示例那样一调用加载就可见. +这里用到 `webview.open_raw`(`webview.open` 同样)的第三个参数,传入一个表,然后设置 `hidden` 键为 `true`。 +默认是 `false`,代表像前面示例那样一调用加载就可见。 ```lua webview.open_raw(webview_id, feedback_html, {hidden = true}) ``` -等到 `webview.CALLBACK_RESULT_URL_OK` 事件触发说明资源加载完毕. 这时就可以显示webview了, 调用 `webview.set_visible` 函数即可. +等到 `webview.CALLBACK_RESULT_URL_OK` 事件触发说明资源加载完毕。这时就可以显示 webview 了,调用 `webview.set_visible` 函数即可。 -升级版的回调函数如下: +升级版的回调函数如下: ```lua local function webview_callback(self, webview_id, request_id, type, data) if type == webview.CALLBACK_RESULT_URL_OK then - -- 顺利完成加载, 显示webview! + -- 顺利完成加载,显示 webview! webview.set_visible(webview_id, 1) elseif type == webview.CALLBACK_RESULT_URL_ERROR then -- ... ``` ## 运行 JavaScript 脚本 -现在来解决关闭webview的事. +现在来解决关闭 webview 的事。 -前例中已经出现过, 遇到错误事件触发时, 调用了 `webview.destroy` 函数. 但是没有错误就没法把握关闭webview的时机. -幸好引擎提供了从 Lua 到webview里运行的 JavaScript 程序的交互功能. 这样我们就能关注webview里js变量的变化. +前例中已经出现过,遇到错误事件触发时,调用了 `webview.destroy` 函数。但是没有错误就没法把握关闭 webview 的时机。 +幸好引擎提供了从 Lua 到 webview 里运行的 JavaScript 程序的交互功能。这样我们就能关注 webview 里 js 变量的变化。 -当玩家按下网页上的一个按钮时, 运行一段程序, 改变一个变量. +当玩家按下网页上的一个按钮时,运行一段程序,改变一个变量。 ```js var shouldClose = false; function closeWebview() { @@ -226,10 +226,10 @@ function submitFeedback() { ``` -不论玩家按了 "提交反馈" 还是 "取消反馈" 按钮, 程序都会把 `shouldClose` 变量设置为 true. +不论玩家按了 "提交反馈" 还是 "取消反馈" 按钮,程序都会把 `shouldClose` 变量设置为 true。 -在 Lua 脚本里我们只要发现 `shouldClose` 变为 true 了, 就知道该调用 `webview.destroy` 了. -正好有个好地方来关注这个变量, 就是在每帧都会被调用的 `update` 函数里. +在 Lua 脚本里我们只要发现 `shouldClose` 变为 true 了,就知道该调用 `webview.destroy` 了。 +正好有个好地方来关注这个变量,就是在每帧都会被调用的 `update` 函数里。 ```lua function update(self, dt) @@ -239,7 +239,7 @@ function update(self, dt) end ``` -注意这里 `webview.eval` 返回的并不是 JavaScript 的运行结果, 而是 *运行请求id*. 我们首先要确定回调函数的请求id, 这个函数里传入的 `data.result` 才是真正的 JavaScript 运行结果. +注意这里 `webview.eval` 返回的并不是 JavaScript 的运行结果,而是 *运行请求 id*。我们首先要确定回调函数的请求 id,这个函数里传入的 `data.result` 才是真正的 JavaScript 运行结果。 ```lua local function webview_callback(self, webview_id, request_id, type, data) @@ -256,4 +256,4 @@ local function webview_callback(self, webview_id, request_id, type, data) -- ... ``` -这样一来玩家在点击按钮之后就能返回继续游戏了! +这样一来玩家在点击按钮之后就能返回继续游戏了! diff --git a/docs/zh/manuals/extensions-best-practices.md b/docs/zh/manuals/extensions-best-practices.md index 092fc92d..ac4c30ee 100644 --- a/docs/zh/manuals/extensions-best-practices.md +++ b/docs/zh/manuals/extensions-best-practices.md @@ -1,72 +1,68 @@ --- title: 原生扩展 - 最佳实践 -brief: 本教程介绍了开发原生扩展的最佳实践. +brief: 本手册介绍了开发原生扩展的最佳实践。 --- # 最佳实践 -编写跨平台代码可能很困难, 但是通过一些方法可以更易于开发与维护. 本教程列举了 Defold 与跨平台原生代码共同工作的 API 和方法. +编写跨平台代码可能很困难,但是通过一些方法可以更易于开发与维护。 -## Defold 代码 +## 编写原生代码 -在 Defold 引擎中我们很少使用 C++ 代码. 事实上, 大多数是 C-like 代码. 除了少数容器类外, 我们去掉了模板, 因为模板会增加编译时间和包体大小. +在 Defold 源码中,C++ 的使用非常有限,大多数代码非常类似于 C。除了少数容器类外,几乎没有模板,因为模板会增加编译时间和可执行文件大小。 ### C++ 版本 -Defold 源码使用默认 C++ 版本编译. +Defold 源码是使用每个编译器的默认 C++ 版本构建的。Defold 源码本身使用的 C++ 版本不高于 C++98。虽然可以使用更高版本来构建扩展,但更高版本可能带来 ABI 变化。这可能会使得无法在引擎或[资源门户](/assets)中将一个扩展与其他扩展一起使用。 -我们不用最新的 C++ 版本及特性. 主要因为默认版本对于游戏引擎足够用了. 追寻最新 C++ 版本特性相当耗费时间. +Defold 源码避免使用 C++ 的最新功能或版本。主要是因为在构建游戏引擎时不需要新功能,而且追踪 C++ 的最新功能是一项耗时的任务,真正掌握这些功能需要大量宝贵时间。 -这也有助于向扩展开发者提供稳定 ABI. 而且使用最新 C++ 特性的话很可能会在不同平台上造成编译问题. +这对扩展开发者还有一个额外的好处,即 Defold 维护了稳定的 ABI。还值得指出的是,使用最新的 C++ 功能可能会由于不同平台的支持程度不同而阻止代码在不同平台上编译。 ### 标准模板库 - STL -Defold 引擎不支持 STL 代码, 除了某些算法和数学库 (std::sort, std::upper_bound 等), 但是你的扩展里可以使用 STL. +由于 Defold 引擎不使用任何 STL 代码,除了一些算法和数学函数(`std::sort`、`std::upper_bound` 等),在你的扩展中使用 STL 可能是可行的。 -再次注意 ABI 的不兼容性可能使你使用其他扩展或者第三方库造成困难. +再次记住,当你的扩展与其他扩展或第三方库一起使用时,ABI 不兼容性可能会阻碍你。 -去掉 (重模板化的) STL 库, 还能减少编译时间, 更重要的是, 减小应用体积. +避免使用(重度模板化的)STL 库,也可以改善我们的构建时间,更重要的是,减少可执行文件大小。 #### 字符串 -在 Defold 引擎中, 我们使用 `const char*` 代替了 `std::string`. +在 Defold 引擎中,使用 `const char*` 而不是 `std::string`。使用 `std::string` 是在混合不同版本的 C++ 或编译器版本时的常见陷阱,因为它可能导致 ABI 不匹配。使用 `const char*` 和一些辅助函数可以避免这种情况。 -`std::string` 使得 C++ 不同版本混合编译造成困难: 原因是 ABI 不匹配. -所以我们选择使用 `const char*` 及相关工具函数代替. +### 使函数隐藏 -### 函数隐藏 - -本地函数尽量使用 `static` 关键字定义. 这样便于编译器优化, 提高性能减小应用体积. +如果可能,在编译单元本地函数上使用 `static` 关键字。这让编译器可以进行一些优化,既可以提高性能,也可以减少可执行文件大小。 ## 第三方库 -当我们选用第三方库时 (不管由什么语言编写), 至少需要考虑这些事情: +当选择要使用的第三方库时(无论语言),请考虑以下几点: -* 功能 - 这个库满足你的功能要求了吗? -* 性能 - 运行时是否需要消耗大量性能? -* 体积 - 会给包体增大多少体积?是否在可接受范围内? -* 依赖 - 是否依赖其他库? -* 支持 - 这个库是个什么样的状态? 是否有许多bug? 是否还在维护? -* 证书 - 是否可以合法使用? +* 功能 - 它是否解决了你遇到的特定问题? +* 性能 - 它是否会在运行时带来性能成本? +* 库大小 - 最终可执行文件会增大多少?这是否可接受? +* 依赖 - 它是否需要额外的库? +* 支持 - 库的状态如何?它是否有很多未解决的问题?它是否仍在维护? +* 许可证 - 是否可以在这个项目中使用? ## 开源依赖 -确定你能访问你的依赖库. 比如说在 GitHub 上托管的库, 随时可能被移除, 突然改变开发方向或者改变拥有者维护者. 如果你fork了这个库就能减少这些变化带来的损失. +始终确保你可以访问你的依赖项。例如,如果你依赖于 GitHub 上的某些内容,没有什么可以阻止该存储库被删除,或者突然改变方向或所有权。你可以通过分叉存储库并使用你的分叉而不是上游项目来减轻这种风险。 -库代码是直接注入你的游戏中的, 所以需要保证它在满足你的要求的前提下不会在后台做什么小动作! +记住,库中的代码将被注入到你的游戏中,所以确保库做了它应该做的事情,而不是其他事情! ## 项目结构 -当你创建扩展, 开发和维护时是有些技巧的. +创建扩展时,有几件事可以帮助开发和维护它。 -### Lua api +### Lua API -应该只有一个 Lua api, 只有一个实现方法. 这样有助于在所有平台上保持一致的表现. +应该只有一个 Lua API,并且只有一个实现。这使得在所有平台上具有相同的行为变得更加容易。 -如果某平台不支持这个扩展, 建议不要注册 Lua 模块. -这样就可以通过检查非 nil 来判断对扩展的支持性: +如果相关平台不支持该扩展,建议根本不注册 Lua 模块。这样你可以通过检查 nil 来检测支持: if myextension ~= nil then myextension.do_something() @@ -74,48 +70,47 @@ Defold 引擎不支持 STL 代码, 除了某些算法和数学库 (std::sort, st ### 文件夹结构 -这是我们开发扩展使用的常用结构. +以下文件夹结构经常用于扩展: /root /input - /main -- 示例项目根目录 + /main -- 实际示例项目的所有文件 /... - /myextension -- 扩展根目录 + /myextension -- 扩展的实际根文件夹 ext.manifest - /include -- 其他扩展使用的外部包含 + /include -- 外部包含,其他扩展使用 /libs - / -- 各个平台使用的外部包含 + / -- 所有支持平台的外部库 /src - myextension.cpp -- 扩展的 Lua api 及其生命周期函数 - 还包含 Lua api 功能的通用实现方法. - myextension_private.h -- 每个平台需要实现的内部 api (也就是 `myextension_Init` 之类的功能) - myextension.mm -- 如果需要调用 iOS/macOS 原生功能. 就要为 iOS/macOS 实现 `myextension_Init` 之类的功能 - myextension_android.cpp -- 如果需要调用 Android 的JNI. 就要为 Android 实现 `myextension_Init` 之类的功能 + myextension.cpp -- 扩展的 Lua api 和扩展生命周期函数 + 还包含你的 Lua api 函数的通用实现。 + myextension_private.h -- 每个平台将实现的内部 api(即 `myextension_Init` 等) + myextension.mm -- 如果 iOS/macOS 需要原生调用。为 iOS/macOS 实现 `myextension_Init` 等 + myextension_android.cpp -- 如果 Android 需要 JNI 调用。为 Android 实现 `myextension_Init` 等 /java - / -- Android 需要的java文件 - /res -- 平台需要的资源文件 + / -- Android 需要的任何 java 文件 + /res -- 平台需要的任何资源 /external - README.md -- 扩展相关编译打包的说明/脚本 - /bundleres -- 需要打包的资源 (参见 game.project 以及 [bundle_resources 设置](/manuals/project-settings/#Project)) + README.md -- 关于如何构建或打包任何外部库的说明/脚本 + /bundleres -- 应该为(参见 game.project 和 [bundle_resources 设置]([physics scale setting](/manuals/project-settings/#project)))捆绑的资源 / game.project - game.appmanifest -- 其他应用设置 + game.appmanifest -- 任何额外的应用配置信息 -注意 `myextension.mm` 和 `myextension_android.cpp` 只在调用平台特定原生功能时使用. +注意,`myextension.mm` 和 `myextension_android.cpp` 只有在为该平台进行特定的原生调用时才需要。 #### 平台文件夹 -在某些地方, 需要针对架构平台命名文件夹, 以便应用编译/打包时使用正确的文件. -结构是这样的: +在某些地方,平台架构被用作文件夹名称,以了解在编译/捆绑应用程序时使用哪些文件。这些形式如下: - -目前支持的有: +当前列表是: arm64-ios, armv7-ios, x86_64-ios, arm64-android, armv7-android, x86_64-linux, x86_64-osx, x86_64-win32, x86-win32 -比如, 这么放置平台相关库: +因此,例如,将平台特定的库放在: /libs /arm64-ios diff --git a/docs/zh/manuals/extensions-build-variants.md b/docs/zh/manuals/extensions-build-variants.md index 4384e930..281121e1 100644 --- a/docs/zh/manuals/extensions-build-variants.md +++ b/docs/zh/manuals/extensions-build-variants.md @@ -1,29 +1,29 @@ --- title: 原生扩展 - Build variants -brief: 本教程介绍了 Defold 能创建的各种 Build variants 以及它们如何与原生扩展和引擎交互. +brief: 本手册介绍了 Defold 能创建的各种 Build variants 以及它们如何与原生扩展和引擎交互。 --- :[Build Variants](../shared/build-variants.md) ## App Manifest -不但可以为引擎加入原生扩展功能, 还可以从引擎中剔除一些部分. 比如你不需要物理引擎, 就可以从应用中去除. 关于如何去除引擎功能参见 [应用清单教程](/manuals/app-manifest). +不但可以为引擎加入原生扩展功能,还可以从引擎中剔除一些部分。比如你不需要物理引擎,就可以从应用中去除。关于如何去除引擎功能参见[应用清单手册](/manuals/app-manifest)。 ### 上下文组合 -实际上 app manifest 有着与 extension manifest 相同的结构和语法. 这使我们能够在最终编译时为每个平台混合上下文配置. +实际上 app manifest 有着与 extension manifest 相同的结构和语法。这使我们能够在最终编译时为每个平台混合上下文配置。 -而且, Defold 自身, 有其基础 build manifest (`build.yml`). 编译每个扩展时, 这些文件做如下混合: +而且,Defold 自身,有其基础 build manifest(`build.yml`)。编译每个扩展时,这些文件做如下混合: manifest = merge(game.appmanifest, ext.manifest, build.yml) -用户可以覆盖引擎和每个扩展的默认行为. 而且, 对于最终链接阶段, 我们混合了 app manifest 与 defold manifest: +用户可以覆盖引擎和每个扩展的默认行为。而且,对于最终链接阶段,我们混合了 app manifest 与 defold manifest: manifest = merge(game.appmanifest, build.yml) ### 语法 -这是一个参考示例: +这是一个参考示例: ```yml platforms: @@ -88,15 +88,15 @@ platforms: #### 白名单 -对于所有关键字, 我们提供白名单过滤. 这样可以避免非法路径处理和访问编译上载文件夹之外的文件. +对于所有关键字,我们提供白名单过滤。这样可以避免非法路径处理和访问编译上载文件夹之外的文件。 #### linkFlags -可以在这里添加指定平台的编译标志. +可以在这里添加指定平台的编译标志。 #### libs -此标志仅在需要添加平台或者 Defold SDK 里的库时使用. 应用的扩展库是自动添加的, 不应在这里添加. 下面是从引擎中剔除 3D 物理的例子: +此标志仅在需要添加平台或者 Defold SDK 里的库时使用。应用的扩展库是自动添加的,不应在这里添加。下面是从引擎中剔除 3D 物理的例子: x86_64-linux: context: @@ -107,7 +107,7 @@ platforms: #### Exclude flags -此标志用于剔除平台上下文已经预先定义的东西. 下面是从引擎中剔除 Facebook 扩展的例子 (注意 `(.*)` 是帮助去掉正确元素而使用的正则表达式). +此标志用于剔除平台上下文已经预先定义的东西。下面是从引擎中剔除 Facebook 扩展的例子(注意 `(.*)` 是帮助去掉正确元素而使用的正则表达式)。 armv7-android: context: @@ -117,8 +117,8 @@ platforms: libs: [] linkFlags: [] -#### 所有 flags, libraries, symbols 在哪? +#### 所有 flags、libraries、symbols 在哪? -与其在这里列举我们不然努力把 manifest 的编辑功能加入编辑器, 让用户使用更方便. +与其在这里列举,我们不如努力把 manifest 的编辑功能加入编辑器,让用户使用更方便。 -与此同时, [Manifestation](https://britzl.github.io/manifestation/) 工具也会持续更新. +与此同时,[Manifestation](https://britzl.github.io/manifestation/) 工具也会持续更新。 diff --git a/docs/zh/manuals/extensions-cocoapods.md b/docs/zh/manuals/extensions-cocoapods.md new file mode 100644 index 00000000..7e2f07f1 --- /dev/null +++ b/docs/zh/manuals/extensions-cocoapods.md @@ -0,0 +1,26 @@ +--- +title: 在 iOS 和 macOS 构建中使用 CocoaPods 依赖 +brief: 本手册介绍了如何在 iOS 和 macOS 构建中使用 CocoaPods 解析依赖。 +--- + +# CocoaPods + +[CocoaPods](https://cocoapods.org/) 是 Swift 和 Objective-C Cocoa 项目的依赖管理器。CocoaPods 通常用于管理和集成 Xcode 项目中的依赖。Defold 在为 iOS 和 macOS 构建时不使用 Xcode,但它仍然使用 Cocoapods 在构建服务器上解析依赖。 + +## 解析依赖 + +原生扩展可以在 `manifests/ios` 和 `manifests/osx` 文件夹中包含 `Podfile` 文件来指定扩展依赖。例如: + +``` +platform :ios '11.0' + +pod 'FirebaseCore', '10.22.0' +pod 'FirebaseInstallations', '10.22.0' +``` + +构建服务器将收集所有扩展中的 `Podfile` 文件,并使用这些文件解析所有依赖,并在构建原生代码时包含它们。 + +示例: + +* [Firebase](https://github.com/defold/extension-firebase/blob/master/firebase/manifests/ios/Podfile) +* [Facebook](https://github.com/defold/extension-facebook/blob/master/facebook/manifests/ios/Podfile) \ No newline at end of file diff --git a/docs/zh/manuals/extensions-debugging-android.md b/docs/zh/manuals/extensions-debugging-android.md index 7b28397e..3bd07461 100644 --- a/docs/zh/manuals/extensions-debugging-android.md +++ b/docs/zh/manuals/extensions-debugging-android.md @@ -1,6 +1,6 @@ --- title: Android调试 -brief: 本教程介绍了如何调试运行在 Android 设备上的应用. +brief: 本手册介绍了如何调试运行在 Android 设备上的构建. --- -教程移动至 [调试 Android 原生代码](/manuals/debugging-native-code-android) \ No newline at end of file +已移至 [调试 Android 原生代码](/manuals/debugging-native-code-android) \ No newline at end of file diff --git a/docs/zh/manuals/extensions-debugging-ios.md b/docs/zh/manuals/extensions-debugging-ios.md index 809c4092..1670cc2c 100644 --- a/docs/zh/manuals/extensions-debugging-ios.md +++ b/docs/zh/manuals/extensions-debugging-ios.md @@ -1,6 +1,6 @@ --- title: 在 iOS/macOS 中调试 -brief: 本教程介绍了如何使用 Xcode 进行调试. +brief: 本手册介绍了如何使用 Xcode 调试构建. --- -本教程已被移至 [调试 iOS 原生代码](/manuals/debugging-native-code-ios). \ No newline at end of file +已移至 [调试 iOS 原生代码](/manuals/debugging-native-code-ios). \ No newline at end of file diff --git a/docs/zh/manuals/extensions-debugging.md b/docs/zh/manuals/extensions-debugging.md index cc515b52..0b1e022b 100644 --- a/docs/zh/manuals/extensions-debugging.md +++ b/docs/zh/manuals/extensions-debugging.md @@ -1,6 +1,6 @@ --- title: 调试原生扩展 -brief: 本教程介绍了一些调试包含原生扩展程序应用的方法. +brief: 本手册介绍了一些调试包含原生扩展的应用的方法. --- -详见 [调试原生代码教程](/manuals/debugging-native-code). +请参阅[调试原生代码手册](/manuals/debugging-native-code). diff --git a/docs/zh/manuals/extensions-defold-sdk.md b/docs/zh/manuals/extensions-defold-sdk.md index e593045f..ac5a628b 100644 --- a/docs/zh/manuals/extensions-defold-sdk.md +++ b/docs/zh/manuals/extensions-defold-sdk.md @@ -1,11 +1,11 @@ --- title: 原生扩展 - Defold SDK -brief: 本教程介绍了创建原生扩展时如何使用 Defold SDK. +brief: 本手册介绍了创建原生扩展时如何使用 Defold SDK. --- # Defold SDK -Defold SDK 包含了应用运行的原生平台底层接口与高层Lua逻辑接口来实现原生扩展的功能. +Defold SDK 包含了声明原生扩展所需的功能,以及与应用运行的原生平台底层接口和创建游戏逻辑的高层Lua层进行交互的功能. ## 用法 @@ -13,7 +13,7 @@ Defold SDK 包含了应用运行的原生平台底层接口与高层Lua逻辑接 #include -可用的SDK功能都在 [API文档](/ref/dmExtension/) 里写明了. SDK包含以下命名空间和功能: +可用的SDK功能和命名空间在我们的 [API参考文档](/ref/overview_cpp) 中有详细说明. Defold SDK头文件作为单独的 `defoldsdk_headers.zip` 压缩包包含在每个Defold [GitHub发布版本](https://github.com/defold/defold/releases)中. 你可以在你选择的编辑器中使用这些头文件进行代码补全. * [Align](/ref/dmAlign/) - 公共宏. 用来保证编译器兼容 * [Array](/ref/dmArray/) - 具有边界检测的模板化数组. @@ -45,5 +45,3 @@ Defold SDK 包含了应用运行的原生平台底层接口与高层Lua逻辑接 * [Shared Library](/ref/sharedlibrary/) - 共享库导入导出功能. * [Sony vector Math Library](../assets/Vector_Math_Library-Overview.pdf) - Sony 矢量计算库 主要为了3D图像和3D, 4D矢量运算, 矩阵运算和四元运算. -如果需要 `dmsdk/sdk.h` 头文件请到 [Defold 官方 Github 库](https://github.com/defold/defold/blob/dev/engine/sdk/src/dmsdk/sdk.h) 查询, 这里有 [各种命名空间的头文件](https://github.com/defold/defold/tree/dev/engine/dlib/src/dmsdk/dlib). - diff --git a/docs/zh/manuals/extensions-details.md b/docs/zh/manuals/extensions-details.md index 247c2f78..63820853 100644 --- a/docs/zh/manuals/extensions-details.md +++ b/docs/zh/manuals/extensions-details.md @@ -1,19 +1,19 @@ --- title: 原生扩展 - 详述 -brief: 本教程介绍了有关编译系统用来编译原生扩展的一些细节. +brief: 本手册介绍了有关编译系统用来编译原生扩展的一些细节. --- -# Defold 编译器 +# Defold 编译系统 -为了让你的扩展整合更加方便, 我们这里列举了一些编译相关的细节. +为了让你的扩展整合更加方便,我们这里列举了一些编译相关的细节。 -在创建 Defold 引擎扩展的时候, 要考虑一些事情. 对于更全面的如何开发跨平台原生代码以及扩展/Lua API的使用, 请参考 [原生扩展 - 最佳实践](/manuals/extensions-best-practices) +在创建 Defold 引擎扩展的时候,要考虑一些事情。对于更全面的如何开发跨平台原生代码以及扩展/Lua API的使用,请参考 [原生扩展 - 最佳实践](/manuals/extensions-best-practices) ## C++ 版本 -在引擎里我们用的都是不会高于C++98的版本. 你在开发扩展时可能使用了更高的版本, 注意高版本可能会引入 ABI 的变化. 这可能导致你无法在引擎或者asset store里使用你的扩展. +在引擎里我们用的都是不会高于C++98的版本。你在开发扩展时可能使用了更高的版本,注意高版本可能会引入 ABI 的变化。这可能导致你无法在引擎或者资源商店里使用你的扩展。 -要记住创建代码库 (比如扩展)时, 最好选择最具兼容性的版本. +要记住创建代码库(比如扩展)时,最好选择最具兼容性的版本。 ## 工具链 @@ -34,22 +34,22 @@ brief: 本教程介绍了有关编译系统用来编译原生扩展的一些细 * Win32: `Microsoft Visual Studio 2022` (`clang 17` on build server) * iOS/macOS: `apple-clang` (`clang 17` on build server) -对于 iOS/macOS, 我们分别使用了 `-miphoneos-version-min=11.0` 和 `-mmacosx-version-min=10.13` 参数. +对于 iOS/macOS,我们分别使用了 `-miphoneos-version-min=11.0` 和 `-mmacosx-version-min=10.13` 参数。 -由于我们不指定 C++ 版本, 所以各个编译器都使用了默认设置. +由于我们不指定 C++ 版本,所以各个编译器都使用了默认设置。 ## Win32 + Clang -近来的版本能够在Windows上使用clang. -这使得我们编译服务器运行更快速, 同时打包更精简. +近来的版本能够在Windows上使用clang。 +这使得我们编译服务器运行更快速,同时打包更精简。 ## 静态链接 -自定义引擎使用静态链接进行编译. -主要原因时 iOS 版本 < 8 时, app store 不支持运行一个 .ipa 里的多个可执行程序. +自定义引擎使用静态链接进行编译。 +主要原因是 iOS 版本 < 8 时,应用商店不支持运行一个 .ipa 里的多个可执行程序。 ## 没有 C++ Exceptions -在引擎里我们不使用任何C++ Exceptions. -游戏引擎基本用不到, 因为 (大多数) 游戏数据在引擎开发时是未知的. -移除 C++ exceptions 支持能够减小包体提升运行效率. +在引擎里我们不使用任何C++ Exceptions。 +游戏引擎基本用不到,因为(大多数)游戏数据在引擎开发时是未知的。 +移除 C++ exceptions 支持能够减小包体提升运行效率。 diff --git a/docs/zh/manuals/extensions-ext-manifests.md b/docs/zh/manuals/extensions-ext-manifests.md new file mode 100644 index 00000000..be2ec0d0 --- /dev/null +++ b/docs/zh/manuals/extensions-ext-manifests.md @@ -0,0 +1,75 @@ +--- +title: 原生扩展 - 扩展清单 +brief: 本手册描述了扩展清单及其与应用清单和引擎清单的关联方式。 +--- + +# 扩展、应用和引擎清单文件 + +扩展清单是一个配置文件,包含用于构建单个扩展的标志和定义。此配置与应用级配置和Defold引擎本身的基础级配置相结合。 + +## 应用清单 + +应用清单(文件扩展名`.appmanifest`)是关于如何在构建服务器上构建游戏的应用级配置。应用清单允许您移除引擎中不使用的部分。如果您不需要物理引擎,可以从可执行文件中移除它以减小其大小。了解如何排除未使用的功能,请参阅[应用清单手册](/manuals/app-manifest)。 + +## 引擎清单 + +Defold引擎有一个构建清单(`build.yml`),它包含在引擎和Defold SDK的每个版本中。该清单控制使用哪些SDK版本,运行哪些编译器、链接器和其他工具,以及向这些工具传递哪些默认构建和链接标志。该清单可以在GitHub上的share/extender/build_input.yml中找到[在GitHub上](https://github.com/defold/defold/blob/dev/share/extender/build_input.yml)。 + +## 扩展清单 + +另一方面,扩展清单(`ext.manifest`)是专门针对扩展的配置文件。扩展清单控制扩展的源代码如何编译和链接,以及包含哪些额外的库。 + +这三种不同的清单文件都共享相同的语法,以便它们可以合并并完全控制扩展和游戏的构建方式。 + +对于每个构建的扩展,清单按以下方式合并: + + manifest = merge(game.appmanifest, ext.manifest, build.yml) + +这允许用户覆盖引擎的默认行为以及每个扩展的行为。而且,在最终的链接阶段,我们将应用清单与defold清单合并: + + manifest = merge(game.appmanifest, build.yml) + + +### ext.manifest文件 + +除了扩展名称外,清单文件还可以包含特定于平台的编译标志、链接标志、库和框架。如果*ext.manifest*文件不包含"platforms"部分,或者列表中缺少某个平台,您为其打包的平台仍将构建,但不会设置任何额外的标志。 + +以下是一个示例: + +```yaml +name: "AdExtension" + +platforms: + arm64-ios: + context: + frameworks: ["CoreGraphics", "CFNetwork", "GLKit", "CoreMotion", "MessageUI", "MediaPlayer", "StoreKit", "MobileCoreServices", "AdSupport", "AudioToolbox", "AVFoundation", "CoreGraphics", "CoreMedia", "CoreMotion", "CoreTelephony", "CoreVideo", "Foundation", "GLKit", "JavaScriptCore", "MediaPlayer", "MessageUI", "MobileCoreServices", "OpenGLES", "SafariServices", "StoreKit", "SystemConfiguration", "UIKit", "WebKit"] + flags: ["-stdlib=libc++"] + linkFlags: ["-ObjC"] + libs: ["z", "c++", "sqlite3"] + defines: ["MY_DEFINE"] + + armv7-ios: + context: + frameworks: ["CoreGraphics", "CFNetwork", "GLKit", "CoreMotion", "MessageUI", "MediaPlayer", "StoreKit", "MobileCoreServices", "AdSupport", "AudioToolbox", "AVFoundation", "CoreGraphics", "CoreMedia", "CoreMotion", "CoreTelephony", "CoreVideo", "Foundation", "GLKit", "JavaScriptCore", "MediaPlayer", "MessageUI", "MobileCoreServices", "OpenGLES", "SafariServices", "StoreKit", "SystemConfiguration", "UIKit", "WebKit"] + flags: ["-stdlib=libc++"] + linkFlags: ["-ObjC"] + libs: ["z", "c++", "sqlite3"] + defines: ["MY_DEFINE"] +``` + +#### 允许的键 + +特定于平台的编译标志允许的键有: + +* `frameworks` - 构建时要包含的Apple框架(iOS和macOS) +* `weakFrameworks` - 构建时可选包含的Apple框架(iOS和macOS) +* `flags` - 应传递给编译器的标志 +* `linkFlags` - 应传递给链接器的标志 +* `libs` - 链接时要包含的额外库 +* `defines` - 构建时要设置的定义 +* `aaptExtraPackages` - 应生成的额外包名(Android) +* `aaptExcludePackages` - 要排除的包的正则表达式(或确切名称)(Android) +* `aaptExcludeResourceDirs` - 要排除的资源目录的正则表达式(或确切名称)(Android) +* `excludeLibs`, `excludeJars`, `excludeSymbols` - 这些标志用于移除平台上下文中先前定义的内容。 + +对于所有关键字,我们应用白名单过滤器。这是为了避免非法路径处理和访问构建上传文件夹之外的文件。 \ No newline at end of file diff --git a/docs/zh/manuals/extensions-gradle.md b/docs/zh/manuals/extensions-gradle.md new file mode 100644 index 00000000..98fe3c62 --- /dev/null +++ b/docs/zh/manuals/extensions-gradle.md @@ -0,0 +1,30 @@ +--- +title: 在Android构建中使用Gradle依赖 +brief: 本手册解释了如何在Android构建中使用Gradle解析依赖项。 +--- + +# Android的Gradle + +与Android应用程序的典型构建方式不同,Defold在整个构建过程中不使用[Gradle](https://gradle.org/)。相反,Defold在本地构建中直接使用Android命令行工具,如`aapt2`和`bundletool`,仅在构建服务器上解析依赖项时利用Gradle。 + +## 解析依赖项 + +原生扩展可以在`manifests/android`文件夹中包含一个`build.gradle`文件来指定扩展依赖项。示例: + +``` +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.google.firebase:firebase-installations:17.2.0' + implementation 'com.google.android.gms:play-services-base:18.2.0' +} +``` + +构建服务器将收集所有扩展中的`build.gradle`文件,并使用这些文件解析所有依赖项,并在构建原生代码时包含它们。 + +示例: + +* [Firebase](https://github.com/defold/extension-firebase/blob/master/firebase/manifests/android/)build.gradle +* [Facebook](https://github.com/defold/extension-facebook/blob/master/facebook/manifests/android/build.gradle) \ No newline at end of file diff --git a/docs/zh/manuals/extensions-manifest-merge-tool.md b/docs/zh/manuals/extensions-manifest-merge-tool.md index 5d43f98d..3da1211d 100644 --- a/docs/zh/manuals/extensions-manifest-merge-tool.md +++ b/docs/zh/manuals/extensions-manifest-merge-tool.md @@ -1,19 +1,19 @@ --- -title: 原生扩展 - Manifest 混合工具 -brief: 本教程介绍了应用的 manifests 混合是如何工作的 +title: 原生扩展 - 清单合并工具 +brief: 本手册介绍了应用清单的合并工作原理 --- -# Application manifests +# 应用清单 -一些平台上需要提供 manifests 片段 (或称存根) 来为扩展提供支持. -可以是部分 `AndroidManifest.xml`, `Info.plist` 或者 `engine_template.html` +一些平台上需要提供清单片段(或称存根)来为扩展提供支持。 +可以是部分 `AndroidManifest.xml`、`Info.plist` 或者 `engine_template.html`。 -从应用基础 manifest 开始, 每个扩展 manifest 存根一个一个的被应用. -基础 manifest 可以是默认的 (位于 `builtins\manifests\\...`), 也可以是由用户自定义的. +从应用基础清单开始,每个扩展清单存根依次被应用。 +基础清单可以是默认的(位于 `builtins\manifests\\...`),也可以是由用户自定义的。 ## 命名和结构 -扩展 manifests 必须被放置在适当的指定位置才能生效. +扩展清单必须被放置在适当的指定位置才能生效。 /myextension ext.manifest @@ -30,12 +30,12 @@ brief: 本教程介绍了应用的 manifests 混合是如何工作的 ## Android -Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.jar` 中使用此工具混合 Manifest. -关于 Android manifests 的详细信息, 参见 [官方文档](https://developer.android.com/studio/build/manifest-merge) +Android 平台提供了清单合并工具(基于 `ManifestMerger2`),`bob.jar` 中使用此工具合并清单。 +关于 Android 清单的详细信息,请参见[官方文档](https://developer.android.com/studio/build/manifest-merge) -::: sidenote -如果扩展 manifest 中没有设置应用的 `android:targetSdkVersion` , 下列权限会被自动加入: `WRITE_EXTERNAL_STORAGE`, `READ_PHONE_STATE`, `READ_EXTERNAL_STORAGE`. 详情请见 [此文档](https://developer.android.com/studio/build/manifest-merge#implicit_system_permissions). -我们推荐这么设置: `` +::: important +如果扩展清单中没有设置应用的 `android:targetSdkVersion`,下列权限会被自动加入:`WRITE_EXTERNAL_STORAGE`、`READ_PHONE_STATE`、`READ_EXTERNAL_STORAGE`。详情请见[此文档](https://developer.android.com/studio/build/manifest-merge#implicit_system_permissions)。 +我们推荐这样设置:`` ::: ### 示例 @@ -74,7 +74,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j ``` -混合结果 +合并结果 ```xml @@ -107,7 +107,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j ## iOS / macOS -对于 `Info.plist` 我们实现了专用的工具混合列表和字典. 可以在键上指定混合属性 `merge`, `keep` 或 `replace`, 默认是 `merge`. +对于 `Info.plist`,我们使用自己的实现来合并列表和字典。可以在键上指定合并标记属性 `merge`、`keep` 或 `replace`,默认是 `merge`。 ### 示例 @@ -135,11 +135,11 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j REAL 8.0 - + BASE64 SEVMTE8gV09STEQ= - + Array1 @@ -150,7 +150,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j - + Array2 @@ -187,7 +187,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j INT 42 - + REAL 16.0 @@ -217,13 +217,13 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j ``` -混合结果: +合并结果: ```xml - + NSAppTransportSecurity @@ -248,19 +248,19 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j INT 8 - + REAL 16.0 - + BASE64 SEVMTE8gV09STEQ= - + INT 42 - + Array1 @@ -272,7 +272,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j - + Array2 @@ -295,8 +295,8 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j ## HTML5 -对于 html 模板, 我们给各部分命名, 以便混合时相匹配 (比如 "engine-start"). -标签属性可以是 `merge` 或者 `keep`. `merge` 是默认值. +对于html模板,我们为每个部分命名,以便能够匹配(例如"engine-start")。 +然后可以指定属性 `merge` 或 `keep`。`merge` 是默认值。 ### 示例 @@ -335,7 +335,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j ``` -混合结果 +合并结果 ```html @@ -345,7 +345,7 @@ Android 平台提供了 manifest 混合工具 (基于 `ManifestMerger2`), `bob.j ``` -`CUSTOM_PARAMETERS` 可包含如下参数: +`CUSTOM_PARAMETERS`可能包含以下字段: ``` 'archive_location_filter': - 包地址过滤. + 将为每个存档路径运行的过滤函数。 'unsupported_webgl_callback': - 如果不支持 WebGL 则需调用的回调函数. + 如果不支持WebGL则调用的函数。 'engine_arguments': - 传入引擎的参数列表 (字符串). + 将传递给引擎的参数列表(字符串)。 'custom_heap_size': - 自定义内存使用的大小. + 指定内存堆大小的字节数。 'disable_context_menu': - 为 true 的话则在canvas上关闭右键上下文弹出菜单. + 如果为true,则在canvas元素上禁用右键单击上下文菜单。 'retry_time': - 文件下载失败重试时间间隔. + 错误后重试文件加载之前的暂停时间(以秒为单位)。 'retry_count': - 文件下载失败充实次数. + 尝试下载文件时我们进行多少次尝试。 'can_not_download_file_callback': - 如果重试次数已满但是仍没有得到文件则需调用的回调函数. + 如果在'retry_count'尝试后无法下载文件则调用的函数。 'resize_window_callback': - 当屏幕 改变大小/改变方向/改变焦点 时调用. + 当发生调整大小/方向更改/焦点事件时调用的函数。 + +'start_success': + 成功加载后调用main之前调用的函数。 + +'update_progress': + 进度更新时调用的函数。参数进度更新为0-100。 ``` -## HTML5 的文件操作 +## HTML5中的文件操作 -HTML5 支持 `sys.save()`, `sys.load()` 和 `io.open()` 之类的文件操作, 但是与其他平台实现方法不同. 基于安全考虑浏览器里运行的 Javascript 无权直接读写本地文件. Emscripten (即 Defold) 使用 [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB) 代替, 它是基于浏览器的持久化存储方案, 基于浏览器的虚拟文件系统. 与其他平台的区别主要是比直接读写文件要慢而且实质上读写的是一个数据库. 浏览器开发者工具通常都提供了 IndexedDB 的读写功能. +HTML5构建支持诸如`sys.save()`、`sys.load()`和`io.open()`之类的文件操作,但这些操作的内部处理方式与其他平台不同。当Javascript在浏览器中运行时,没有真正的文件系统概念,并且出于安全原因阻止了本地文件访问。相反,Emscripten(因此Defold)使用[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB),这是一个用于持久存储数据的浏览器内数据库,在浏览器中创建虚拟文件系统。与其他平台文件系统访问的重要区别在于,写入文件和更改实际存储在数据库之间可能存在轻微延迟。浏览器开发者控制台通常允许您检查IndexedDB的内容。 -## 给 HTML5 游戏传递参数 +## 向HTML5游戏传递参数 -一些情况下我们需要在游戏启动前或者启动时为其提供某些参数. 可能是用户 id, session 令牌或者告诉游戏启动时为当前玩家加载哪一关. 有多种方法实现这样的功能, 下面就列举一些. +有时有必要在游戏启动之前或启动时向游戏提供额外的参数。例如,这可能是用户ID、会话令牌或游戏启动时要加载的关卡。这可以通过多种不同的方式实现,其中一些在此描述。 ### 引擎参数 -用于在引进加载时指定引擎参数. 这些参数在运行时可以使用 `sys.get_config()` 得到. 在 `index.html` 里修改 `extra_params` 对象的 `engine_arguments` 项, 加入键值对以提供参数: +可以在配置和加载引擎时指定额外的引擎参数。这些额外的引擎参数可以在运行时使用`sys.get_config()`检索。要添加键值对,请修改传递给`index.html`中加载的引擎的`extra_params`对象的`engine_arguments`字段: ```