【Unity3D】实现横版2D游戏——攀爬绳索(简易版)

目录

GeneRope.cs 场景绳索生成类 

HeroColliderController.cs 控制角色与单向平台是否忽略碰撞

HeroClampController.cs 控制角色攀爬

OnTriggerEnter2D方法

OnTriggerStay2D方法

OnTriggerExit2D方法

Update方法

开始攀爬

结束攀爬

Sensor_HeroKnight.cs 角色触发器

HeroKnight.cs 角色类


  

基于【Unity3D】实现横版2D游戏——单向平台(简易版)-CSDN博客

实现了单向平台后才能进行攀爬绳索到平台,否则攀爬到平台后会被阻挡,以及无法从平台往下攀爬。

GeneRope.cs 场景绳索生成类 

GitHub - dbrizov/NaughtyAttributes: Attribute Extensions for Unity

上面是一个特性能够让脚本上出现一个按钮去点击执行我们的生成绳索方法   

用法:[Button("Generate Rope")] 写在生成绳索方法头部。

绳索标签Rope

using System.Collections;
using NaughtyAttributes;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class GeneRope : MonoBehaviour
{
    public Sprite top;
    public Sprite middle;
    public Sprite bottom;

    public int ropeNodeCount;
    public Vector2 ropeNodeSize;

    List<GameObject> nodeList;
    BoxCollider2D boxCollider2D;

    [Button("Generate Rope")]
    public void GenerateRope()
    {
        //清理
        if (nodeList == null)
        {
            nodeList = new List<GameObject>();
        }
        else
        {
            for (int i = nodeList.Count - 1; i >= 0; i--)
            {
                DestroyImmediate(nodeList[i]);
            }
            nodeList.Clear();
        }

        Transform[] childrens = transform.GetComponentsInChildren<Transform>();
        if (childrens != null && childrens.Length > 0)
        {
            for (int i = childrens.Length - 1; i >= 0; i--)
            {
                if (childrens[i].gameObject != this.gameObject)
                {
                    DestroyImmediate(childrens[i].gameObject);
                }
            }
        }

        //生成绳索节点图片
        for (int i = 0; i < ropeNodeCount; i++)
        {
            Sprite sprite;
            if (i == 0)
            {
                //头部
                sprite = top;
            }
            else if (i == ropeNodeCount - 1)
            {
                //尾部
                sprite = bottom;
            }
            else
            {
                //中间
                sprite = middle;
            }
            SpriteRenderer sp = new GameObject("node_" + i, typeof(SpriteRenderer)).GetComponent<SpriteRenderer>();
            sp.sprite = sprite;
            nodeList.Add(sp.gameObject);
            sp.transform.SetParent(transform);
            //从根节点向下延伸的绳索
            sp.transform.localPosition = new Vector3(0, i * -ropeNodeSize.y - 0.5f, 0);
        }

        //生成绳索碰撞体(1个)
        boxCollider2D = GetComponent<BoxCollider2D>();
        if (boxCollider2D == null)
        {
            boxCollider2D = gameObject.AddComponent<BoxCollider2D>();
        }
        boxCollider2D.size = new Vector2(ropeNodeSize.x, ropeNodeSize.y * ropeNodeCount);
        boxCollider2D.offset = new Vector2(0, boxCollider2D.size.y * -0.5f);
        boxCollider2D.isTrigger = true;

        //绳索标签(触发检测使用判定是否为绳索,处理攀爬逻辑)
        gameObject.tag = "Rope";
    }
}

