unity3d制作RPG游戏系列(4)——UI界面

个人博客文章链接:http://www.huqj.top/article?id=161

游戏中除了3d场景以外,UI界面也是很重要的一部分,例如主角的头像、血条、背包、按钮等。

下面是myrpg中的简单UI界面,使用NGUI插件制作:

 

一、导入NGUI插件

NGUI是一个非官方的GUI插件,但是特别好用,所以一般大家都会选择NGUI来制作界面,导入方式很简单,下载NGUI 的unity包然后拖到项目中即可,因为NGUI包有版本要求,所以这里就不贴资源了,大家可以到网上下载。

倒入之后菜单栏会多一个NGUI的菜单:

 

二、使用NGUI制作血条示例

NGUI有一些预制的组件,例如进度条,这可以方便的实现血条显示功能,首先从 NGUI -> open -> prefeb toolbar 打开预制对象

然后选择一个进度条样式拖拽到Hierarchy中,这样就创建了一个进度条:

这里的hp bar就是血条,它由多个子物体组成,包括背景色、前景色等,可以在Inspector中设置调整进度条的颜色,大小、位置

 

然后注意到进度条中on个有一个UISlider组件,通过这个组件的value值就可以调整进度条的进度显示:

所以在代码中人物血量变化的地方(例如收到攻击、喝药水),重新设置进度条的进度从而实现血条的功能。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

//血条显示

private static UISlider _hPSlider;

 

void Start()

{

    _hPSlider = GameObject.FindGameObjectWithTag("hp").GetComponent();

}

 

/**

* 给主角造成伤害

*/

public static void GotAttack(int hurt)

{

   ......

 

    tmp = Health - hurt;

    Health = tmp > 0 ? tmp : 0;

    //血条显示值变化

    _hPSlider.value = (float) Health / MaxHealth;

}

 

三、按钮制作示例

按钮制作的关键在于绑定点击事件,如果使用u3d自带的ui组件还挺麻烦的,但是NGUI就很简单了。

首先创建一个sprite组件,这个组件其实就可以理解为一个图片,然后选择图集里的一张图片绑定在sprite上,然后在父sprite目录下还可以创建子sprite,同样可以绑定一张图片,这样就可以实现按钮的背景和前景图片了。

这里我制作的是加血的按钮,所以还有一个数字label用来显示还剩多少血药,效果如下:

为了实现按钮的效果还需要给sprite绑定一个按钮点击组件,方法是选中sprite之后,点击NGUI->Attach->Collider,这样添加了一个碰撞检测,然后再点击NGUI->Attach->Button Script这样就绑定了一个点击脚本,会发现这时候多出了一个组件可以绑定一个点击函数:

然后写一个点击事件的脚本,创建一个public的非静态方法:

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

/**

 * 使用血药加血(按钮效果)

 */

public void UseHP()

{

    UseHP_s();

}

 

//用于快捷键

public void UseHP_s()

{

    if (HPNum > 0)

    {

        _hero.DrinkPotion();

        HPNum--;

        Health += HPRecoverNum;

        if (Health > MaxHealth)

        {

            Health = MaxHealth;

        }

 

        _hPSlider.value = (float) Health / MaxHealth;

        _hPNumLabel.text = HPNum.ToString();

    }

}

然后将这个脚本绑定到任何一个游戏对象中,这里我绑定的是camera,然后将camera拖拽到上面的button script中即可。

 

四、背包制作

背包是RPG游戏中不可或缺的一部分,制作背包的过程其实也是使用Sprite的过程,就我这里的背包来说,包含以下几个部分:

背景、关闭按钮、物品格子、物品图标

所以首先创建一个sprite并将背包背景图片绑定在上面,然后创建若干子sprite作为格子,并将格子图片绑定在上面,同理还有关闭按钮。最后如果格子里有物品还需要将物品作为一个sprite并且赋给格子的自物体。结构如下:

然后通过创建一个背包按钮来控制背包的显示和关闭即可。背包中的物品图标应当事先做做成预制体,当游戏逻辑中剪刀物品放入背包后则在格子相应的sprite下面添加一个该预制体。

向背包中添加物品的方法如下:

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

41

42

43

44

45

private bool AddItemToBag(string name)

{

    //血药和体力值不往背包放

    if (name.Equals(GoodsManager.HP_POTIONS_NAME))

    {

        HeroStatus.AddHPMedicine();

        return true;

    }

 

    if (name.Equals(GoodsManager.EP_POTIONS_NAME))

    {

        HeroStatus.AddEPMedicine();

        return true;

    }

 

    if (goodsNum >= cells.Length)

    {

        return false//背包已满

    }

 

    if (GoodsManager.Name2Sprite.ContainsKey(name))

    {

        //之前这些是放在场景中置为不可见的,需要复制一份出来

        _tmpGoods = Instantiate(GoodsManager.Name2Sprite[name]);

        _tmpGoods.SetActive(true);

        _tmpGoods.name = name;

        cells[goodsNum].AddChild(_tmpGoods);

        Destroy(_tmpGoods);

        goodsNum++;

        if (goods.ContainsKey(name))

        {

            _tmpNum = goods[name] + 1;

            goods.Remove(name);

        }

        else

        {

            _tmpNum = 1;

        }

 

        goods.Add(name, _tmpNum);

    }

 

 

    return true;

}

