【Unity3D插件】UniRx(基于Unity的响应式编程框架)插件教程

news2024/9/24 7:23:30

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • 简书地址
  • 我的个人博客
  • QQ群:1040082875

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、介绍UniRx插件

UniRx是一种基于Unity3D的响应式编程框架

UniRx就是Unity版本的Rx响应式扩展响应式就是观察者和定时器,扩展指的是LINQ的操作符。Rx响应式扩展的特点就是擅长处理时间上的异步的逻辑。用Rx响应式扩展的方式编程可以很好地组织大量异步与并行处理。

UniRx重写了.Net的响应式扩展,主要作用是解决时间上异步的逻辑,让异步逻辑变得更加简洁和优雅。

Unity3D通常是单线程,但是UniRx可以让多线程更容易。

UniRx可以简化 UGUI 的编程,所有的UI事件可以转化为UniRx 的事件流。

UniRx支持的平台有PC/Mac/Android/iOS/WebGL/WindowsStore等平台和库。

二、为什么使用UniRx插件

在项目中的一些逻辑操作需要做异步时间处理,比如说动画播放、网络请求、资源加载、场景过渡等等,这种情况通常要使用协程,也就是WWWCoroutine,但是使用协程来做异步通常不是一个很好的选择,因为:

  • 协程不能返回值,它的返回类型必须是IEnumerator
  • 协程不能处理异常,因为yield return 语句没有办法try-catch
  • 会导致使用大量的回调来处理逻辑
  • 使用协程会导致程序的耦合性高,造成协程中的逻辑过于复杂

UniRx就是为了解决这些问题来的,那么它有哪些优点呢:

  • UniRx的使用方式介于回调和事件之间,有事件的概念,也使用了回调,回调是在事件经过组织之后,只需要调用一次进行事件的处理。
  • UniRx促进了多线程的操作,提供了UGUI的UI编程,UI事件可以转化为UniRx的事件流。
  • Unity3D在2017版本后支持了C#中的astnc/awaitUniRx也为Unity提供了更轻量、强大的astnc/await集成。

三、UniRx插件下载

源码地址:
https://github.com/neuecc/UniRx

Unity Asset Store 地址(免费):
http://u3d.as/content/neuecc/uni-rx-reactive-extensions-for-unity/7tT

插件下载地址:
https://github.com/neuecc/UniRx/releases

UniRx中的astnc/await集成
https://github.com/Cysharp/UniTask

四、怎么使用UniRx插件

4-1、快速入门

将插件导入到项目中:
在这里插入图片描述
新建脚本UniRxTest.cs编辑代码,实现一个双击检测Demo:

using System;
using UniRx;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        // Observable.EveryUpdate调用协程的yield return null。
        // 它位于Update之后,LateUpdate之前。
        // Where等待操作的事件(当前事件是左键单击)
        var doubleClick = Observable.EveryUpdate()
            .Where(value => Input.GetMouseButtonDown(0));

        // Buffer 添加一个事件
        // Throttle 响应的最大间隔
        // TimeSpan.FromMilliseconds(250) 设置为250毫秒
        // Where 等待操作的事件(当前事件是左键单击)
        // Subscribe 绑定委托
        doubleClick.Buffer(doubleClick.Throttle(TimeSpan.FromMilliseconds(250)))
            .Where(value => value.Count >= 2)
            .Subscribe(value => Debug.Log("双击! 点击次数:" + value.Count));
    }
}

运行结果:
在这里插入图片描述
这个Demo使用了5行代码就演示了以下功能:

  • Update作为事件流
  • 组合事件流
  • 合并自身流
  • 方便处理基于时间的操作

4-2、定时功能(与协程对比)

在平时项目开发中,可能会遇到需要经过一段时间出发某些逻辑的操作,可以用协程这么写:

using System;
using System.Collections;
using UniRx;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        // 每5秒调用一次函数
        StartCoroutine(Timer(5, DoSomething));
    }

    // 定时器
    IEnumerator Timer(float seconds, Action callback)
    {
        yield return new WaitForSeconds(seconds);
        callback();
    }

    // 调用函数
    void DoSomething()
    {
        Debug.Log("TODO");
    }
}

那么用UniRx怎么写呢:

using System;
using System.Collections;
using UniRx;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        // 每5秒调用一次函数
        Observable.Timer(TimeSpan.FromSeconds(5))
           .Subscribe(value => {DoSomething();});
    }

    // 调用函数
    void DoSomething()
    {
        Debug.Log("TODO");
    }
}

