[Unity]UGUI基于MVC模式的技能装备系统

效果预览

[Unity]UGUI基于MVC模式的技能装备系统

项目分享

链接:https://pan.baidu.com/s/1azI-EPCn4zkBHwt850mstg 
提取码:oem8 

顺便分享一个网站 爱给网 ,里面大多数资源都可以免费下载

什么是MVC

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
说白了就是把模型、视图、控制分开,得到较高的可维护性和可复用性

如何应用到Unity当中

https://blog.csdn.net/yupu56/article/details/53728489
这位博主提到
”底部的控制、逻辑用什么实现好呢?方式很多,至少MVC框架模式是一个选择。而在使用MVC时,我个人认为模块内小规模使用MVC更合理,模块内的数据、控制、界面关系比较紧密,模块之间提供合理的接口进行跳转即可(不排除模块间消息沟通)。“
游戏是一个复杂的系统,局部MVC使用是非常好的选择,比如UI、AI、主角状态机,可能有时应用起来会走样,但是无论是MVC还是其他框架,目的都是分离代码逻辑,提高可维护性,目的达到即可。

项目分析

项目结构
  • ItemModel 属于Model层,定义了技能的结构,存储技能
  • ItemView 属于View层,控制技能面板的显示效果
  • MoveWithMouse 属于View层,拖拽时让图标随着鼠标移动
  • ItemControl 属于Controller层,实现了技能的交换
  • DragEvent 属于Controller层,触发拖拽操作,调用ItemControl

以上是技能界面涉及到的所有脚本及其功能

  • AbilityView 属于View层,主界面技能栏的显示效果
  • OpenCloseBox 属于控制层,打开关闭技能界面以及刷新主界面的技能栏(但并没有对数据进行更改,只是用来控制显示层)

这两个脚本是用来显示刷新主界面技能栏与打开关闭技能界面用的(数据来自于ItemModel,MVC的优点已经有些许体现)

核心思路

给每一个格子一个ID,用来判断技能交换
交换核心函数

 public static void SwapItem(int gridID)
    {
        ItemModel.Item temp = pickedItem;
        if (gridID >= ItemView.row * ItemView.col)
        {
            pickedItem = ItemModel.ability[gridID - ItemView.row * ItemView.col];
            ItemModel.ability[gridID - ItemView.row * ItemView.col] = temp;
        }
        else
        {
            pickedItem = ItemModel.items[gridID];
            ItemModel.items[gridID] = temp;
        }
      
        GameObject.Find("Bag").GetComponent<ItemView>().ShowItems();
    }

通过接口IBeginDragHandler,IDragHandler,IEndDragHandler,添加拖拽函数
在拖拽开始,拖拽中,拖拽结束时调用三次SwapItem

  1. 第一次时,把物品放在pickedItem中,把原所在格子置为空
  2. 第二次,交换目标格子与pickedItem的物品
  3. 第三次,交换物品原所在格子与pickedItem中的物品(三次交换完成后,pickedItem仍然为null,完成了两个格子中技能的交换)
注意点

在拖拽时,被拖拽的物体跟随鼠标移动,被遮挡的对象就接受不到事件了(无法**Drag相关的三个事件),所以我们需要添加UI事件穿透
引用雨凇MOMO大佬的帖子 http://www.xuanyusong.com/archives/3480

using UnityEngine;
using System.Collections;
 
public class UIFocus : MonoBehaviour ,ICanvasRaycastFilter
{
	public bool IsFocus= false;
	public bool IsRaycastLocationValid (Vector2 sp, Camera eventCamera)
	{
		return IsFocus;
	}
}

代码部分

