在上一篇中,我们写了背包系统的伪代码,也说了mvc的设计思路,那么这一篇的任务就是将伪代码补全。
首先制作一个背包面板,我这里比较简单,就是一个滚动视图,还有一个提示文本,外加两个按钮,一个是使用物品,一个是增加物品,然后给滚动视图的Content添加上布局组件,这样当我们动态添加物品预制体的时候就会自动布局了。
这里我们将背包的mvc三层脚本都挂载在背包面板的根节点上,方便互相获取调用,因为我们本篇是针对背包的实现进行的,至于mvc这三层脚本如何获取调用,实际上是由UI框架去决定的,包括代码的周期等等,所以我们这里方便测试,直接挂载在面板根节点。
首先,我们的背包是用来存放物品的,因此必须有一个物品类,来体现这个物品所具有的属性。
public class Item
{
public ulong onlyId;
public int spriteId;
}
这里我只记录了onlyId和这个物品对应的图片id,首先onlyId这是一个物品的唯一标识,因为每个物品都是不同的,即使两个物品完全一样,那么他们也是独立出来的两个物品,结合实际就可以想象得知。而图片是为了等下演示方便大家可以看出来是不同物品,这里写的很简陋了,大家可以自行添加,这里只是讲解思路。
然后在看到数据层脚本。
public class BagData : MonoBehaviour
{
BagCtrl ctrl;
Dictionary items; //存储背包中所有物品
ulong onlyId;
private void Awake()
{
onlyId = 0;
ctrl = GetComponent();
items = new Dictionary();
}
public void TestAddItem()
{
onlyId++;
Item newItem = new Item();
newItem.onlyId = onlyId;
newItem.spriteId = Random.Range(1, 6);
items.Add(newItem.onlyId, newItem);
}
public Item[] GetItems()
{
if (items.Count == 0)
return null;
Item[] temp = new Item[items.Count];
items.Values.CopyTo(temp, 0);
//返回背包中所有物品
return temp;
}
public void AddItem()
{
//增加物品
}
public void DelItem(ulong onlyId)
{
if (items.ContainsKey(onlyId))
items.Remove(onlyId);
//删除物品
}
public void UpdateItem()
{
//更新物品
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
首先获取逻辑层的引用,然后是实例化我们的物品字典,根据物品的唯一标识来存储物品信息,然后还写了一个测试添加物品的方法,因为我们游戏当中物品来源有很多种,比如打怪掉落,或者是商城购买等等,我们这里就直接给一个按钮去调用这个方法,然后每次新增物品onlyId都去自增,保证onlyId不重复,然后随机给一张图片,从1-6,因为我只放了五张图片资源。这个方法也很简单了,new一个物品对象然后存储在字典中。
还有删除方法,根据外部传进来的onlyId删除物品,还有一个获取背包当前所有物品的方法,也是很简单,直接返回字典中的所有物品信息。
接着看到逻辑层。
public class BagCtrl : MonoBehaviour
{
BagView view;
BagData data;
private void Awake()
{
view = GetComponent();
data = GetComponent();
}
public Item[] GetItems()
{
if (data.GetItems() == null)
return null;
return data.GetItems();
}
public void AddItem()
{
data.AddItem();
view.UpdateView();
}
public void Add()
{
data.TestAddItem();
view.UpdateView();
}
public void UpdateItem()
{
data.UpdateItem();
}
public void UseItem(ulong id)
{
//使用物品使用效果
//删除该物品或减少该物品数量
data.DelItem(id);
view.UpdateView();
}
public void Discard(ulong id)
{
//删除该物品或减少该物品数量
data.DelItem(id);
view.UpdateView();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
在上一篇我们已经说了,逻辑层是视图层以及数据层的中介,所以要先获取这两者的引用。然后向外提供一个获取所有物品的方法,目的是让视图层获取数据层的物品信息,还有添加物品方法。使用物品丢弃物品,这里我们直接将物品给删了,物品的效果,这个大家自行扩展了,比如你打开的是一个礼包,那么就是调用数据层的添加物品方法,然后再把这个礼包给删了,实际上都是一些简单逻辑。处理完数据,在调用视图层的UpdateView方法,也就是刷新视图层的显示,因为物品已经改变了,这里实际上最好应该先判断一下,如果背包面板没有打开的话,是不需要去刷新的。
最后看到视图层,
public class BagView : MonoBehaviour
{
BagCtrl ctrl;
public Button btn_use;
public Button btn_add;
public Text txt_tips;
public Transform img_content;
ulong current_onlyId;
List items;
private void Awake()
{
ctrl = GetComponent();
items = new List();
}
// Start is called before the first frame update
void Start()
{
current_onlyId = 0;
btn_use.onClick.AddListener(() =>
{
if (current_onlyId == 0)
return;
ctrl.UseItem(current_onlyId);
current_onlyId = 0;
});
btn_add.onClick.AddListener(() =>
{
ctrl.Add();
current_onlyId = 0;
});
UpdateView();
}
public void UpdateView()
{
txt_tips.text = "当前没有选中的物品";
if(items.Count > 0)
{
for(int i = 0; i < items.Count; i++)
{
Destroy(items[i]);
}
}
items.Clear();
if (ctrl.GetItems() == null)
return;
Item[] newItems = ctrl.GetItems();
GameObject itemRes = Resources.Load("Prefab/Item");
for(int i = 0; i < newItems.Length; i++)
{
GameObject newItem = Instantiate(itemRes);
newItem.transform.SetParent(img_content);
items.Add(newItem);
Sprite spr = Resources.Load("Sprite/" + newItems[i].spriteId);
Image img = newItem.transform.GetChild(0).GetComponent();
img.sprite = spr;
ulong temp = newItems[i].onlyId;
newItem.GetComponent().onClick.AddListener(() =>
{
current_onlyId = temp;
txt_tips.text = "当前选中的物品的onlyId是:" + temp;
});
}
}
// Update is called once per frame
void Update()
{
}
}
最上面的几个参数,currentOnlyId,这个是用来记录当前玩家选中的物品的,还有一个items列表,是用来保存当前背包面板下的所有物品游戏对象的也就是GameObject类型的。一开始先将currentOnlyId归零,获取逻辑层的引用。给两个按钮添加监听,当按下使用按钮,则调用逻辑层的使用物品按钮,将当前选中的物品的onlyId传进去,添加物品就直接调用逻辑层的使用物品方法即可,最后调用一下刷新方法。
刷新方法,首先把提示信息重置一下,当前没有选择任何物品,然后将items列表中的所有游戏对象销毁掉,也就是清空操作,然后再从逻辑层获取现有的所有物品重新进行生成,就是获取物品预制体,然后实例化,根据物品信息的图片给物品修改图片,将物品存进items列表,挂载再content物品下自动布局。最后给物品添加监听,当按下物品时修改提示文案和cuurentOnlyId。这样就是一次刷新操作了。
大家可以可以复制代码自行测试下效果,工程我会放在qq群上,大家也可以加群获取。
最后补充一下,本文章讲的是一个mvc实现背包的一个设计思路,比如说上面的销毁,实际上可以放在对象池,等等的一些优化操作,这个视项目而定,因为本文章面向的是还无法自己完成背包系统的同学,所以会尽可能的只针对背包来讲,希望本文章对大家有所帮助。