【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱1(附带项目源码)

效果演示

在这里插入图片描述

文章目录

  • 效果演示
  • 系列目录
  • 前言
  • 人物和视角基本控制
  • 简单的背包系统和物品交互
    • 绘制背包UI
    • 脚本控制
  • 源码
  • 完结

系列目录

前言

欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中,我们将探索如何用unity制作一个3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱等功能,我会附带项目源码,以便你更好理解它。

人物和视角基本控制

具体可以看我这篇文章:
【unity小技巧】unity最完美的CharacterController 3d角色控制器,实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果,复制粘贴即用

这里我就直接贴出代码了

人物移动控制

[RequireComponent(typeof(CharacterController))]
public class MovementScript : MonoBehaviour
{[Tooltip("角色控制器")] public CharacterController characterController;[Tooltip("重力加速度")] private float Gravity = -19.8f;private float horizontal;private float vertical;[Header("移动")][Tooltip("角色行走的速度")] public float walkSpeed = 6f;[Tooltip("角色奔跑的速度")] public float runSpeed = 9f;[Tooltip("角色移动的方向")] private Vector3 moveDirection;[Tooltip("当前速度")] private float speed;[Tooltip("是否奔跑")] private bool isRun;[Header("地面检测")][Tooltip("是否在地面")] private bool isGround;[Header("跳跃")][Tooltip("角色跳跃的高度")] public float jumpHeight = 8f;private float _verticalVelocity;void Start(){speed = walkSpeed;}void Update(){horizontal = Input.GetAxis("Horizontal");vertical = Input.GetAxis("Vertical");//地面检测isGround = characterController.isGrounded;SetSpeed();SetRun();SetMove();SetJump();}//速度设置void SetSpeed(){if (isRun){speed = runSpeed;}else{speed = walkSpeed;}}//控制奔跑void SetRun(){if (Input.GetKey(KeyCode.LeftShift)){isRun = true;}else{isRun = false;}}//控制移动void SetMove(){moveDirection = transform.right * horizontal + transform.forward * vertical; // 计算移动方向moveDirection = moveDirection.normalized; // 归一化移动方向,避免斜向移动速度过快  }//控制跳跃void SetJump(){bool jump = Input.GetButtonDown("Jump");if (isGround){// 在着地时阻止垂直速度无限下降if (_verticalVelocity < 0.0f){_verticalVelocity = -2f;}if (jump){_verticalVelocity = jumpHeight;}}else{//随时间施加重力_verticalVelocity += Gravity * Time.deltaTime;}characterController.Move(moveDirection * speed * Time.deltaTime + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);}
}

视角控制

public class MouseLook : MonoBehaviour
{// 鼠标灵敏度public float mouseSensitivity = 500f;// 玩家的身体Transform组件,用于旋转public Transform playerBody;// x轴的旋转角度float xRotation = 0f;void Start(){// 锁定光标到屏幕中心,并隐藏光标Cursor.lockState = CursorLockMode.Locked;}// Update在每一帧调用void Update(){// 执行自由视角查看功能FreeLook();}// 自由视角查看功能的实现void FreeLook(){// 获取鼠标X轴和Y轴的移动量,乘以灵敏度和时间,得到平滑的移动速率float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;//限制旋转角度在-90到90度之间,防止过度翻转xRotation = Mathf.Clamp(xRotation, -90f, 90f);// 累计x轴上的旋转量xRotation -= mouseY;// 应用摄像头的x轴旋转transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);// 应用玩家身体的y轴旋转playerBody.Rotate(Vector3.up * mouseX);}
}

效果
在这里插入图片描述

简单的背包系统和物品交互

对UI知识还不太懂的小伙伴可以看这篇基础篇文件:【Unity游戏开发教程】零基础带你从小白到超神30——UI组件和布局的使用

绘制背包UI

物品插槽背景框
在这里插入图片描述
物品插槽,可以把物品插槽做出预制体,后面好修改
在这里插入图片描述

准星图像和文本
在这里插入图片描述

脚本控制

物品信息脚本

public class Item : MonoBehaviour
{public new string name = "New Item";//物品名称[TextArea]public string description = "New Description";//物品描述public Sprite icon;//物品图标public int currentQuantity = 1;//物品当前数量public int maxQuantity = 16;//物品最大堆叠数量
}

背包插槽脚本

public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{public bool hovered; // 鼠标是否悬停在该槽位上的标志private Item heldItem; // 当前槽位持有的物品private Color opaque = new Color(1, 1, 1, 1); // 不透明颜色private Color transparent = new Color(1, 1, 1, 0); // 透明颜色private Image thisSlotImage; // 该槽位的图像组件public TMP_Text thisSlotQuantityText; // 用于显示物品数量的文本组件// 初始化槽位public void initialiseSlot(){thisSlotImage = gameObject.GetComponent<Image>();thisSlotQuantityText = transform.GetChild(0).GetComponent<TMP_Text>();thisSlotImage.sprite = null;thisSlotImage.color = transparent;setItem(null);}// 设置槽位中的物品public void setItem(Item item){heldItem = item;if (item != null){thisSlotImage.sprite = heldItem.icon;thisSlotImage.color = opaque;updateData();}else {thisSlotImage.sprite = null;thisSlotImage.color = transparent;updateData();}}// 获取当前槽位持有的物品public Item getItem(){return heldItem;}// 当前槽位是否持有的物品public bool hasItem(){return heldItem ? true : false;}// 更新槽位显示的数据public void updateData(){if (heldItem != null) // 如果持有物品thisSlotQuantityText.text = heldItem.currentQuantity.ToString(); // 显示物品的数量else // 如果不持有物品thisSlotQuantityText.text = "";}// 当鼠标指针进入槽位区域时调用public void OnPointerEnter(PointerEventData pointerEventData){hovered = true;}// 当鼠标指针离开槽位区域时调用public void OnPointerExit(PointerEventData pointerEventData){hovered = false;}
}

库存系统脚本

public class Inventory : MonoBehaviour
{[Header("UI")]public GameObject inventory; // 游戏中的背包界面public List<Slot> allInventorySlots = new List<Slot>(); // 所有的槽位列表public List<Slot> inventorySloats = new List<Slot>();//背包的的槽位列表public Image crosshair; // 准星图像public TMP_Text itemHoverText; // 当中心悬停在物品上时显示物品名称的文本[Header("射线检测")]public float raycastDistance = 5f; // 射线检测的距离public LayerMask itemLayer; // 射线检测的目标层,用于识别物品public void Start(){toggleInventory(false); // 初始时关闭背包界面//合并槽位allInventorySlots.AddRange(inventorySloats);foreach (Slot uiSlot in allInventorySlots) // 初始化所有槽位{uiSlot.initialiseSlot();}}public void Update(){itemRaycast(Input.GetKeyDown(KeyCode.E)); // 显示物品名称和按E拾取物品if (Input.GetKeyDown(KeyCode.Tab)) // 按下tab键切换背包界面的显示状态toggleInventory(!inventory.activeInHierarchy);}private void itemRaycast(bool hasClicked = false){itemHoverText.text = ""; // 默认不显示任何物品名称Ray ray = Camera.main.ScreenPointToRay(crosshair.transform.position); // 从准星位置发出射线RaycastHit hit;if (Physics.Raycast(ray, out hit, raycastDistance, itemLayer)) // 如果射线检测到物品层的对象{if (hit.collider != null){if (hasClicked) // 如果是按了操作,尝试捡起物品{Item newItem = hit.collider.GetComponent<Item>();if (newItem){addItemToInventory(newItem); // 将物品添加到背包中}}else // 否则,仅获取物品名称以显示{Item newItem = hit.collider.GetComponent<Item>();if (newItem){itemHoverText.text = newItem.name; // 显示物品名称}}}}}//将物品添加到背包中private void addItemToInventory(Item itemToAdd){int leftoverQuantity = itemToAdd.currentQuantity; // 剩余需要添加到背包的物品数量Slot openSlot = null; // 记录一个空的槽位for (int i = 0; i < allInventorySlots.Count; i++) // 遍历所有槽位{Item heldItem = allInventorySlots[i].getItem();if (heldItem != null && itemToAdd.name == heldItem.name) // 如果槽位中有相同名称的物品{int freeSpaceInSlot = heldItem.maxQuantity - heldItem.currentQuantity; // 计算槽位中的剩余空间if (freeSpaceInSlot >= leftoverQuantity) // 如果剩余空间足够{heldItem.currentQuantity += leftoverQuantity; // 添加物品到该槽位Destroy(itemToAdd.gameObject); // 销毁场景中的物品对象allInventorySlots[i].updateData(); // 更新槽位显示的数据return;}else // 如果剩余空间不足{heldItem.currentQuantity = heldItem.maxQuantity; // 填满当前槽位leftoverQuantity -= freeSpaceInSlot; // 更新剩余需要添加的物品数量}}else if (heldItem == null) // 如果槽位为空{if (!openSlot)openSlot = allInventorySlots[i]; // 记录第一个空槽位}allInventorySlots[i].updateData(); // 更新槽位显示的数据}if (leftoverQuantity > 0 && openSlot) // 如果还有剩余物品且找到了空槽位{openSlot.setItem(itemToAdd); // 将物品添加到空槽位itemToAdd.currentQuantity = leftoverQuantity; // 更新物品的数量itemToAdd.gameObject.SetActive(false); // 隐藏场景中的物品对象}else{itemToAdd.currentQuantity = leftoverQuantity; // 更新物品的数量}}private void toggleInventory(bool enable){//关闭背包时,关闭所有鼠标悬停在该槽位上的标志if (!enable){foreach (Slot curSlot in allInventorySlots){curSlot.hovered = false;}}inventory.SetActive(enable); // 根据参数显示或隐藏背包界面Cursor.lockState = enable ? CursorLockMode.None : CursorLockMode.Locked; // 根据背包界面的状态锁定或解锁鼠标指针Cursor.visible = enable; // 设置鼠标指针的可见性// 禁用或启用相机的旋转控制Camera.main.GetComponent<MouseLook>().enabled = !enable;}
}

物品挂载Item脚本,配置参数,记得添加碰撞体并修改图层为Item
在这里插入图片描述
背包插槽挂载Slot脚本
在这里插入图片描述
角色上挂载Inventory脚本
在这里插入图片描述

拾取
在这里插入图片描述

源码

源码不出意外的话我会放在最后一节

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/466366.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go高级并发模式

Go对并发提供了强大的原生支持&#xff0c;本文讨论Go的高级并发模式&#xff0c;理解这些并发模式&#xff0c;可以帮助我们编写高效的Go应用程序。原文: Advanced Concurrency Patterns in Go "并发不是并行&#xff0c;但使并行成为可能。" —— Rob Pike 本文将深…

android中实现设备尺寸适配

1、引言 设备尺寸适配的重要性想必就不用我多说了&#xff0c;在我发布的历史文章中我曾谈过Qt中的设备尺寸适配问题&#xff0c;那这里我就来教大家如何在android中做设备尺寸适配。在android中设备尺寸适配的方式有好几种&#xff0c;但我喜欢的还是使用获取设备真实尺寸然后…

就业|高校毕业生就业信息小程序|基于微信小程序的高校毕业生就业信息的设计与实现(源码+数据库+文档)

高校毕业生就业信息小程序目录 目录 基于微信小程序的高校毕业生就业信息的设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户小程序模块 2、用户信息管理 2、职位招聘管理 3、公司信息管理 4、论坛信息管理 四、数据库设计 1、实体ER图 五、核心代码 …

C语言学习记录

牛牛的第二个整数_牛客题霸_牛客网 (nowcoder.com) 注意输入的格式&#xff0c;%d空格%d空格%d

数据结构第十四天(树的存储/双亲表示法)

目录 前言 概述 接口&#xff1a; 源码&#xff1a; 测试函数&#xff1a; 运行结果&#xff1a; 往期精彩内容 前言 孩子&#xff0c;一定要记得你的父母啊&#xff01;&#xff01;&#xff01; 哈哈&#xff0c;今天开始学习树结构中的双亲表示法&#xff0c;让孩…

C++重新入门-C++ 函数

函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;即主函数 main() &#xff0c;所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的&#xff0c;但在逻辑上&#xff0c;划分通常…

【JAVA-Day76】Java线程解析:三态和五态

Java线程解析&#xff1a;三态和五态 《Java线程状态深度解析&#xff1a;三态和五态探秘 &#x1f60e;》摘要引言一、什么是三态 &#x1f60a;二、什么是五态 &#x1f603;三、五态之间如何转变3.1 新建状态转换到运行状态3.2 运行状态转换到阻塞状态3.3 运行状态转换到等待…

为什么Python是数据科学家的首选语言

这篇文章全面探讨了Python作为数据科学领域首选语言的原因。从Python的历史、特性&#xff0c;到在数据科学中的应用实例&#xff0c;再到与其他数据科学语言的比较&#xff0c;以及在实际企业中的应用&#xff0c;我们深入剖析了Python的优势与挑战&#xff0c;最后对Python的…

【原创 附源码】Flutter安卓及iOS海外登录--Facebook登录最详细流程

最近接触了几个海外登录的平台&#xff0c;踩了很多坑&#xff0c;也总结了很多东西&#xff0c;决定记录下来给路过的兄弟坐个参考&#xff0c;也留着以后留着回顾。更新时间为2024年2月12日&#xff0c;后续集成方式可能会有变动&#xff0c;所以目前的集成流程仅供参考&…

详细分析Redis中数值乱码的根本原因以及解决方式

目录 前言1. 问题所示2. 原理分析3. 拓展 前言 对于这方面的相关知识推荐阅读&#xff1a; Redis框架从入门到学精&#xff08;全&#xff09;Java关于RedisTemplate的使用分析 附代码java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; …

nginx2

mkdir /usr/local/develop cd /usr/local/develop 下载 wget http://nginx.org/download/nginx-1.17.4.tar.gz yum install git git clone https://github.com/arut/nginx-rtmp-module.git 解压文件 tar zxmf nginx-1.17.4.tar.gz 进入解压目录 cd nginx-1.17.4/ 安装编译…

Ocr之TesseractOcr的安装及使用

目录 一、安装环境 二、安装内容 三、安装过程及识别测试 1. 安装过程 2. 程序编写 总结 1. 安装复杂度较低 2. 国外开源Ocr 3. 可设置识别参数 4. 工具类 一、 系统环境windows 10 linux环境也可安装, 可借鉴此篇文章>> | 二、安装内容 Tesseract exe 程序安…