HeroColliderController.cs 控制角色与单向平台是否忽略碰撞

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HeroColliderController : MonoBehaviour
{
    BoxCollider2D heroCollider;
    public bool isOn;
    Collider2D m_Collision;

    public bool isIgnoreCollision = false;
    private void Awake()
    {
        heroCollider = GetComponent<BoxCollider2D>();
    }

    IEnumerator StartIgnoreCollision(Collider2D collider2D)
    {
        //Debug.Log("忽略碰撞");
        //等待人物完整地达到平台上 或 平台下时 退出死循环
        while (isOn)
        {
            yield return new WaitForEndOfFrame();
        }
        //Debug.Log("恢复");
        Physics2D.IgnoreCollision(heroCollider, collider2D, false); //恢复
        isIgnoreCollision = false;
        isOn = false;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        //实现从下往上跳跃穿过单向平台效果
        if (collision != null && collision.gameObject.tag == "SingleDirCollider" && !isOn)
        {
            //this.transform.position.y + 0.062f是人物minY
            if (collision.GetComponent<SingleDirCollider>().maxY > (this.transform.position.y + 0.062f))
            {
                IgnoreCollision(collision);
            }
        }
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        //实现按下↓键位 让角色从单方向平台往下掉效果
        if (Input.GetKeyDown(KeyCode.DownArrow) && collision.gameObject.tag == "SingleDirCollider")
        {
            IgnoreCollision(collision.collider);
        }
    }

    public void IgnoreCollision(Collider2D collision)
    {
        isOn = true;
        m_Collision = collision;
        Physics2D.IgnoreCollision(heroCollider, collision); //这2个碰撞体互相忽略碰撞
        isIgnoreCollision = true;
        StartCoroutine(StartIgnoreCollision(collision));
    }

    public void ResumeCollider()
    {
        Physics2D.IgnoreCollision(heroCollider, m_Collision, false); //恢复
        isOn = false;
        isIgnoreCollision = false;
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision != null && collision.gameObject.tag == "SingleDirCollider" && isOn)
        {
            var singleDirCollider = collision.GetComponent<SingleDirCollider>();
            //this.transform.position.y + 0.062f是人物minY, this.transform.position.y + 1.31f是人物maxY
            //当人物完整地位于平台之上 或 之下时
            if (singleDirCollider.maxY < (this.transform.position.y + 0.062f) || singleDirCollider.minY > (this.transform.position.y + 1.31f))
            {
                //协程死循环退出
                isOn = false;
            }
        }
    }
}

HeroClampController.cs 控制角色攀爬

OnTriggerEnter2D方法

当某个触发器检测到单向平台,持有它 并 triggerCount计数+1

OnTriggerStay2D方法

当角色检测到绳索时,角色底部在绳索顶部之上,需要按↓键才开始往下爬,此时需要立刻将角色底部瞬移到绳索顶部之下,避免与结束攀爬条件3冲突即

//3.接触单向平台 且人物在单向平台之上时 离开攀爬
lastIsTouchSingleDirPlatform = (singleDirCollider.maxY) < (heroKnight.transform.position.y + 0.062f);

角色底部在绳索顶部之下,需要按↑键才开始往上爬。 

OnTriggerExit2D方法

检测到某个触发器离开单向平台时,triggerCount计数-1,当triggerCount为0时,需要进行结束攀爬条件2判定

//2.接触单向平台 且人物在单向平台之上时 离开攀爬
lastIsTouchSingleDirPlatform = (singleDirCollider.maxY) <= (heroKnight.transform.position.y + 0.062f);

 并且将持有的单向平台脚本置空 singleDirCollider = null;

Update方法

检查到攀爬中,会使用角色类heroKnight.MoveY移动角色进行匀速运动上下攀爬。
进行结束攀爬条件1,2种情况:
1、isGrounds && !singleDirCollider :在地面且没有接触中的单向平台,结束攀爬
2、isGrounds && singleDirCollider && (singleDirCollider.maxY) <= (heroKnight.transform.position.y + 0.062f):在地面且接触单向平台中且角色在平台之上,结束攀爬。

开始攀爬

需要立即忽略重力影响以及接触中的单向平台碰撞体,否则会发生异常。

结束攀爬

