4-1 複 製 預 製 物 件 在 這 裡 的 重 點 是 你 必 須 了 解 在 一 個 基 礎 階 級 (fundamental level) 裡 的 預 設 物 件 概 念 (concept of Prefabs), 他 們 是 一 個 被 重 複 在 遊 戲 裡 使 用 的 預 設 定 義 的 遊 戲 物 件 (GameObjects) 和 元 件 (Components) 的 組 合 如 果 你 不 清 楚 什 麼 是 預 設 物 件 (Prefab), 我 們 建 議 你 先 查 閱 Prefabs 這 一 頁 的 內 容 預 設 物 件 (Prefab) 在 當 你 想 要 舉 例 說 明 複 雜 的 遊 戲 物 件 (GameObjects) 時 非 常 方 便, 選 擇 例 示 的 預 設 物 件 (Prefabs) 所 創 造 的 遊 戲 物 件 (GameObjects) 都 是 有 編 碼 的, 例 示 預 設 物 件 (Prefabs) 在 可 以 選 擇 的 方 法 上 有 許 多 好 處 : 你 可 以 用 一 行 編 碼 來 例 示 預 設 物 件 (Prefab), 並 且 擁 有 完 整 的 功 能 建 立 相 同 的 遊 戲 物 件 (GameObjects) 平 均 要 用 五 行 編 碼, 也 有 可 能 會 更 多 你 可 以 快 速 而 簡 單 地 在 場 景 (Scene) 和 屬 性 視 窗 (Inspector) 裡 設 定, 測 試, 和 修 改 預 設 物 件 (Prefab) 你 可 以 改 變 被 例 示 的 預 設 物 件 (Prefab) 而 不 必 改 變 例 示 它 的 編 碼 一 個 簡 單 的 火 箭 也 可 以 改 變 成 一 個 超 級 動 力 火 箭, 不 一 定 要 改 變 它 的 編 碼 常 見 方 案 為 了 說 明 預 設 物 件 (Prefab) 的 好 處, 我 們 舉 例 了 一 些 會 遇 到 的 基 本 情 況 : 1. 使 用 一 個 " 區 塊 (brick)" 預 設 物 件 在 不 同 位 置 建 立 數 個 來 創 作 一 面 牆 2. 當 火 箭 飛 行 時 會 例 示 一 個 火 箭 飛 行 預 設 物 件 (flying rocket Prefab) 這 個 預 設 物 件 包 含 了 網 面 模 型 (Mesh), 固 體 (Rigidbody), 碰 撞 器 (Collider), 和 一 個 追 蹤 遊 戲 子 物 件 (child GameObject) 的 粒 子 系 統 (Particle System) 3. 當 一 個 機 器 人 分 解 成 數 塊 碎 片 機 器 人 是 依 照 機 器 人 擊 毀 預 設 物 件 (wrecked robot Prefab) 來 被 摧 毀 和 替 換 的, 這 個 預 設 物 件 會 把 機 器 人 分 成 好 幾 個 部 位 組 合 起 來, 全 部 的 設 定 都 是 依 照 本 身 的 固 體 (Rigidbodies) 和 粒 子 系 統 (Particle Systems), 這 個 技 巧 可 以 讓 你 把 一 個 機 器 人 分 解 成 很 多 塊, 只 需 一 行 編 碼, 和 使 用 一 個 預 設 物 件 (Prefab) 來 替 代 一 個 物 件
建 立 一 面 牆 以 下 說 明 將 會 講 解 比 較 利 用 預 設 物 件 (using a Prefab) 和 從 編 碼 來 創 立 物 件 (creating objects from code) 兩 者 間 的 優 點 首 先, 我 們 用 編 碼 來 建 立 一 面 牆 : function Start () { for (var y = 0; y < 5; y++) { for (var x = 0; x < 5; x++) { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.addcomponent(rigidbody); cube.transform.position = Vector3 (x, y, 0); 利 用 上 面 的 script 我 們 可 以 簡 單 的 儲 存 script 並 拖 拉 到 一 個 空 白 的 遊 戲 物 件 (GameObject) 裡 從 GameObject->Create Empty 來 建 立 一 個 空 白 遊 戲 物 件 如 果 你 執 行 這 些 編 碼, 當 你 進 入 執 行 模 式 (Play Mode) 你 就 會 看 到 整 片 牆 被 建 立 起 來 這 裡 有 2 個 關 於 不 同 單 獨 區 塊 的 功 能 :CreatePrimitive() 和 AddComponent() 現 在 還 不 那 麼 壞, 但 是 每 一 個 區 塊 都 是 沒 有 結 構 的 每 個 想 加 入 給 區 塊 的 動 作, 像 變 更 貼 圖 (texture), 摩 擦 力 (friction), 或 是 固 體 (Rigidbody), 都 要 另 外 增 加 編 碼 如 果 你 建 立 一 個 預 設 物 件 (Prefab) 並 預 先 設 定 好 所 有 設 定, 你 只 要 使 用 一 行 編 碼 來 執 行 建 立 和 設 定 每 個 區 塊 這 讓 你 在 想 要 變 更 時 省 去 維 護 和 修 改 許 多 的 編 碼 的 步 驟 使 用 預 設 物 件, 你 只 要 修 改 和 執 行, 不 需 要 改 變 編 碼.
如 果 你 要 使 用 預 設 物 件 (Prefab) 給 每 個 單 獨 區 塊, 這 些 編 碼 是 你 所 需 要 建 立 的 var cube : Transform; function Start () { for (var y = 0; y < 5; y++) { for (var x = 0; x < 5; x++) { var cube = Instantiate(cube, Vector3 (x, y, 0), Quaternion.identity); 這 不 是 很 完 整 但 是 可 以 用 的 這 裡 例 示 一 個 方 塊 並 且 當 包 含 固 性 主 體 所 有 在 預 設 物 件 (Prefab) 裡 的 定 義 都 可 以 利 用 編 輯 器 (Editor) 來 快 速 修 改 現 在 我 們 只 需 要 在 編 輯 器 (Editor) 裡 建 立 預 設 物 件 (Prefab), 步 驟 如 下 : 1 選 擇 GameObject->Create Other->Cube 2 選 擇 Component->Physics->Rigidbody 3 選 擇 Assets->Create Prefab 4 在 專 案 視 窗 (Project View) 裡, 改 變 你 新 的 預 設 物 件 (Prefab) 的 名 字 為 "Brick" 5 在 專 案 視 窗 (Project View) 裡 拖 拉 你 在 屬 性 視 窗 (Hierarchy) 中 所 建 立 的 正 方 體 到 "Brick" 預 設 物 件 (Prefab) 6 當 預 設 物 件 建 立 完 成 後, 你 能 安 全 地 把 在 屬 性 視 窗 (Hierarchy) 裡 的 正 方 體 刪 除 掉 ( 在 Windows 為 Delete,Mac 為 Command-Backspace) 我 們 建 立 了 自 己 的 區 塊 預 設 物 件 (Brick Prefab), 所 以 現 在 我 們 要 在 腳 本 (script) 裡 把 它 加 入 到 cube 變 數 裡 選 擇 包 含 腳 本 的 空 白 遊 戲 物 件 (empty GameObject), 注 意 看 到 屬 性 視 窗 (Inspector) 中 出 現 的 新 變 數, 把 它 命 名 為 "cube"
這 個 變 數 能 接 受 任 何 遊 戲 物 件 或 預 設 物 件 現 在 從 專 案 視 窗 (Project View) 中 拖 曳 "Brick" 預 設 物 件 (Prefab) 到 屬 性 視 窗 (Inspector) 中 的 cube 變 數 上 按 下 Play 你 就 會 看 到 牆 利 用 預 設 物 件 (Prefab) 來 建 立 這 是 能 在 Unity 中 不 斷 重 複 使 用 的 工 作 模 式 剛 開 始 時 你 會 想 為 什 麼 這 樣 會 比 較 好, 因 為 利 用 腳 本 (script) 來 建 立 正 方 體 的 話 只 需 要 2 行 多 的 編 碼. 但 是 因 為 你 現 在 使 用 了 預 設 物 件 (Prefab), 你 可 以 隨 時 地 調 整 預 設 物 件 想 要 大 量 的 調 整 實 體 (instance)? 只 需 調 整 預 設 物 件 裡 的 硬 性 主 體 (Rigidbody) 想 要 給 所 有 實 體 (instance) 使 用 不 同 的 材 質 (Material)? 只 需 拖 拽 材 質 到 預 設 物 件 就 可 以 想 要 改 變 摩 擦 力? 使 用 不 同 的 物 理 材 質 (Physic Material) 給 預 設 物 件 的 碰 撞 器 (collider) 就 可 以 想 要 增 加 一 個 粒 子 系 統 (Particle System) 給 所 有 的 盒 子 (boxes)? 只 要 加 入 一 個 子 物 件 給 預 設 物 件 就 可 以 Instantiating rockets & explosions 以 下 是 有 關 預 設 物 件 所 適 合 的 方 案 : 1 一 個 火 箭 筒 當 使 用 者 按 下 fire 時 會 例 示 (instantiates) 火 箭 預 設 物 件 這 個 預 設 物 件 包 含 一 個 網 面 模 型 (mesh), 剛 性 主 體 (Rigidbody), 碰 撞 器 (Collider), 和 一 個 追 蹤 遊 戲 子 物 件 (child GameObject) 的 粒 子 系 統
(Particle System) 2 當 火 箭 碰 撞 到 時 會 例 示 爆 炸 預 設 物 件 爆 炸 預 設 物 件 包 含 一 個 粒 子 系 統 (Particle System), 一 個 漸 暗 的 燈 光, 和 一 個 毀 壞 遊 戲 物 件 周 圍 的 腳 本 (script) 它 也 可 以 完 全 的 使 用 編 碼 (code) 方 式 來 建 立 火 箭 遊 戲 物 件, 手 動 增 加 元 件 (Components) 和 調 整 變 數 (properties), 它 比 例 示 (instantiate) 一 個 預 設 物 件 還 簡 單 你 可 以 只 用 一 行 編 碼 來 例 示 火 箭, 不 管 火 箭 預 設 物 件 多 複 雜 例 示 預 設 物 件 後 你 也 能 例 示 物 件 (instantiated object) 來 修 改 它 的 屬 性 ( 例 如 : 你 能 在 火 箭 的 固 性 主 體 (Rigidbody) 上 來 設 定 速 度 ) 除 此 之 外 更 簡 單 的, 你 可 以 後 來 再 來 更 新 火 箭 所 以 如 果 你 要 建 立 一 個 火 箭, 不 必 馬 上 增 加 一 個 粒 子 追 蹤 給 它, 你 可 以 以 後 再 做 當 你 要 增 加 一 個 追 蹤 軌 跡 到 子 遊 戲 物 體 給 預 設 物 件 時, 所 有 你 例 示 (instantiated) 的 火 箭 都 會 有 粒 子 追 蹤 最 後, 你 可 以 很 快 的 在 屬 性 視 窗 (Inspector) 中 調 整 火 箭 預 設 物 件 的 屬 性, 它 更 加 容 易 優 化 你 的 遊 戲 這 個 腳 本 展 示 火 箭 如 何 使 用 Instantiate() 函 數. // Require the rocket to be a rigidbody. 命 令 火 箭 為 固 性 主 體 (rigidbody) // This way we the user can not assign a prefab without rigidbody 這 裡 我 們 不 能 沒 有 分 配 固 性 主 體 給 預 設 物 件 (prefab) var rocket : Rigidbody; var speed = 10.0; function FireRocket () { var rocketclone : Rigidbody = Instantiate(rocket, transform.position, transform.rotation); rocketclone.velocity = transform.forward * speed; // You can also acccess other components / scripts of the clone rocketclone.getcomponent(myrocketscript).dosomething(); // Calls the fire method when holding down ctrl or mouse
當 按 下 ctrl 或 滑 鼠 時 呼 叫 fire 方 法 function Update () { if (Input.GetButtonDown("Fire1")) { FireRocket(); Replacing a character with a ragdoll or wreck 利 用 舊 玩 偶 或 毀 壞 物 來 替 換 腳 色 Let's say you have a fully rigged enemy character and he dies. You could simply play a death animation on the character and disable all scripts that usually handle the enemy logic. You probably have to take care of removing several scripts, adding some custom logic to make sure that no one will continue attacking the dead enemy anymore, and other cleanup tasks. 假 如 你 有 一 個 完 全 作 弊 的 敵 人 角 色 而 且 他 死 亡 了 你 可 以 在 腳 色 上 簡 單 地 播 放 死 亡 動 畫 和 使 敵 人 邏 輯 裡 的 腳 本 (scripts) 無 效 你 可 能 要 依 照 移 走 好 幾 個 腳 本, 增 加 一 些 特 別 的 邏 輯 (custom logic) 來 讓 別 人 無 法 再 攻 擊 死 掉 的 腳 色, 和 一 些 清 理 工 作. A far better approach is to immediately delete the entire character and replace it with an instantiated wrecked prefab. This gives you a lot of flexibility. You could use a different material for the dead character, attach completely different scripts, spawn a Prefab containing the object broken into many pieces to simulate a shattered enemy, or simply instantiate a Prefab containing a version of the character. 有 一 個 更 好 的 方 法 是 馬 上 刪 除 全 部 角 色 並 且 使 用 一 個 例 示 毀 壞 預 設 物 件 (instantiated wrecked prefab) 替 代 它 這 給 他 更 多 靈 活 性 你 可 以 用 一 個 不 同 的 材 質 給 死 亡 角 色, 附 加 在 完 全 不 同 的 腳 本 上, 產 生 一 個 包 含 物 件 被 破 壞 所 產
生 的 許 多 破 片 用 來 假 裝 成 的 虛 弱 的 敵 人 (shattered enemy) 之 預 設 物 件, 或 簡 單 地 例 示 一 個 包 含 角 色 譯 本 的 預 設 物 件 Any of these options can be achieved with a single call to Instantiate(), you just have to hook it up to the right prefab and you're set! 任 何 的 設 定 都 能 單 一 呼 叫 使 用 Instantiate() 來 達 成, 你 只 要 勾 到 正 確 的 預 設 物 件 (prefab) 和 你 的 集 合! The important part to remember is that the wreck which you Instantiate() can be made of completely different objects than the original. For example, if you have an airplane, you would model two versions. One where the plane consists of a single GameObject with Mesh Renderer and scripts for airplane physics. By keeping the model in just one GameObject, your game will run faster since you will be able to make the model with less triangles and since it consists of fewer objects it will render faster than using many small parts. Also while your plane is happily flying around there is no reason to have it in separate parts. 有 一 個 重 要 部 分 是 記 住 你 使 用 的 Instantiate() 可 以 是 和 原 本 的 物 件 完 全 不 同 的 例 如, 如 果 你 有 一 架 飛 機, 你 會 製 作 2 種 不 同 版 本 一 個 是 有 網 面 模 型 輸 出 器 (Mesh Renderer) 的 唯 一 遊 戲 物 件 和 控 制 飛 機 物 理 的 腳 本 保 持 模 型 為 單 個 遊 戲 物 件, 你 的 遊 戲 就 會 因 為 減 少 三 角 面 而 運 行 地 更 快, 還 有 比 起 利 用 許 多 小 部 位 來 輸 出 的 方 式 還 來 的 快 這 樣 你 的 飛 機 就 可 以 飛 的 很 好 也 沒 有 必 要 分 割 成 許 多 部 位. To build a wrecked airplane Prefab, the typical steps are: 要 建 立 一 個 毀 壞 的 飛 機 預 設 物 件, 步 驟 如 下 : 1. Model your airplane with lots of different parts in your favorite modeler 2. Create an empty Scene 3. Drag the model into the empty Scene
4. Add Rigidbodies to all parts, by selecting all the parts and choosing Component->Physics->Rigidbody 5. Add Box Colliders to all parts by selecting all the parts and choosing Component->Physics->Box Collider 6. For an extra special effect, add a smoke-like Particle System as a child GameObject to each of the parts 7. Now you have an airplane with multiple exploded parts, they fall to the ground by physics and will create a Particle trail due to the attached particle system. Hit Play to preview how your model reacts and do any necessary tweaks. 8. Choose Assets->Create Prefab 9. Drag the root GameObject containing all the airplane parts into the Prefab 1 用 許 多 不 同 部 位 來 組 裝 你 喜 歡 的 飛 機 模 型 2 建 立 一 個 空 白 場 景 3 把 拖 曳 到 空 白 場 景 裡 4 增 加 固 性 主 體 () 到 所 有 部 位, 選 取 所 有 部 位 後 選 擇 Component->Physics->Rigidbody 5 增 加 方 盒 碰 撞 器 () 到 所 有 部 分, 選 取 所 有 部 位 後 選 擇 Component->Physics->Box Collider 6 為 了 一 個 額 外 的 特 殊 效 果, 增 加 一 個 煙 霧 粒 子 系 統 作 為 子 遊 戲 物 件 給 每 一 個 部 位 7 現 在 你 有 一 個 有 分 開 許 多 成 部 位 的 飛 機, 它 們 會 依 照 物 理 判 定 墜 落 到 地 面 並 且 藉 由 粒 子 系 統 製 造 粒 子 追 蹤 點 擊 執 行 (Play) 來 預 覽 觀 看 你 的 模 型 運 作 並 做 任 何 必 要 的 修 改 8 選 擇 Assets->Create Prefab 9 拖 曳 根 遊 戲 物 件 (root GameObject) 包 含 所 有 飛 機 部 位 的 到 預 設 物 件 (Prefab) 裡 var wreck : GameObject;
// As an example, we turn the game object into a wreck after 3 seconds automatically 在 這 個 例 子 裡, 我 們 讓 遊 戲 物 件 在 3 秒 後 自 動 消 滅 function Start () { yield WaitForSeconds(3); KillSelf(); // Calls the fire method when holding down ctrl or mouse 當 按 下 ctrl 或 滑 鼠 時 呼 叫 fire 方 法 function KillSelf () { // Instantiate the wreck game object at the same position we are at 例 示 摧 毀 物 件 在 我 們 指 定 的 同 一 位 置 上 var wreckclone = Instantiate(wreck, transform.position, transform.rotation); // Sometimes we need to carry over some variables from this object // to the wreck 有 時 我 們 要 持 續 一 些 變 數 直 到 物 件 被 摧 毀 wreckclone.getcomponent(myscript).somevariable = GetComponent(MyScript).someVariable; // Kill ourselves 殺 死 本 身 Destroy(gameObject); The First Person Shooter tutorial explains how to replace a character with a ragdoll version and also synchronize limbs with the last state of the animation. You can find that tutorial on the Tutorials page. 第 一 人 稱 射 擊 遊 戲 教 學 裡 說 明 了 如 何 用 舊 玩 偶 譯 本 (ragdoll version) 替 代 一 個 角 色 和 同 步 分 解 上 一 步 驟 的 動 畫 你 可 以 在 Tutorials 頁 面 裡 找 到 教 學
配 置 一 串 物 件 在 具 體 樣 式 裡 如 果 你 想 要 設 置 一 串 物 件 為 一 個 方 形 或 圓 形 樣 式 基 本 上 有 以 下 兩 個 方 法 : 1. 完 全 使 用 編 碼 來 建 立 物 件 這 是 很 麻 煩 的! 把 一 個 值 輸 入 到 腳 本 也 很 慢, 不 直 覺 化 而 且 不 值 得 去 自 找 麻 煩 2. 製 作 完 整 地 作 弊 物 件 (fully rigged object), 複 製 他 並 擺 放 很 多 次 到 場 景 裡 面 這 是 很 麻 煩 的, 而 且 很 難 精 準 地 把 物 件 擺 在 格 子 (grid) 裡 所 以 使 用 預 設 物 件 的 Instantiate()! 我 想 你 已 經 想 到 如 何 在 這 些 方 案 裡 有 效 利 用 預 設 物 件, 以 下 這 是 這 個 方 案 所 需 要 的 編 碼 : // Instantiates a prefab in a circle 在 園 型 內 例 示 預 設 物 件 var prefab : GameObject; var numberofobjects = 20; var radius = 5; function Start () { for (var i = 0; i < numberofobjects; i++) { var angle = i * Mathf.PI * 2 / numberofobjects; var pos = Vector3 (Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius; Instantiate(prefab, pos, Quaternion.identity); // Instantiates a prefab in a grid 在 方 形 內 例 示 預 設 物 件 var prefab : GameObject; var gridx = 5; var gridy = 5; var spacing = 2.0;
function Start () { for (var y = 0; y < gridy; y++) { for (var x=0;x<gridx;x++) { var pos = Vector3 (x, 0, y) * spacing; Instantiate(prefab, pos, Quaternion.identity);