甚至可以简化成一行代码:

using System;
using System.Collections;
using UniRx;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        // 每5秒调用一次函数
        Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(value => { Debug.Log("TODO"); });
    }
}

为了避免this销毁的时候,流程还没有销毁的情况,可以加一行代码:

using System;
using System.Collections;
using UniRx;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        // 每5秒调用一次函数
        Observable.Timer(TimeSpan.FromSeconds(5))
           .Subscribe(value => { DoSomething(); })
           .AddTo(this);
    }

    // 调用函数
    void DoSomething()
    {
        Debug.Log("TODO");
    }
}

AddTo(this)之后,就会将延迟和this(MonoBehaviour)绑定在一起了,当this被销毁的时候,定义的流程也会被销毁。

4-3、GET和POST操作

一般写法:

using System;
using System.Collections;
using UniRx;
using UnityEngine;
using UnityEngine.Networking;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(RequestData("www.baidu.com", new WWWForm(), ReturnValue));
    }

    //回调函数
    private void ReturnValue(string value)
    {
        Debug.Log(value);
    }

    /// <summary>
    /// 数据请求与发送
    /// </summary>
    /// <param name="url">请求的url</param>
    /// <param name="form">表单</param>
    /// <param name="dele">返回数据</param>
    /// <returns></returns>
    private IEnumerator RequestData(string url, WWWForm form, Action<string> dele = null)
    {
        UnityWebRequest req = UnityWebRequest.Post(url, form);
        yield return req.SendWebRequest();
        if (req.result == UnityWebRequest.Result.ProtocolError)
        {
            dele?.Invoke(req.error);
        }

        if (req.isDone)
        {
            dele?.Invoke(req.downloadHandler.text);
        }
    }
}

用UniRx写法:

using System;
using System.Collections;
using UniRx;
using UnityEngine;
using UnityEngine.Networking;

public class UniRxTest : MonoBehaviour
{
    void Start()
    {
        var request = ObservableWWW.Post("www.baidu.com", new WWWForm())
            .Subscribe(value => Debug.Log(value))
            .AddTo(this);
    }
}

注意:不是讨论那个写法好,那么写法不好,只是使用UniRx更加简洁,更推荐是用UnityWebRequest,因为UnityWebRequest功能更完善,更加有效。

4-4、加载场景-AsyncOperation

在异步加载资源或者异步加载场景的时候往往会用到 AsyncOperation。

UniRx 对 AsyncOperation 做了支持。使得加载进度可以很容易地监听。

示例代码如下:

using UniRx;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace UniRxLesson
{
    public class AsyncOperationExample : MonoBehaviour
    {
        void Start()
        {
            var progressObservable = new ScheduledNotifier();

            SceneManager.LoadSceneAsync(0).AsAsyncOperationObservable(progressObservable)
                        .Subscribe(asyncOperation =>
                        {
                            Debug.Log("load done");

                            Resources.LoadAsync("TestCanvas").AsAsyncOperationObservable()
                                     .Subscribe(resourceRequest =>
                                     {
                                         Instantiate(resourceRequest.asset);
                                     });
                        });

            progressObservable.Subscribe(progress =>
            {
                Debug.LogFormat("加载了:{0}", progress);
            });
        }
    }
} 

4-5、UGUI支持

示例代码:

using System;
using System.Collections;
using UniRx;
using UniRx.Diagnostics;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class UniRxTest : MonoBehaviour
{
    public Button mButton;
    public Toggle mToggle;
    public InputField mInput;
    public Text mText;
    public Slider mSlider;

    void Start()
    {
        // Button 按钮绑定事件
        mButton.onClick.AsObservable().Subscribe(_ => Debug.Log("clicked"));

        // Toggle 控制其他UI对象的激活
        mToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);

        // mInput Where筛选值不等于空的情况 绑定Text组件
        mInput.OnValueChangedAsObservable()
        .Where(x => x != null)
        .SubscribeToText(mText);

        // mSlider 绑定Text组件
        mSlider.OnValueChangedAsObservable()
            .SubscribeToText(mText, x => Math.Round(x, 2).ToString());
    }
}

可以看出来,UniRx去绑定UI还是很好用的,但不仅于此。

使用 UniRx可以很容易地实现 MVP(MVRP)设计模式。

为什么应该用 MVP模式而不是 MVVM模式?Unity 没有提供 UI 绑定机制,创建一个绑定层过于复杂并且会对性能造成影响(使用反射)。尽管如此,视图还是需要更新。 Presenters层知道 View 组件并且能更新它们。