需要立即恢复重力以及碰撞体

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HeroClampController : MonoBehaviour
{
    //英雄类对象 控制移动
    HeroKnight heroKnight;

    //是否处于攀爬状态
    public bool isOn;

    //仅用于观察、调试参数
    public bool heroGround;

    //仅用于观察 绳索Y参数
    public float ropeY;

    //攀爬速度
    public float speed = 2f;

    //攀爬达到的单向平台
    private SingleDirCollider singleDirCollider;
    private int triggerCount = 0; //可能会有多个触发检测到平台,需要用计数形式来检测 是否完全离开平台

    //一个控制是否忽略角色与平台碰撞的控制器(攀爬开始 检测到平台会立刻忽略碰撞 防止角色和平台发生碰撞, 以及结束攀爬时立即恢复碰撞 让玩家站在上面)
    private HeroColliderController heroColliderController;

    //仅用于观测数据 调试 : 上次是否触碰到单向平台 并恰好离开了平台 标记
    public bool lastIsTouchSingleDirPlatform;

    void Start()
    {
        heroKnight = GetComponent<HeroKnight>();
        heroColliderController = GetComponent<HeroColliderController>();
    }

    public float InputY
    {
        get
        {
            return Input.GetAxis("Vertical");
        }
    }

    private void Update()
    {
        heroGround = heroKnight.IsGrounded();
        if (isOn)
        {
            //攀爬移动
            float inputY = InputY;
            heroKnight.MoveY(inputY * speed * Time.deltaTime);
            //Debug.Log("攀爬:" + (inputY * speed * Time.deltaTime) + ", posY:" + (heroKnight.transform.position.y + 0.062f));

            bool isGrounds = heroKnight.IsGrounded();

            //1.接触地面 离开攀爬
            //2.接触单向平台 且人物在单向平台之上时 离开攀爬
            bool isTouchGround = isGrounds && !singleDirCollider;
            bool isTouchSingleDirPlatform = isGrounds && singleDirCollider && (singleDirCollider.maxY) <= (heroKnight.transform.position.y + 0.062f);
            if (isTouchGround || isTouchSingleDirPlatform)
            {
                EndClamp();

                //Debug.LogError("攀爬结束 isOn:" + isOn + ", isTouchGround:" + isTouchGround + ", isTouchSingleDirPlatform:" + isTouchSingleDirPlatform
                //    + " singleDirCollider.maxY:" + singleDirCollider?.maxY + ", posY:" + (heroKnight.transform.position.y + 0.062f));
            }
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        //触发检测到平台 持有它
        if (collision.tag == "SingleDirCollider")
        {
            triggerCount++;
            singleDirCollider = collision.transform.GetComponent<SingleDirCollider>();
        }
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        if (isOn && singleDirCollider != null)
        {
            //3.接触单向平台 且人物在单向平台之上时 离开攀爬
            lastIsTouchSingleDirPlatform = (singleDirCollider.maxY) < (heroKnight.transform.position.y + 0.062f);
            //Debug.LogError("离开时: " + (singleDirCollider.maxY) + ", HeroY:" + (heroKnight.transform.position.y + 0.062f));                
            if (lastIsTouchSingleDirPlatform)
            {
                EndClamp();

                //Debug.LogError(">>>>> 3333 攀爬结束 isOn:" + isOn + ", isTouchSingleDirPlatform:" + lastIsTouchSingleDirPlatform
                //    + " singleDirCollider.maxY:" + singleDirCollider?.maxY + ", posY:" + (heroKnight.transform.position.y + 0.062f));
            }
        }

        if (collision.tag == "Rope" && !isOn)
        {
            float inputY = InputY;
            bool hasInput = Mathf.Abs(inputY) > 0;
            bool isGrounds = heroKnight.IsGrounded();
            bool isValidInput = !isGrounds; //非站立地面则允许攀爬
            float heroY = heroKnight.transform.position.y + 0.062f;
            float ropeY = collision.transform.position.y;

            float deltaY = Mathf.Abs(heroY - ropeY); //绳索顶部和英雄底部的差值
            //若站立地面上
            if (hasInput && isGrounds)
            {
                this.ropeY = ropeY;
                if (heroY > ropeY)
                {
                    isValidInput = inputY < 0; //角色位于绳索上方,按↓键才能攀爬
                }
                else
                {
                    isValidInput = inputY > 0; //角色位于绳索下方,按↑键才能攀爬
                }
            }

            //Debug.Log("isValidInput:" + isValidInput + " inputY :"  + inputY + ", heroY:" + heroY + ", ropeY:" + ropeY);

            if (hasInput && isValidInput) //可以检测角色中心点x是否在绳索范围内才进入攀爬状态
            {
                var hero = heroKnight.transform;
                var rope = collision.transform;
                int dir = (inputY > 0 ? 0 : -1); //按↑时不需要偏移Y值,按↓时进行偏移
                //Debug.LogError("攀爬开始 heroY:" + heroY + ", ropeY:" + ropeY + ", inputY:" + inputY + ", isGrounds:" + isGrounds);

                //当角色从绳索顶部往下爬时,需要增加一个Y轴偏移 使得角色底部位于绳索顶部之下 避免发生临界点问题
                //(避免:开始攀爬 又会检测到物体在绳索上而结束攀爬的情况)
                float offsetY = 0f;
                if (heroY > ropeY)
                {
                    offsetY = -deltaY - 0.01f;
                }

                hero.position = new Vector3(rope.position.x, hero.position.y + offsetY, 0);
                isOn = true;
                heroKnight.isClamp = true;
                heroKnight.BlockGravity(true); //禁用重力影响
                if (singleDirCollider != null)
                {
                    heroColliderController.IgnoreCollision(singleDirCollider.GetComponent<BoxCollider2D>()); //主动忽略碰撞体
                }

                //heroKnight.MoveY(inputY * speed * Time.deltaTime);

                //Debug.LogError("攀爬开始 isOn:" + isOn + ", heroY:" + (hero.position.y + 0.062f) + ", dir * deltaY:" + dir * deltaY);
            }
        }

        //Debug.Log("heroKnight.transform.position.y + 0.062f:" + (heroKnight.transform.position.y + 0.062f));
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.tag == "SingleDirCollider" && singleDirCollider != null && collision.gameObject == singleDirCollider.gameObject)
        {
            triggerCount--;
            if (triggerCount <= 0)
            {
                if (isOn)
                {
                    //2.接触单向平台 且人物在单向平台之上时 离开攀爬
                    lastIsTouchSingleDirPlatform = (singleDirCollider.maxY) <= (heroKnight.transform.position.y + 0.062f);
                    //Debug.LogError("离开时: " + (singleDirCollider.maxY) + ", HeroY:" + (heroKnight.transform.position.y + 0.062f));                
                    if (lastIsTouchSingleDirPlatform)
                    {
                        EndClamp();

                        //Debug.LogError(">>>>> 2222 攀爬结束 isOn:" + isOn + ", isTouchSingleDirPlatform:" + lastIsTouchSingleDirPlatform
                        //    + " singleDirCollider.maxY:" + singleDirCollider?.maxY + ", posY:" + (heroKnight.transform.position.y + 0.062f));
                    }
                }

                singleDirCollider = null;
            }
        }

        //还有其他的情况 退出攀爬状态的,比如攀爬中允许跳出去之类的 就还要写类似计数形式的 检测是否完全离开了绳索,再结束攀爬状态isOn = false
        //if (collision.tag == "Rope")
        //{
        //    isOn = false;
        //}
    }

    /// <summary>
    /// 结束攀爬
    /// </summary>
    private void EndClamp()
    {
        isOn = false;
        heroKnight.isClamp = false;
        heroKnight.BlockGravity(false); //恢复重力影响
        if (heroColliderController != null)
        {
            heroColliderController.ResumeCollider(); //恢复碰撞体
        }
    }
}