这里因为一些原因我没有将物品图标sprite作为预制体而是直接放在场景中置为不可见,当需要添加的时候复制一份并置为可见即可。而寻找对应

 

四、背包物品悬浮显示说明以及拖拽丢弃

背包中的物品需要在鼠标悬浮的时候显示物品说明,以及当背包满了的时候可以拖拽物品丢到地面。实现起来也比较简单,首先要给物品sprite添加一个UIEventListener组件,添加方法就直接点击Inspector中的Add Compenet即可。

这个组件可以通过委托的方式实现各种事件。添加组件之后给物品绑定一个c#脚本,脚本中指定悬浮、拖拽等方法。

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

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

public class GoodsUsage : MonoBehaviour

{

    private UIEventListener _listener;

 

    public Texture2D UsageBackground;

 

    private string _desc;

 

    private Vector2 _size;

 

    private Vector2 _mousePosition;

 

    private bool _shouldDraw;

 

    private Hero _hero;

 

    //拖拽时记录物品原来的位置

    private Vector2 _originPos;

 

    // Use this for initialization

    void Start()

    {

        _listener = UIEventListener.Get(gameObject);

        _listener.onHover += OnHoverGoods;

        _listener.onDrag += OnDrag;

        _listener.onDragEnd += OnDragEnd;

        _listener.onDragStart += OnDragStart;

        _desc = GoodsManager.Name2Desc[gameObject.name];

        _hero = GameObject.FindGameObjectWithTag("hero").GetComponent();

    }

 

    // Update is called once per frame

    void Update()

    {

    }

 

    //悬浮显示物品用途

    private void OnHoverGoods(GameObject obj, bool isOver)

    {

        if (isOver) //进入悬浮

        {

            StartCoroutine(ShowUsageWait());

            _shouldDraw = true;

        }

        else //退出悬浮

        {

            _shouldDraw = false;

        }

    }

 

    //拖拽

    private void OnDragStart(GameObject obj)

    {

        if (!obj.transform.parent.gameObject.tag.Equals("cell"))

        {

            return;

        }

 

        _originPos = transform.localPosition;

    }

 

    private void OnDrag(GameObject obj, Vector2 delta)

    {

        if (!obj.transform.parent.gameObject.tag.Equals("cell"))

        {

            return;

        }

 

        _shouldDraw = false;

        transform.localPosition = new Vector3(transform.localPosition.x + delta.x,

            transform.localPosition.y + delta.y, transform.localPosition.z);

    }

 

    private void OnDragEnd(GameObject obj)

    {

        if (!obj.transform.parent.gameObject.tag.Equals("cell"))

        {

            return;

        }

 

        //拖拽到地面

        if (Input.mousePosition.x < Screen.width - 430 || Input.mousePosition.y < Screen.height - 500)

        {

            Destroy(obj);

 

            _hero.ThrowGoods(gameObject.name, obj.transform.parent.gameObject);

        }

        else //回到格子

        {

            transform.localPosition = new Vector3(_originPos.x, _originPos.y, transform.localPosition.z);

        }

    }

 

    IEnumerator ShowUsageWait()

    {

        yield return new WaitForSeconds(1f);

    }

 

    private void OnGUI()

    {

        if (_shouldDraw)

        {

            _size = GUI.skin.label.CalcSize(new GUIContent(_desc));

            _mousePosition = Input.mousePosition;

            GUI.DrawTexture(new Rect(_mousePosition.x - _size.x - 10, Screen.height - _mousePosition.y,

                _size.x + 10, _size.y + 10), UsageBackground);

            GUI.color = Color.white;

            GUI.Label(new Rect(_mousePosition.x - 5 - _size.x, Screen.height - _mousePosition.y + 5,

                _size.x, _size.y), _desc);

        }

    }

}

这里,对于悬浮显示物品使用说明是通过OnGUI方法绘制的,在悬浮事件中设置一个是否绘制GUI的标识,然后在OnGUI方法中判断是否需要绘制说明,并且还需呀实现说明位置跟随鼠标移动,这里通过Input.mousePosition获得鼠标的位置信息。

对于拖拽事件,实际上有三个委托:OnDragStart, OnDrag, 和OnDragEnd。分别对应点击开始拖拽事件、拖拽中事件、和鼠标释放结束拖拽事件,分别需要实现:记录图片初始位置(以便为拖拽出去的时候复位)、让图标跟随鼠标移动实现拖拽效果、和判断是否将物品拖拽出去了的功能。

当然拖拽丢弃物品之后还需要在地图上生成一个3d物品,这个就不属于GUI应该做的逻辑了。

 

鼠标悬浮显示说明:

 

拖拽丢弃:

你可能感兴趣的:(unity3d)