虽然没有真的绑定,但 Observables 可以通知订阅者,功能上也差不多。这种模式叫做 Reactive Presenter 设计模式,示例代码如下:

using System;
using System.Collections;
using UniRx;
using UniRx.Diagnostics;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class UniRxTest : MonoBehaviour
{
    public Button mButton;
    public Toggle mToggle;
    public Text MyText;

    // 状态更改Model
    Enemy enemy = new Enemy(1000);

    void Start()
    {
        // 以响应式的方式从视图和模型中提供用户事件
        mButton.OnClickAsObservable().Subscribe(_ => enemy.CurrentHp.Value -= 100);
        mToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);

        // Model通过Rx通知更新视图
        enemy.CurrentHp.SubscribeToText(MyText);
        enemy.IsDead.Where(isDead => isDead)
            .Subscribe(_ =>
            {
                mToggle.interactable = mButton.interactable = false;
            });
    }
}
// 所有属性的值更改时都会通知
public class Enemy
{
    public ReactiveProperty<long> CurrentHp { get; private set; }
    public ReadOnlyReactiveProperty<bool> IsDead { get; private set; }

    public Enemy(int initialHp)
    {
        // 声明属性
        CurrentHp = new ReactiveProperty<long>(initialHp);
        IsDead = CurrentHp.Select(x => x <= 10).ToReadOnlyReactiveProperty();
    }
}

4-6、响应式属性ReactiveProperty

UniRx还有一个很强的属性ReactiveProperty,也就是响应式属性,之所以强大,是因为它让变量的变化过程中可以增加更多的功能更加的灵活。

比如,要监听一个变量值是否发生变化,可以这么写:

using System;
using System.Collections;
using UniRx;
using UniRx.Diagnostics;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class UniRxTest : MonoBehaviour
{
    public int mAge;
    public int Age
    {
        get
        {
            return mAge;
        }
        set
        {
            if (mAge != value)
            {
                mAge = value;
                OnAgeChanged();
            }
        }
    }

    public void OnAgeChanged()
    {
        Debug.Log("Value变化了");
    }
}

上述代码虽然也可以完成监听变量值变化的功能,但是如果要在外部访问,还需要写一个委托来监听,比较麻烦,如果用UniRx就会简单许多:

using System;
using System.Collections;
using UniRx;
using UniRx.Diagnostics;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class UniRxTest : MonoBehaviour
{
    public ReactiveProperty<int> Age = new ReactiveProperty<int>();
    void Start()
    {
        // 绑定值
        Age.Subscribe(value =>
        {
            Debug.Log("通知值变化");
        });

        // 改变值可以用 变量.value来获取或者更改
        Age.Value = 5;
    }
}

4-7、Animation播放某一帧的动画

代码主要用到了UniRx.Async,后面UniRx.Async被分割成Cysharp/UniTask,需要再导入Cysharp/UniTask包,步骤如下:

(1)Window→Package Manager打开包管理器:
在这里插入图片描述
(2)选择Add package from git URL…
在这里插入图片描述
(3)添加 https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask 至包管理器
在这里插入图片描述
(4)导入完成
在这里插入图片描述
(5)修改Api Compatibility Level:
在这里插入图片描述
示例代码:

using Cysharp.Threading.Tasks;
using UniRx;
using UnityEngine;

public class UniRxTest : MonoBehaviour
{
    private bool stopLoop = false;//动画控制
    void Start()
    {
    }

    /// <summary>
    /// Animation播放指定帧的动画
    /// </summary>
    /// <param name="myAnim">动画组件</param>
    /// <param name="startTimeInt">开始时间</param>
    /// <param name="endTimeInt">结束时间</param>
    private async void PlayAnimation(Animation myAnim, int startTimeInt, int endTimeInt)
    {
        int speed = GetSpeed(startTimeInt, endTimeInt);
        float frame = GetFrame(myAnim);
        float startTime;
        float endTime;
        if (speed == 1)
        {
            startTime = frame * startTimeInt;
            endTime = frame * endTimeInt;
        }
        else
        {
            startTime = frame * endTimeInt;
            endTime = frame * startTimeInt;
        }
        stopLoop = false;

        while (!stopLoop)
        {
            myAnim[myAnim.clip.name].time = startTime;//跳过开始帧
            myAnim[myAnim.clip.name].speed = speed;//正播还是倒播
            myAnim.Play(myAnim.clip.name);//Play()
            await UniTask.DelayFrame(1);//帧延缓,等Play()启动                                                               
            await UniTask.WaitUntil(() => myAnim[myAnim.clip.name].time > endTime);//播放到指定的进度点则停止        
            myAnim.Stop();//停止播放
            stopLoop = true;//停止播放
        }
    }