Sensor_HeroKnight.cs 角色触发器

主要是忽略对Rope绳索层的检测,否则角色会认为绳索是地面或障碍物。

using UnityEngine;
using System.Collections;

public class Sensor_HeroKnight : MonoBehaviour
{

    public int m_ColCount = 0;

    public float m_DisableTimer;

    public bool m_State;

    private void OnEnable()
    {
        m_ColCount = 0;
    }

    public bool State()
    {
        if (m_DisableTimer > 0)
        {
            m_State = false;
            return m_State;
        }
        m_State = m_ColCount > 0;
        return m_State;
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.tag != "Rope")
        {
            m_ColCount++;
        }
    }

    void OnTriggerExit2D(Collider2D other)
    {
        if (other.tag != "Rope")
        {
            m_ColCount--;
        }
    }

    void Update()
    {
        m_DisableTimer -= Time.deltaTime;
    }

    public void Disable(float duration)
    {
        m_DisableTimer = duration;
    }
}

HeroKnight.cs 角色类

如下是部分代码新增isClamp是否攀爬中标记,Move和Roll判定要加上非攀爬中才允许进行,新增MoveY(攀爬时上下移动使用)、BlockGravity(禁用重力影响 攀爬时会禁用)、IsGrounded(获取是否真正在地面上)

    public bool isClamp;

        // Move
        if (!m_rolling && !isClamp )
            m_body2d.velocity = new Vector2(inputX * m_speed, m_body2d.velocity.y);

        // Roll
        else if (Input.GetKeyDown("left shift") && !m_rolling && !isClamp)
        {
            m_rolling = true;
            m_animator.SetTrigger("Roll");
            m_body2d.velocity = new Vector2(m_facingDirection * m_rollForce, m_body2d.velocity.y);
        }



    public void MoveY(float y)
    {
        m_body2d.MovePosition(new Vector2(transform.position.x, transform.position.y + y));
    }

    public void BlockGravity(bool isStatic)
    {
        float oldScale = m_body2d.gravityScale;
        float newScale = isStatic ? 0f : 1f;
        if (Mathf.Abs(oldScale - newScale) > 0.0001)
        {
            m_body2d.gravityScale = newScale;
            m_body2d.velocity = new Vector2(0, 0);
        }
    }

    public bool IsGrounded() { return m_groundSensor.State(); }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker搭建redis集群(三主三从)

