天色天歌天音 - Unity 换装游戏 B站视频
注:视频有声音,请开大音量食用。
游戏预览:
制作起因 「忘不了的记忆,不要忘记的记忆,然而却最终再也找回不了的记忆……」——《9-nine 天色天歌天音》
原本我们应该仿照 UGUI 的官方例子做一个装备栏/背包系统,但是最近笔者被一款游戏的剧情深深打动,不自禁地便为其女主角做了这样一款换装游戏。我认为你们都该玩一下《天色天歌天音》!(虽然结局很烂)
在本游戏里,你不仅可以为她选择可爱的衣服,还能调整她的表情、改变她的姿势,以及更换游戏的场景。为你心目中的她构造出最美的画面吧~
操作指南 点击物品栏中物品即可选中该物品,在装备栏处点击即可将物品放入装备栏。只有当表情、服装、动作齐全时,人物的立绘才会更换。
技术细节 3D UI 3D UI 的构建较为简单。首先,我们创建一个 Canvas 然后在其下再创建一个子 Canvas,并且再之下新建两个 Panel,分别用于“物品栏”和“装备栏”,以及一个额外的 Image 用于之后的拖拽使用:
为两个 Panel 添加 Grid Layout Group 之后,我们就可以向其中添加绑定了 Button 组件的 Image 了,它也就是我们的单个物品栏。
在根据需要改变锚点和位置属性后,我们为两个 Panel 绑定脚本,使之会根据鼠标的移动缓慢改变朝向:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 using System.Collections;using System.Collections.Generic;using UnityEngine;public class FaceWindow : MonoBehaviour { public Vector2 range = new Vector2(5 f, 3 f); Transform panelTransform; Quaternion panelRotate; Vector2 panelFacing = Vector2.zero; void Start ( ) { panelTransform = transform; panelRotate = panelTransform.localRotation; } void Update ( ) { Vector3 mousePosition = Input.mousePosition; float halfWidth = Screen.width * 0.5 f; float halfHeight = Screen.height * 0.5 f; float x = Mathf.Clamp((mousePosition.x - halfWidth) / halfWidth, -1 f, 1 f); float y = Mathf.Clamp((mousePosition.y - halfHeight) / halfHeight, -1 f, 1 f); panelFacing = Vector2.Lerp(panelFacing, new Vector2(x, y), Time.deltaTime * 5 f); panelTransform.localRotation = panelRotate * Quaternion.Euler(-panelFacing.y * range.y, panelFacing.x * range.x, 0 f); } }
至此,3D UI 已经基本构建完毕,接下来我们关心的是如何将 角色以及背景等场景元素 和 UI 分离的问题。这个问题也较简单:我们新建一个 GUI Camera,通过 Culling Mask 区分其和 Main Camera 的渲染内容即可:
物品栏和物品“拖拽” 首先,物品要如何体现呢?很简单,我们之前制作物品栏的时候,每个 Grid 都包含有 Image 和 Button 两个组件,我们将 Image 的 Source Image 设置为我们想要的图片就好了:
至于拖拽就比较麻烦了,还记得我们前面的用于拖拽的 Image 吗,这里它将派上大用场!首先,我们令其跟随鼠标:
1 2 3 Vector3 camera = Camera.main.WorldToScreenPoint(transform.position); Vector3 pos = new Vector3(Input.mousePosition.x + 40 , Input.mousePosition.y - 40 , camera.z); transform.position = Camera.main.ScreenToWorldPoint(pos);
然后,在每次点击物品栏中的物品时,我们将二者的 Image Source 交换,就有了“取出物品”的体验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 if (isEmpty){ if (gsm.GetMouse().GetMouseType() != -1 ) { equipType = gsm.GetMouse().GetMouseType(); GetComponent<Image>().sprite = gsm.GetMouse().present; content = gsm.GetMouse().content; gsm.GetMouse().present = null ; gsm.GetMouse().SetMouseType(-1 ); isEmpty = false ; } } else { if (gsm.GetMouse().GetMouseType() == -1 ) { gsm.GetMouse().present = GetComponent<Image>().sprite; GetComponent<Image>().sprite = null ; gsm.GetMouse().SetMouseType(equipType); gsm.GetMouse().content = content; isEmpty = true ; equipType = -1 ; } else { var temp = gsm.GetMouse().present; gsm.GetMouse().present = GetComponent<Image>().sprite; GetComponent<Image>().sprite = temp; var tempc = gsm.GetMouse().content; gsm.GetMouse().content = content; content = tempc; var tempe = gsm.GetMouse().GetMouseType(); gsm.GetMouse().SetMouseType(equipType); equipType = tempe; } gsm.refresh(); }
将物品放入装备栏也是同理,不再赘述。
如何“换装” 单从 Unity 的角度来说,这是简单的。我们对每个人物部件进行标号,并且预先生成各种情况下的人物立绘,再根据装备栏的情况选择加载即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void refresh ( ) { getChar(); if (equippedItem1s[3 ].isEmpty) return ; GameObject.Find("Background" ).GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("bgimage/" + equippedItem1s[3 ].content); } public void getChar ( ) { for (int i = 0 ; i < 3 ; ++i) { if (equippedItem1s[i].isEmpty) return ; } GameObject.Find("Character" ).GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("figure/" + equippedItem1s[1 ].content + equippedItem1s[2 ].content + equippedItem1s[0 ].content); }
真正的难点在于:如何把图片从游戏中提取出来并且组合成我们想要的形式呢?首先,我们要将数据包中的立绘资源(fgimage.xp3)取出(工具 ),取出来的东西大概是长这个样子:
看上去游戏里的角色被拆分成很多份存起来了……这是游戏为了节约内存空间而设计的。注意到文件夹中的 txt 文件了吗?它们指定了完整立绘的合成规则。借助一些小工具(百度一下就有~)我们就能合成出完整的立绘啦~
Enjoy Your Time with Her!