这里只有技能界面涉及到的脚本,完整项目在文章开头提供了下载

  • Model层
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemModel : MonoBehaviour
{
    public class Item
    {
        public string name;
        public Sprite img;

        public Item(string name,Sprite img)
        {
            this.name = name;
            this.img = img;
        }
    }

    public static List<Item> items;
    public static List<Item> ability;

    public int size;//物品总数量,实际应在数据库中检索
    Sprite[] sprites;

    private void Awake()//初始化数据,将精灵引用到items
    {
        items = new List<Item>();
        ability = new List<Item>();
        sprites = Resources.LoadAll<Sprite>("ability");//实际与数据库检索结果有关

        for (int i = 0; i < ItemView.row; i++)
        {
            for(int j = 0; j < ItemView.col; j++)
            {
                items.Add(new Item("", null));
            }
        }

        for(int i = 0; i < 5; i++)
        {
            ability.Add(new Item("", null));
        }

        for(int i = 0; i < size; i++)
        {
            items[i] = new Item(" ", sprites[i]);
        }
    }
}

  • View层
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ItemView : MonoBehaviour
{
    public static int row=4;
    public static int col=6;

    public GameObject grid;
    public Transform ability;

    float width;
    float height;

    private void Awake()
    {
        width = grid.GetComponent<RectTransform>().rect.width + 10;
        height = grid.GetComponent<RectTransform>().rect.height + 10;
    }

    //初始化,显示所有格子
    private void Start()
    {
        for(int i = 0; i < row; i++)
        {
            for(int j = 0; j < col; j++)
            {
                int id = j + i * col;
                GameObject itemGrid = Instantiate(grid, transform.position + new Vector3(j * width-col*(width-10), -i * height, 0), Quaternion.identity) as GameObject;
                itemGrid.transform.SetParent(transform);
                ShowItem(itemGrid.transform, id);

                itemGrid.GetComponent<ItemControl>().gridID = id;//给格子编号
            }
        }

        for (int i = 0; i < 5; i++)
        {
            int id = row * col+i;
            GameObject itemGrid = Instantiate(grid, ability.position + new Vector3(i * width - 5 * (width - 10), 0, 0), Quaternion.identity) as GameObject;
            itemGrid.transform.SetParent(ability);
            ShowItem(itemGrid.transform, id);

            itemGrid.GetComponent<ItemControl>().gridID = id;
        }
    }

    //刷新所有格子
    public void ShowItems()
    {
        for(int i = 0; i < row * col; i++)
        {
            Transform itemGrid = transform.GetChild(i);
            ShowItem(itemGrid, i);
        }
        for (int i = 0; i < 5; i++)
        {
            Transform itemGrid = ability.GetChild(i);
            ShowItem(itemGrid, row * col + i);
        }
    }

    //显示格子
    private void ShowItem(Transform itemGrid,int id)
    {
        Image imageUGUI = itemGrid.GetChild(0).GetComponent<Image>();
        if (id >= row * col)
        {
            if (ItemModel.ability[id - row * col].img!=null)
            {
                imageUGUI.color = Color.white;
            }
            else
            {
                imageUGUI.color = Color.clear;
            }
            imageUGUI.sprite = ItemModel.ability[id - row * col].img;
            return;
        }
        if (ItemModel.items[id].img != null)
        {
            imageUGUI.color = Color.white;
        }else{
            imageUGUI.color = Color.clear;
        }
        imageUGUI.sprite = ItemModel.items[id].img;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MoveWithMouse : MonoBehaviour, ICanvasRaycastFilter
{//事件穿透的必要性
    RectTransform rect;
    Image icon;
    // Start is called before the first frame update
    void Awake()
    {
        rect = GetComponent<RectTransform>();
        icon = transform.GetChild(0).GetComponent<Image>();
    }

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

        //如果原方框中没有图片,那么就透明显示
        if (ItemControl.pickedItem != null)
        {
            if (ItemControl.pickedItem.img != null)
            {
                icon.color = Color.white;
                icon.sprite = ItemControl.pickedItem.img;
            }
            else
            {
                icon.color = Color.clear;
            }
        }
    }
    //忽略鼠标图标上的射线
    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        return false;
    }
}

  • Controller层
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class DragEvent : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler
{
    int gridID = 0;//格子编号
    public static int lastID;

    void Start()
    {
        gridID = GetComponentInParent<ItemControl>().gridID;
    }

    //三个函数对应三个接口
    public void OnBeginDrag(PointerEventData eventData)
    {
        lastID = gridID;
        Debug.Log(string.Format("拖动物体原所在格子:{0:D2}",lastID));
        ItemControl.SwapItem(gridID);
    }

    public void OnDrag(PointerEventData eventData)
    {
        
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        ItemControl.SwapItem(gridID);
    }

}

using System.Collections;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine;

public class ItemControl : MonoBehaviour,IDropHandler
{
    public int gridID;
    public static ItemModel.Item pickedItem;
    // Start is called before the first frame update
    void Start()
    {
        pickedItem = new ItemModel.Item("", null);
    }

    public static void SwapItem(int gridID)
    {
        //第一次时,把物品放在pickedItem中,把原所在格子置为空
        //第二次,交换目标格子与pickedItem的物品
        //第三次,交换物品原所在格子与pickedItem中的物品(三次交换完成后,pickedItem仍然为null,完成了两个格子中技能的交换)
        ItemModel.Item temp = pickedItem;
        if (gridID >= ItemView.row * ItemView.col)
        {
            pickedItem = ItemModel.ability[gridID - ItemView.row * ItemView.col];
            ItemModel.ability[gridID - ItemView.row * ItemView.col] = temp;
        }
        else
        {
            pickedItem = ItemModel.items[gridID];
            ItemModel.items[gridID] = temp;
        }
      
        GameObject.Find("Bag").GetComponent<ItemView>().ShowItems();
    }

    public void OnDrop(PointerEventData eventData)
    {
        Debug.Log(string.Format("拖动物体目标格子:{0:D2}", gridID));
        if (gridID != DragEvent.lastID)
        {
            SwapItem(gridID);
        }
    }
}