本篇文章不包含理论解释&#xff0c;直接开始集群&#xff08;三主三从&#xff09;搭建 环境 centos7 docker 26.1.4 redis latest &#xff08;7.4.2&#xff09; 服务器搭建以及环境配置 请查看本系列前几篇博客 默认已搭建好三个虚拟机并安装配置好docker 相关博客&#xf…

物联网智能项目之——智能家居项目的实现!

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网智能项目之——智能家居项目…

Deep Seek R1本地化部署

目录 说明 一、下载ollama 二、在ollama官网下载模型 三、使用 后记 说明 操作系统&#xff1a;win10 使用工具&#xff1a;ollama 一、下载ollama 从官网下载ollama&#xff1a; ollama默认安装在C盘&#xff0c;具体位置为C:\Users\用户名\AppData\Local\Programs\O…

跟李沐学AI:视频生成类论文精读(Movie Gen、HunyuanVideo)

Movie Gen&#xff1a;A Cast of Media Foundation Models 简介 Movie Gen是Meta公司提出的一系列内容生成模型&#xff0c;包含了 3.2.1 预训练数据 Movie Gen采用大约 100M 的视频-文本对和 1B 的图片-文本对进行预训练。 图片-文本对的预训练流程与Meta提出的 Emu: Enh…

Java---入门基础篇(上)

前言 本片文章主要讲了刚学Java的一些基础内容,例如注释,标识符,数据类型和变量,运算符,还有逻辑控制等,记录的很详细,带你从简单的知识点再到练习题.如果学习了c语言的小伙伴会发现,这篇文章的内容和c语言大致相同. 而在下一篇文章里,我会讲解方法和数组的使用,也是Java中基础…

3、C#基于.net framework的应用开发实战编程 - 实现(三、三) - 编程手把手系列文章...

三、 实现&#xff1b; 三&#xff0e;三、编写应用程序&#xff1b; 此文主要是实现应用的主要编码工作。 1、 分层&#xff1b; 此例子主要分为UI、Helper、DAL等层。UI负责便签的界面显示&#xff1b;Helper主要是链接UI和数据库操作的中间层&#xff1b;DAL为对数据库的操…

Go学习:类型转换需注意的点 以及 类型别名

目录 1. 类型转换 2. 类型别名 1. 类型转换 在从前的学习中&#xff0c;知道布尔bool类型变量只有两种值true或false&#xff0c;C/C、Python、JAVA等编程语言中&#xff0c;如果将布尔类型bool变量转换为整型int变量&#xff0c;通常采用 “0为假&#xff0c;非0为真”的方…

爬虫基础(四)线程 和 进程 及相关知识点

目录 一、线程和进程 &#xff08;1&#xff09;进程 &#xff08;2&#xff09;线程 &#xff08;3&#xff09;区别 二、串行、并发、并行 &#xff08;1&#xff09;串行 &#xff08;2&#xff09;并行 &#xff08;3&#xff09;并发 三、爬虫中的线程和进程 &am…

