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

news2024/11/16 18:38:08

效果演示

在这里插入图片描述

文章目录

  • 效果演示
  • 系列目录
  • 前言
  • 人物和视角基本控制
  • 简单的背包系统和物品交互
    • 绘制背包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.coloradmin.cn/o/1443908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

游戏服务器哪家强?国内几款主流云服务器测评

游戏服务器租用多少钱一年&#xff1f;1个月游戏服务器费用多少&#xff1f;阿里云游戏服务器26元1个月、腾讯云游戏服务器32元&#xff0c;华为云26元&#xff0c;游戏服务器配置从4核16G、4核32G、8核32G、16核64G等配置可选&#xff0c;游戏专业服务器公网带宽10M、12M、15M…

【大厂AI课学习笔记】【1.6 人工智能基础知识】(1)人工智能、机器学习、深度学习之间的关系

6.1 人工智能、机器学习与深度学习的关系 必须要掌握的内容&#xff1a; 如上图&#xff1a;人工智能>机器学习>深度学习。 机器学习是人工智能的一个分支&#xff0c;该领域的主要研究对象是人工智能&#xff0c;特别是如何在经验学习中改进具体算法的性能。 深度学习…

算法||实现典型数据结构的查找、添加和删除数据 并分析其时间和空间复杂度

实现典型数据结构的查找、添加和删除数据 并分析其时间和空间复杂度 线性结构&#xff1a; 数组&#xff1a;是一种线性表数据结构&#xff0c;它用一组连续的内存空间&#xff0c;来存储一组具有相同类型的数据。 查找数据 &#xff1a;随机访问 流程图 /** 查询元素下标…

02 数据库管理 数据表管理

文章目录 数据库管理数据表管理基础数据类型表的基本操作 数据库管理 查看已有库 show databases; 创建库 create database 库名 [character set utf8]; e.g. 创建stu数据库&#xff0c;编码为utf8 create database stu character set utf8; create database stu charsetutf8;…

LSF 主机状态 unreach 分析

在LSF集群运行过程中&#xff0c;有主机状态变为 unreach。熟悉LSF的朋友都知道主机状态为 unreach 表示主机上的 SBD 服务中断服务了&#xff0c;但其它服务 LIM 和 RES 还在正常运行。 影响分析 那么主机上的 SBD 服务中断的影响是什么呢&#xff1f; 我们需要先明白 SBD …

Java 学习和实践笔记(6)

各数据类型所占的空间&#xff1a; byte: 1个字节 short&#xff1a;2个字节 int&#xff1a;4个 long&#xff1a;8个 float&#xff1a;4个 double: 8个 char:1个 boolean:1bit 所有引用数据类型都是4个字节&#xff0c;实际其值是指向该数据类型的地址。 上图中稍特…

使用Softing edgeConnector模块将云轻松连接到Siemens PLC

一 工业边缘的连接解决方案 云服务提供商 (CSP) 引入了服务和功能&#xff0c;以简化基于云的工业物联网解决方案的实施。Azure Industrial IoT Platform或AWS IoT SiteWise支持标准协议和接口&#xff0c;例如OPC UA或MQTT。但是&#xff0c;如果您希望在典型的旧改项目中连接…

Debezium发布历史122

原文地址&#xff1a; https://debezium.io/blog/2022/05/04/switch-to-java-11/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. Switching to Java 11/17 May 4, 2022 by Vojtěch Jurnek community news 你可…

腾讯云4核8G服务器性能如何?支持多少用户访问?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

VSTO打包Word插件WPS也支持

启动AdvancedInstallerPortable.exe打包软件 选择“加载项” 选择“office加载项”之后点“创建项目” 四、输入自已的插件名和公司名 任选一种包类型 五、选择包的保存位置 勾选“vsto office加载项” 六、选择要打包的项目debug文件夹 选择相应版本 配置相应环境 选择语言 添…

Linux基础-配置网络

Linux配置网络的方式 1.图形界面 右上角-wired-配置 点加号-新建网络配置文件2.NetworkManager工具 2.1用图形终端nmtui 1.新建网络配置文件add 1.指定网络设备的类型Ethernet 2.配置网络配置文件的名称&#xff0c;名称可以有空格 3.配置网络配置文件对应的物理网络设备的…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Blank组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Blank组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Blank组件 空白填充组件&#xff0c;在容器主轴方向上&#xff0c;空白填充组件具…

sklearn中一些简单机器学习算法的使用

目录 前言 KNN算法 决策树算法 朴素贝叶斯算法 岭回归算法 线性优化算法 前言 本篇文章会介绍一些sklearn库中简单的机器学习算法如何使用&#xff0c;一些注释已经写在代码中&#xff0c;帮助一些小伙伴入门sklearn库的使用。 注意&#xff1a;本篇文章只涉及到如何使用…

Openwifi 开源项目解读(一)

Openwifi 是一个关于wifi 系统的开源项目&#xff0c;是一个少有的优秀的关于wifi的开源项目&#xff0c;项目中包括了wifi的基带、lowmac、linux驱动 等三部分&#xff0c;其中基带、lowmac部分是在FPGA中实现&#xff0c;wifi驱动部分是运行在Linux下&#xff0c;因此openwif…

失去中国市场的三星仍是全球第一,但中国手机无法失去海外市场

随着2023年分析机构公布全球手机市场和中国手机市场的数据&#xff0c;业界终于看清中国市场早已没有以前那么重要&#xff0c;三星、苹果这些国际品牌对中国市场的依赖没有他们想象的那么严重&#xff0c;相反中国手机对海外市场比以往任何时候都要更依赖了。 三星在2023年被苹…

【matalab】基于Octave的信号处理与滤波分析案例

一、基于Octave的信号处理与滤波分析案例 GNU Octave是一款开源软件&#xff0c;类似于MATLAB&#xff0c;广泛用于数值计算和信号处理。 一个简单的信号处理与滤波分析案例&#xff0c;说明如何在Octave中生成一个有噪声的信号&#xff0c;并设计一个滤波器来去除噪声。 首…

【数学建模】【2024年】【第40届】【MCM/ICM】【C题 网球运动中的“动量”】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 MCM Problem C: Momentum in Tennis In the 2023 Wimbledon Gentlemen’s final, 20-year-old Spanish rising star Carlos Alcaraz defeated 36-year-old Novak Djokovic. The loss was Djokovic’s first at Wimbledon…

react函数组件中使用context

效果 1.在父组件中创建一个createcontext并将他导出 import React, { createContext } from react import Bpp from ./Bpp import Cpp from ./Cpp export let MyContext createContext(我是组件B) export let Ccontext createContext(我是组件C)export default function App…

Linux操作系统基础(五):Linux的目录结构

文章目录 Linux的目录结构 一、Linux目录与Windows目录区别 二、常见目录介绍&#xff08;记住重点&#xff09; Linux的目录结构 一、Linux目录与Windows目录区别 Linux的目录结构是一个树型结构 Windows 系统 可以拥有多个盘符, 如 C盘、D盘、E盘 Linux 没有盘符 这个概…

网站建设详细步骤和流程是什么

我们选择了白嫖雨云的二级域名 浏览器输入https://www.rainyun.com/z22_ 创建账号然后选择一个你喜欢的子域名我建议后缀选择ates.top的 选择自定义地址&#xff0c;类型选择cname 现在要选择记录值了&#xff0c;有a&#xff0c;aa&#xff0c;txt等 根据实际情况填写。就可以…