需求说明
目标:实现玩家移动过程中,拾取物体,物体被放入仓库;点击仓库中物体,重新扔回3D场景中逻辑。
逻辑分析:
- 需要玩家可以移动;
- 需要检测玩家和物体的碰撞,并摧毁物体;
- 需要识别物体的类别;
- 新物体直接新建,已有的物体增加数量;
- 需要记录仓库的物体详情列表,并响应列表的变化;
- 需要仓库的UI界面,点击物体可以生成物体到3D中;
- 需要提供物体的素材,可以动态的增删和展示物体。
成果展示
Scene部分
玩家移动
需要绑定移动相关的脚本
碰撞检测
两个物体能够检测到碰撞,需要至少一个物体绑定刚体组件,需要绑定碰撞体组件,碰撞体组件需要勾选 isTrigger 属性。
被绑定刚体组件的物体,需要取消勾选 use gravity 重力属性,才能阻止物体自然下落(不在平面上的情况)。
识别物体
将需要放进仓库中的物体,做成 profab 预制体。
定义不同的 tag 区分物体类别。
UI仓库栏
建议做成滚动界面【点击查看相关文章】,应对仓库物品多的情况。
仓库栏中展示物体使用 Image 组件,不同的物品对应不同的图片。
制作单个物体UI模板,当有物品增删到仓库时,只需要实例化该模板,修改其中的物体图片和数量即可。
动态素材
新建空物体,绑定素材资源输入的脚本
脚本部分
类的设计
物体类
包括可以放入仓库的物体信息,物体名称、物品个数、物体对应的Profab、物体对应的UI图片等。
public class Goods
{
private GoodsName goodsName;
private int num;
public Goods(GoodsName goodsName)
{
this.goodsName = goodsName;
num = 1;
}
public void AddNum()
{
num++;
}
public void DecNum()
{
num--;
}
public int GetNum()
{
return num;
}
public GoodsName GetGoodsName()
{
return goodsName;
}
public Sprite GetSprite()
{
switch (goodsName)
{
case GoodsName.Cube:
return GameAsset.instance.Cube;
case GoodsName.Cylinder:
return GameAsset.instance.Cylinder;
default:
return null;
}
}
public Transform GetProfab()
{
switch (goodsName)
{
case GoodsName.Cube:
return GameAsset.instance.CubeTransform;
case GoodsName.Cylinder:
return GameAsset.instance.CylinderTransform;
default:
return null;
}
}
}
仓库类
管理仓库,包括物品列表、增删物品等。
public class Inventory
{
//列表发生变化时触发
public event EventHandler OnItemListChanged;
private List<Goods> goodsList;
private Action<Goods> useGoodsAction;
public Inventory(List<Goods> goodsList, Action<Goods> useGoodsAction)
{
this.goodsList = goodsList;
this.useGoodsAction = useGoodsAction;
}
//增加时,新物体加入列表,已有的物体改变数量
public void AddGoods(GoodsName goodsName)
{
Goods target = goodsList.Find(_ => _.GetGoodsName() == goodsName);
if (target != null)
{
target.AddNum();
}
else
{
Goods goods = new Goods(goodsName);
goodsList.Add(goods);
}
OnItemListChanged?.Invoke(this, EventArgs.Empty);
}
//删除时,数量为1的物体从列表移除,否则只改变数量
public void DeleteGoods(GoodsName goodsName)
{
Goods target = goodsList.Find(_ => _.GetGoodsName() == goodsName);
if (target != null)
{
if (target.GetNum() == 1)
{
goodsList.Remove(target);
}
else
{
target.DecNum();
}
}
OnItemListChanged?.Invoke(this, EventArgs.Empty);
}
public List<Goods> GetGoodsList() {
return goodsList;
}
//需要使用仓库中的物品
public void UseGoods(Goods goods) {
useGoodsAction(goods);
}
}
素材脚本
提供不同物体的素材
public class GameAsset : MonoBehaviour
{
public static GameAsset instance;
private void Awake()
{
instance = this;
}
public Sprite Cube;
public Sprite Cylinder;
public Transform CubeTransform;
public Transform CylinderTransform;
}
玩家脚本
键盘按键控制玩家移动;
检测与物体的碰撞;
实例化仓库类;
建立仓库UI和仓库类的关系;
public class Player : MonoBehaviour
{
[SerializeField] private float moveSpeed = 20f;
private void Update()
{
//玩家的移动
Vector3 inputDir = new Vector3(0, 0, 0);
if (Input.GetKey(KeyCode.UpArrow)) inputDir.z += +1f;
if (Input.GetKey(KeyCode.DownArrow)) inputDir.z += -1f;
if (Input.GetKey(KeyCode.LeftArrow)) inputDir.x += -1f;
if (Input.GetKey(KeyCode.RightArrow)) inputDir.x += +1f;
Vector3 moveDir = transform.forward * inputDir.z + transform.right * inputDir.x;
transform.position += moveDir * moveSpeed * Time.deltaTime;
}
Inventory inventory;
[SerializeField] UI_Inventory ui_inventory;
private void Awake()
{
inventory = new Inventory(new List<Goods>(), (goods) =>
{
//使用物体时,删除仓库中的物体,生成新的物体预制体,并随机扔在3D场景中。
inventory.DeleteGoods(goods.GetGoodsName());
Instantiate(goods.GetProfab(), new Vector3(UnityEngine.Random.Range(-20, 20), 0, UnityEngine.Random.Range(-20, 20)), new Quaternion());
});
//初始化UI
ui_inventory.Init(inventory);
}
void OnTriggerEnter(Collider c)
{
//可以放进仓库的物体,增加到仓库,同时摧毁3d场景中的物体
if (Enum.TryParse(c.gameObject.tag, true, out GoodsName goodsName))
{
inventory.AddGoods(goodsName);
Destroy(c.gameObject);
}
}
}
UI脚本
随着仓库物品的列表变化而更新;
点击UI, 可以使用物品;
public class UI_Inventory : MonoBehaviour
{
Transform itemTemplate;
List<Transform> itemTransformList;
Inventory inventory;
public void Init(Inventory inventory)
{
this.inventory = inventory;
inventory.OnItemListChanged += Inventory_OnItemListChanged;
//记录物体UI模板
itemTemplate = transform.Find("itemTemplate");
itemTransformList = new List<Transform>();
}
//事件: 列表变化,刷新UI
private void Inventory_OnItemListChanged(object sender, System.EventArgs e)
{
UpdateItemsList();
}
//列表变化,刷新UI
private void UpdateItemsList()
{
//先删除所有UI的物体
itemTransformList.ForEach(_ =>
{
Destroy(_.gameObject);
});
itemTransformList.Clear();
//重新加载仓库类中的物品列表
inventory.GetGoodsList().ForEach(goods =>
{
Transform itemTemplateTransform = Instantiate(itemTemplate, transform);
itemTemplateTransform.gameObject.SetActive(true);
//输入对应物品图片和数量
itemTemplateTransform.Find("Image").GetComponent<Image>().sprite = goods.GetSprite();
itemTemplateTransform.Find("text").GetComponent<TextMeshProUGUI>().SetText(goods.GetNum().ToString());
itemTransformList.Add(itemTemplateTransform);
//点击物品,触发事件
itemTemplateTransform.GetComponent<Button_UI>().ClickFunc = () => {
inventory.UseGoods(goods);
};
});
}
}