    // 判断是正播还是倒播
    int GetSpeed(int startTime, int endTime)
    {
        if (endTime - startTime > 0)
        {
            return 1;
        }
        else if (endTime - startTime < 0)
        {
            return -1;
        }
        else
        {
            return 1;
        }
    }

    // 得到动画的播放帧率
    float GetFrame(Animation myAnim)
    {
        return myAnim[myAnim.clip.name].length / 100;
    }
}

五、后记

讲解了UniRx插件以及UniTask插件的使用方法,难度有点高,适合慢慢学习(收藏吃灰)。

在学习过程中有什么不懂的都可以在博客主页找到我的联系方式。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

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

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

相关文章

九龙证券|石墨烯电池和锂离子电池哪个好

电池技术是电动汽车大力推广和开展的最大门槛&#xff0c;而电池工业正处于铅酸电池和传统锂电池开展均遇瓶颈的阶段&#xff0c;石墨烯储能设备的研制成功后&#xff0c;若能批量生产&#xff0c;则将为电池工业乃至电动车工业带来新的改造。那么石墨烯电池和锂离子电池哪个更…

VUE VS React 对比

VUE VS React 对比 这是面试中经常考察的一个问题&#xff0c;简单整理一下。我主要写 react &#xff0c;所以 react 的特点阐述的多一点。 语法格式 vue 是单独的文件格式&#xff0c;一个文件包括了 js css HTML 全部 React 通常是 jsx 格式&#xff0c;JS 和 HTML 写在…

shiro相关源码解析

1. 认证过程相关源码解析 前后文可接查看 shiro的登陆都是通过subject.login()方法实现&#xff0c;接下来我们就进入login方法查看实现过程&#xff1a; 1.1 进入DelegatingSubject类的login方法&#xff1a; 此类实现了Subject接口&#xff1a; public void login(Authen…

基于ssm的高校二手物品交易网 java idea mysql

本文论述了民办高校二手物品交易网的设计和实现&#xff0c;该网站从实际运用的角度出发&#xff0c;运用了计算机网站设计、数据库等相关知识&#xff0c;网络和Mysql数据库设计来实现的&#xff0c;网站主要包括用户注册、用户登录、浏览商品、搜索商品、查看商品并进行购买&…

bilibili全链路压测改造之全链自动化测试实践

01 、背景与意义 B站直播营收送礼业务有着高写、在跨晚和S赛等大型活动下流量陡增、数据实时性要求高等特性&#xff0c;传统压测对于写场景为了避免影响线上数据做了各种屏蔽和黑名单处理&#xff0c;有着无法逼近线上真实情况的问题&#xff0c;因此业务对全链路压测有着较大…

拿下阿里自动化测试岗23k*14薪offer的全程面试记录解析以及总结,一面二面三面,项目,功能,自动化,性能测试,面试题问答

一、自我介绍 面试官您好&#xff01;我叫xx&#xff0c;来自深圳&#xff0c;毕业之后一直从事于软件测试的工作&#xff0c;有做过保险、金融、电商等项目&#xff1b;有做过做功能测试、接口测试&#xff0c;自动化测试&#xff0c;在工作中积极主动、可以独立的完成测试工…

shiro(一):shiro基本概念及基本使用(认证、授权)

1. 权限的管理 1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现对用户访问系统的控制&#xff0c;按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用…

算法刷题打卡第76天:判断矩阵是否是一个 X 矩阵

判断矩阵是否是一个 X 矩阵 难度&#xff1a;简单 如果一个正方形矩阵满足下述 全部 条件&#xff0c;则称之为一个 X 矩阵 &#xff1a; 矩阵对角线上的所有元素都 不是 0 矩阵中所有其他元素都是 0 给你一个大小为 n x n 的二维整数数组 grid &#xff0c;表示一个正方形矩…

CV——day72:从零开始学YOLO——YOLO-v3(可以在我的资源里下载完整的v1到v3的笔记啦!)

YOLO-v36. YOLO-v36.1 YOLO-v3 改进综述6.2 多scale方法改进与特征融合6.3 经典变换方法对比分析6.4 残差连接方法解读6.5 整体网络模型架构分析6.6 先验框设计改进6.7 softmax层改进6. YOLO-v3 **tips&#xff1a;**作者本人因为美军广泛运用于军事领域&#xff0c;所以决定不…

