Unity 3D 实现拾取物品功能(一)
刚刚接触Unity不久,为了记录自己学习的过程。我决定写写博客,哈哈哈。
在网上看了一些拾取物品的实现原理之后,自己摸索了一下,写了个小Demo,下面来介绍一下基本功能。
1)人物靠近物体,且视角朝向物体,改变物体颜色。
2)按E键拾取物品到背包,并销毁游戏对象。
3)按B键在背包中显示已有物品。
一般背包设定分为格子上限和负重上限两种,在这个Demo里我们使用格子上限的模式。且物品分为可叠加和不可叠加两种。
首先,来预览一下成果。
在游戏场景中建立这么一些游戏对象,来模拟游戏里的真实物品。
然后我们操作角色,移动到物体前。然后镜头指向游戏想,把默认颜色修改为红色。
一般游戏都使用E键拾取物品,我也使用E键拾取。拾取成功后,我们就使用Destroy()函数,销毁游戏对象。从而达到物品被加入背包的感觉。
我们把ObjectItem挂到物体上,然后设置属性。
方块1数量为685,可叠加,最大叠加数量为1000。
方块2数量为888,可叠加,最大叠加数量为1000。
相同物品的判断使用ObjId字段,所以同类物品就设置ObjId一致就可以了。
拾取以上两个方块后,按下B键,调用显示物品的函数,暂时显示到Debug控制台中。在后续的博客中再继续完善。
我们就可以看到,物品栏就有了两条记录。然后设置两个圆柱个数都为1,且不可叠加。拾取后按B键,查看控制台
就可以看到柱子的数量分别存在的单独的格子。
把背包和物品属性抽象,形成两个类。Pack.cs和ObjectItem.cs
Pack.cs代码如下:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Pack : MonoBehaviour {
//保存物品的List数组
public List<ItemEntity> items = null;
//背包最大容量
public int maxItem = 32;
// Use this for initialization
void Start () {
items = new List<ItemEntity>();
}
//拾取物品
public ObjectItem getItem(ObjectItem item)
{
//使用中间过渡的实体类
ItemEntity itemEntity = ItemEntity.FillData(item);
//如果物品不可合并
if (!itemEntity.IsCanAdd)
{
//背包有空余
if(items.Count < maxItem)
{
//获得物品
items.Add(itemEntity);
item.count = 0;
} else
{
//拾取失败
}
} else
{
//空背包时,直接添加
if (items.Count < 1)
{
items.Add(itemEntity);
item.count = 0;
} else
{
//判断当前背包合并物品
foreach (ItemEntity currItem in items)
{
//相同物品,且可叠加,且分组未满
if (currItem.ObjId.Equals(itemEntity.ObjId) && currItem.Count < currItem.MaxAdd)
{
//数量叠加
currItem.Count = currItem.Count + itemEntity.Count;
//如果超出叠加范围
if (currItem.Count - currItem.MaxAdd > 0)
{
//拾取物品的剩余数量变更
item.count = currItem.Count - currItem.MaxAdd;
itemEntity.Count = item.count;
//背包里的数量为最大值
currItem.Count = currItem.MaxAdd;
} else
{
// 未超出叠加最大值,拾取成功
item.count = 0;
}
} else
{
//如果物品不是同类,或分组是满的,遍历下一个格子
continue;
}
}
//物品超出分组,有剩余格子则添加。否则剩余部分无法拾取
if(item.count > 0 && items.Count < maxItem)
{
items.Add(itemEntity);
item.count = 0;
}
}
}
return item;
}
//显示物品
public void showPack()
{
string show = "物品:\n";
int i = 0;
foreach (ItemEntity currItem in items)
{
show += ++i + " [" + currItem.ObjName + "], 数量: " + currItem.Count + "\n";
}
Debug.Log(show);
}
}
ObjectItem代码如下
using UnityEngine;
using System.Collections;
public class ObjectItem : MonoBehaviour {
public string objId;
public string objName;
public int count;
public string note;
public int level;
public bool isCanAdd;
public int maxAdd;
public bool isChecked;
// Use this for initialization
void Start () {
isChecked = false;
}
// Update is called once per frame
void Update () {
if (isChecked)
{
GetComponent<MeshRenderer>().material.color = Color.red;
} else
{
GetComponent<MeshRenderer>().material.color = Color.white;
}
isChecked = false;
}
}
在使用过程中,使用List保存物品时,调用Destroy()函数后,List里面的对象也会被删除掉。
所以,我们在代码中使用一个中间类ItemEntity.cs,代码如下:
public class ItemEntity {
private string objId;
private string objName;
private int count;
private string note;
private int level;
private bool isCanAdd;
private int maxAdd;
private bool isChecked;
public static ItemEntity FillData(ObjectItem item)
{
ItemEntity itemEntity = new ItemEntity();
itemEntity.ObjId = item.objId;
itemEntity.ObjName = item.objName;
itemEntity.Count = item.count;
itemEntity.Note = item.note;
itemEntity.Level = item.level;
itemEntity.IsCanAdd = item.isCanAdd;
itemEntity.MaxAdd = item.maxAdd;
itemEntity.IsChecked = item.isChecked;
return itemEntity;
}
public string ObjId
{
get
{
return objId;
}
set
{
objId = value;
}
}
public string ObjName
{
get
{
return objName;
}
set
{
objName = value;
}
}
public int Count
{
get
{
return count;
}
set
{
count = value;
}
}
public string Note
{
get
{
return note;
}
set
{
note = value;
}
}
public int Level
{
get
{
return level;
}
set
{
level = value;
}
}
public bool IsCanAdd
{
get
{
return isCanAdd;
}
set
{
isCanAdd = value;
}
}
public int MaxAdd
{
get
{
return maxAdd;
}
set
{
maxAdd = value;
}
}
public bool IsChecked
{
get
{
return isChecked;
}
set
{
isChecked = value;
}
}
}
最后,我们在玩家控制的角色上使用代码:
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour {
private Transform tr;
//背包类
private Pack pack;
// Use this for initialization
void Start () {
tr = GetComponent<Transform>();
pack = GetComponent<Pack>();
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.B))
{
pack.showPack();
}
Debug.DrawRay(tr.position, tr.forward * 2.0f, Color.green);
RaycastHit hit;
if (Physics.Raycast (tr.position, tr.forward, out hit, 2.0f)) {
//Debug.Log ("射线击中:" + hit.collider.gameObject.name + "\n tag:" + hit.collider.tag);
GameObject gameObj = hit.collider.gameObject;
ObjectItem obj = (ObjectItem)gameObj.GetComponent<ObjectItem>();
if (obj != null)
{
obj.isChecked = true;
//Debug.Log(obj.objName);
if (Input.GetKeyDown(KeyCode.E))
{
obj = pack.getItem(obj);
if(obj.count == 0)
{
//gameObj.SetActive(false);
Destroy(gameObj);
}
}
}
}
}
}
这样,我们就可以实现了拾取物品的基本功能。
当拾取超过了背包容量时,可以重叠的物品会叠加至最大数量,然后剩余数量依然存在于场景中。相当于只拾取了一部分。
代码还有很多没有使用到的属性,也是临时的一些想法,方便后续拓展。
代码变量的命名规范及代码流程还可以有更多的优化。有大牛可以给我指点指点 >.<
以上就是我摸索出来的拾取物品的一个大概模型。不知道你们是怎么实现的呢!