V103开发笔记1-20250113

2025-01-13 一、应用方向分析 应用项目&#xff1a; PCBFLY无人机项目&#xff08;包括飞控和手持遥控器&#xff09;&#xff1b; 分析移植项目&#xff0c;应用外设资源包括&#xff1a; GPIO, PWM,USART,GPIO模拟I2C/SPI, ADC,DMA,USB等&#xff1b; 二、移植项目的基本…

AAPM:基于大型语言模型代理的资产定价模型,夏普比率提高9.6%

“AAPM: Large Language Model Agent-based Asset Pricing Models” 论文地址&#xff1a;https://arxiv.org/pdf/2409.17266v1 Github地址&#xff1a;https://github.com/chengjunyan1/AAPM 摘要 这篇文章介绍了一种利用LLM代理的资产定价模型&#xff08;AAPM&#xff09;…

新版231普通阿里滑块 自动化和逆向实现 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向过程 补环境逆向 部分补环境 …

Autosar-Os是怎么运行的?(时间保护)

写在前面&#xff1a; 入行一段时间了&#xff0c;基于个人理解整理一些东西&#xff0c;如有错误&#xff0c;欢迎各位大佬评论区指正&#xff01;&#xff01;&#xff01; 1.功能概述 AUTOSAR OS 的四大可定制类型凸显了时间保护&#xff08;Timing Protection&#xff09;…

vue框架技术相关概述以及前端框架整合

vue框架技术概述及前端框架整合 1 node.js 介绍&#xff1a;什么是node.js Node.js就是运行在服务端的JavaScript。 Node.js是一个事件驱动I/O服务端JavaScript环境&#xff0c;基于Google的V8引擎。 作用 1 运行java需要安装JDK&#xff0c;而Node.js是JavaScript的运行环…

玩转大语言模型——使用langchain和Ollama本地部署大语言模型

系列文章目录 玩转大语言模型——使用langchain和Ollama本地部署大语言模型 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用GraphRAGOllama构建知识图谱 玩转大语言模型——完美解决Gra…

亚博microros小车-原生ubuntu支持系列:15 激光雷达巡逻

一 TF坐标转换 ros2 -5.1 坐标变化工具介绍_ros怎么发布坐标变化-CSDN博客 ros2笔记-5.3 C中地图坐标系变换_c变换坐标系-CSDN博客 header:stamp:sec: 1737893911nanosec: 912000000frame_id: odom_frame child_frame_id: base_footprint pose:pose:position:x: 0.053831271…

C++并发编程指南06

文章目录 4.4 简化代码与同步工具同步工具作为构建块 4.4.1 使用Future的函数化编程函数化编程简介C支持函数化编程 快速排序 - FP模式快速排序串行版快速排序并行版 spawn_task函数结论快速排序 - 串行版快速排序 - 并行版spawn_task函数使用 spawn_task 实现并行快速排序详细…

ios swift画中画技术尝试

继上篇&#xff1a;iOS swift 后台运行应用尝试失败-CSDN博客 为什么想到画中画&#xff0c;起初是看到后台模式里有一个picture in picture&#xff0c;去了解了后发现这个就是小窗口视频播放&#xff0c;方便用户执行多任务。看小窗口视频的同时&#xff0c;可以作其他的事情…

C++,STL 六大组件:容器、迭代器、算法、函数对象、适配器、分配器

文章目录 引言一、容器&#xff08;Containers&#xff09;主要分类 二、迭代器&#xff08;Iterators&#xff09;三、算法&#xff08;Algorithms&#xff09;四、函数对象&#xff08;Functors&#xff09;五、适配器&#xff08;Adapters&#xff09;六、分配器&#xff08…

STM32项目分享:智能鱼缸

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 PCB图 五、程序设计 六、实验效果 七、包含内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; STM32智能鱼缸/水族箱 &#xff08;资料分享见文末…

基于MinIO的对象存储增删改查

MinIO是一个高性能的分布式对象存储服务。Python的minio库可操作MinIO&#xff0c;包括创建/列出存储桶、上传/下载/删除文件及列出文件。 查看帮助信息 minio.exe --help minio.exe server --help …