Get Started with Unity3D - 6

天色天歌天音 - 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(5f, 3f);

Transform panelTransform;
Quaternion panelRotate;
Vector2 panelFacing = Vector2.zero;

// Use this for initialization
void Start () {
panelTransform = transform;
panelRotate = panelTransform.localRotation;
}

// Update is called once per frame
void Update () {
Vector3 mousePosition = Input.mousePosition;

float halfWidth = Screen.width * 0.5f;
float halfHeight = Screen.height * 0.5f;
float x = Mathf.Clamp((mousePosition.x - halfWidth) / halfWidth, -1f, 1f);
float y = Mathf.Clamp((mousePosition.y - halfHeight) / halfHeight, -1f, 1f);
panelFacing = Vector2.Lerp(panelFacing, new Vector2(x, y), Time.deltaTime * 5f);

panelTransform.localRotation = panelRotate * Quaternion.Euler(-panelFacing.y * range.y, panelFacing.x * range.x, 0f);
}
}

至此,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!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×