引言:
这个背包的实现是参照另外一位博主的代码然后修改之后的,因为我觉得那位博主的代码效率实在太低,并且难以拓展托拽的功能,所以我对博主的代码稍微做了调整,但是大致思路都是一样的.
还有就是这个是基于ScriptObject实现的数据存储
实现思路:
代码实现流程:
这个是canvas的结构,这里的mainbag需要调整一下布局,才能达到游戏中的自动格式化填充,感觉和前端一样还是比较简单的。
关于item的代码
这里我创建了一个脚本来区分每一个物体,并且将其做成预制体方便动态创建
public class ClassItem : MonoBehaviour
{
public Image img;
public Text text;
}
关于数据库的代码创建
这里我用的是ScriptObject来创建相应的物体数据库
[CreateAssetMenu(fileName ="CubeDataSet",menuName ="Items/Cube",order = 0)]
public class ItemScriptObj : ScriptableObject
{
public string Name;
public Sprite sprite;
public int num;
[TextArea]
public string info;
}
关于mainBag的代码
既然有了物品,那就必须要有管理这些,或者说罗列这些物品的代码,所以说我们就只需要让背包知道自己有哪些类,然后去相应的类数据库中寻找就可以了
所以代码也是很简洁
public class MainBag : MonoBehaviour
{
public List<ItemScriptObj> ItemList;
}
关于实际道具的代码
既然背包装的是一类物品,那么每一个物品都有的属性就是自己属于哪一类,所以每一个物品的脚本必须有一个数据库对象
public class CubeControl : MonoBehaviour
{
public ItemScriptObj SelfType;
public MainBag mainbag;
public void OnCollisionEnter(Collision collision)
{
if(collision.transform.name == "Capsule")//这个capsule是玩家的名字,后期可以改成tag判断
{
SelfType.num++;
if(!mainbag.ItemList.Contains(SelfType))
{
mainbag.ItemList.Add(SelfType);
BagMannager.InsertIntoUi(SelfType);
}
BagMannager.UpdateUi(SelfType);
Destroy(gameObject);
}
}
}
这样就将物品和他们对应的数据库对应起来了
关于Bagmannager的代码实现
这里用的是一个单例模式,主要功能就是插入和更新数据,刚好和上面的实际道具代码耦合
public class BagMannager : MonoBehaviour
{
public static BagMannager instance;
public ClassItem itemClass;
public MainBag bag;
public GameObject mainBag;
private void Awake()
{
if(instance == null)
instance = this;
else
Destroy(gameObject);
}
private void Start()
{
}
public static void InsertIntoUi(ItemScriptObj item)
{
ClassItem temp = Instantiate(instance.itemClass, instance.bag.transform);
temp.img.sprite = item.sprite;
temp.text.text = item.num.ToString();
}
public static void UpdateUi(ItemScriptObj item)
{
for (int i = 0; i < instance.mainBag.transform.childCount; i++)
{
ClassItem temp = instance.mainBag.transform.GetChild(i).GetComponent<ClassItem>();
if(temp.img.sprite == item.sprite)
{
temp.text.text = item.num.ToString() ;
}
break;
}
}
}
玩家控制代码
这个就是实现了背包的开和闭,以及简单的移动
public class PlayerController : MonoBehaviour
{
private Rigidbody rigid;
private float VerticalInput;
private float HorizontalInput;
public float moveSpeed = 5f;
public GameObject MainBag;
public GameObject Background;
// Start is called before the first frame update
void Start()
{
rigid = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
VerticalInput = Input.GetAxis("Vertical");
HorizontalInput = Input.GetAxis("Horizontal");
rigid.velocity = transform.forward * VerticalInput*moveSpeed + transform.right*HorizontalInput*moveSpeed;
if(Input.GetKey(KeyCode.Tab))
{
MainBag.SetActive(true);
Background.SetActive(true);
}
else
{
MainBag.SetActive(false);
Background.SetActive(false);
}
}
}
物品的托拽并且交换功能的实现
首先介绍两个东西,一个就是 EventSystem,这个是unity自带的事件监听器,可以响应鼠标点击,鼠标移动等等事件,但是要响应就必须实现响应的接口,IBeginDragHandler, IDragHandler, IEndDragHandler
第二个就是 GraphicRaycaster用于UI的射线检测,一般是画布自带的
public class DragItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private Vector2 vector;
private RectTransform rectTransform;
private EventSystem eventSystem;
private GraphicRaycaster gra;
private void Start()
{
eventSystem = EventSystem.current;
gra = FindObjectOfType<GraphicRaycaster>();
rectTransform = GetComponent<RectTransform>();
}
public void OnBeginDrag(PointerEventData eventData)
{
vector = transform.position;
}
public void OnDrag(PointerEventData eventData)
{
Vector3 pos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, eventData.position, eventData.enterEventCamera, out pos);
rectTransform.position = pos;
}
public void OnEndDrag(PointerEventData eventData)
{
//表示是否有检测到物体
List<GameObject> ITM = new List<GameObject>();
List<RaycastResult> results =GetHitItems(Input.mousePosition);
foreach(var item in results)
{
if(item.gameObject.tag == "Item")
{
ITM.Add(item.gameObject);
}
}
if(ITM.Count > 1)
{
int index1 = ITM[0].transform.GetSiblingIndex();
int index2 = ITM[1].transform.GetSiblingIndex();
ITM[0].transform.SetSiblingIndex(index2);
ITM[1].transform.SetSiblingIndex(index1);
}
else
{
transform.position = vector;
}
}
public List<RaycastResult> GetHitItems(Vector2 pos)
{
List<RaycastResult> temp = new List<RaycastResult>();
PointerEventData pd = new PointerEventData(eventSystem);
pd.position = pos;
gra.Raycast(pd, temp);
return temp;
}
}
实现思路就是
鼠标点击事件的开始保存了当前物体的世界坐标
当托拽事件发生的时候将鼠标的坐标以现有的recttransform为模板,转换到 世界坐标,这样就可以实现跟随移动了,最后通过射线判断是否有两个物体,如果有就交换顺序,从而实现物品的交换