基于php电影点播平台/电影网站

摘要网络技术给生活带来了十分的便利。所以把电影点播平台与现在网络相结合。在点播平台发展的整个过程中&#xff0c;电影信息管理担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类电影信息管理程序也在不断改进。本课题所设计的电影点播平台&#xff0c;使…

张艺谋《满江红》起诉自媒体人,杨语莲推荐周兆成意在下个谋女郎

伴随着兔年春节的到来&#xff0c;又迎来一波贺岁剧热潮&#xff0c;著名导演张艺谋的《满江红》&#xff0c;也如期和观众朋友见面。随着春节的逐渐结束&#xff0c;贺岁电影《满江红》票房&#xff0c;也再次创下了新高&#xff0c;关于这部电影的话题也多了起来。 最引人关注…

中国电子学会2021年12月份青少年软件编程Python等级考试试卷一级真题(含答案)

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;一级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 昨天的温度是5摄氏度&#xff0c;今天降温7摄氏度&#xff0c;今天的温度是多少摄氏度&#xff1f;&#xff08; &#xff09; A. 12 …

利用ChatGPT自动编写下载高德地图poi数据的代码

最近ChatGPT很火&#xff0c;它自己对于自己的解释如下图。我们可以让它来帮我们写代码&#xff0c;属于是薅机器人羊毛了。 首先注册账号&#xff0c;可百度&#xff0c;如&#xff1a;【教程】ChatGPT 保姆级注册教程&#xff0c;但中国大陆手机号不支持OpenAI的注册服务&am…

进程概念(PCB、进程创建、进程状态等)

进程是一个运行的程序&#xff0c;是所有计算机的基础。这个过程与计算机代码不一样&#xff0c;尽管它们非常相似。程序通常被认为是 “被动的” 实体&#xff0c;而进程则是 “主动的” 实体。硬件状态、RAM、CPU和其它属性都是进程持有的属性。下面我们就来了解更多关于进程…

活体识别1:近红外(NIR)图像特性

说明 最近在接触活体识别&#xff0c;在网上找到一个介绍近红外光&#xff08;NIR&#xff09;特性的论文&#xff0c;我简单做个笔记。原文的全文在文末参考资料里。 来自&#xff1a;[1]隋孟君,茅耀斌,孙金生.基于近红外图像特征的活体人脸检测[J].自动化与仪器仪表,2021(0…

Win10下使用WSL2

打包 wsl --export Ubuntu-20.04 E:\Ubuntu\ubuntu.tar.gz 注销之前 wsl --unregister Ubuntu-20.04 导入 wsl --import Ubuntu-20.04 E:\Ubuntu\ E:\Ubuntu\ubuntu.tar.gz --version 2 设置默认登陆用户为安装时用户名 ubuntu2004.exe config --default-user dwb 更新清…

vue 预览 word

最近做的项目要求实现预览word, pdf&#xff0c;png等文件功能&#xff0c;pdf以及png都很简单&#xff0c;轻松百度搞定&#xff0c;但是word预览研究了好久&#xff0c;所以特此记录分享。前端实现预览word分为两种&#xff0c;一种是上传前预览&#xff08;也就是前端使用in…

SpringBoot笔记:统一请求参数修改(HttpServletRequest流复制),加解密参数也可参考处理

文章目录需求实现思路实战演练实现过滤器Filter继承 HttpServletRequestWrapper实现 RequestBodyAdvice 统一处理请求参数测试代码测试效果需求 需要进行统一的解密请求 header 头里面的关键字 encryKey &#xff0c;将解密出来的值赋给 provinceId 并传递给后端的每一个请求接…

23年 yolov5车辆识别+行人识别+车牌识别+车速检测代码(python)

行人识别yolov5和v7对比yolo车距yolo车距1代码&#xff1a;yolov5车辆检测代码 已有1503人下载 代码无需更改&#xff0c;直接可以预测&#xff01;&#xff01;&#xff01; 流程&#xff1a; 版本与配置声明 # YOLOv5 requirements # Usage: pip install -r requirements.tx…

C语言--结构体初阶

目录前言结构体类型的声明什么是结构结构的声明结构体变量的定义结构成员的类型结构体变量的初始化结构体的成员访问结构体传参函数调用的参数压栈前言 在前面的C语言学习中&#xff0c;我们学习了形如char&#xff0c;short&#xff0c;int&#xff0c;float等的不同类型的变…