【用unity实现100个游戏之12】unity制作一个俯视角2DRPG《类星露谷物语》资源收集游戏demo

文章目录

  • 前言
  • 加快编辑器运行速度
  • 素材
    • (1)场景人物
    • (2)工具
  • 一、人物移动和动画切换
  • 二、走路灰尘粒子效果
    • 探究
    • 实现
  • 三、树木排序设计
    • 方法一
    • 方法二
  • 四、绘制拿工具的角色动画
  • 五、砍树实现
  • 六、存储拾取物品
    • 引入Unity 的可序列化字典类
  • 七、实现靠近收获物品自动吸附
  • 八、树木被砍掉的粒子效果
  • 九、新增更多可收集物
  • 十、更多工具切换
  • 十一、扩展
  • 源码
  • 完结

前言

采集收集生存类游戏一直是我的最爱,今天就来用unity制作一个俯视角2DRPG类星露谷物语资源收集游戏

先来看看最终效果
在这里插入图片描述
游戏现已经上线至itch网站,欢迎大家游玩支持
https://xiangyu.itch.io/survive

加快编辑器运行速度

修改项目配置
在这里插入图片描述
这样,运行时我们就不需要再等待很久了

素材

(1)场景人物

https://cypor.itch.io/12x12-rpg-tileset
在这里插入图片描述

(2)工具

https://dantepixels.itch.io/tools-asset-16x16
在这里插入图片描述

一、人物移动和动画切换

这个直接功能实现我之前炸弹人那期文章已经说过了,直接抄答案就行了,这里就不再重复介绍了,具体实现可以看文章:
【用unity实现100个游戏之8】用Unity制作一个炸弹人游戏
在这里插入图片描述

最终效果(这里用TileMap简单绘制了一下地图,TileMap的使用可以看我主页之前的文章)
在这里插入图片描述

在这里插入图片描述

二、走路灰尘粒子效果

探究

要实现走路灰尘效果,Unity的粒子系统(Particle System)中有属性RateOverDistance:根据移动距离发射粒子,不移动时不发射。恰好可满足当前需求

实际使用时发现,不管怎么移动都不发射粒子,但RateOverTime(随时间推移发射粒子)的功能是正常的

解决方案
粒子系统有一属性:EmitterVelocity(发射器速度模式),它有2种模式

Transform:通过Transform中Position的变化计算粒子系统的移动速度
Rigidbody:将刚体(若有)的速度作为粒子系统的移动速度

看了上述解释即可想到,若EmitterVelocity设置为Rigidbody模式,当该粒子系统没有刚体时,系统会认为该发射器是不动的,因此移动速度为0,因此移动距离为0:因此RateOverDistance不会发射粒子

所以将EmitterVelocity(发射器速度模式)设置为Transform(变换)即可

实现

素材图片
在这里插入图片描述
材质
在这里插入图片描述

完整粒子系统配置
在这里插入图片描述

在这里插入图片描述
效果
在这里插入图片描述

三、树木排序设计

我们希望实现人物走到树前,人物遮挡树木,当人物走到树后,树又遮挡玩家

如果我们直接修改图层排序方式肯定是不行的,玩家要么直接被遮挡,要么直接遮挡树木

当然你可以通过代码的方式,动态的修改人物图层排序值,当时太麻烦了,这里我们就不探讨了

方法一

最简单的方法就是将树叶和树根分开,树叶图层排序比人物高,树根图层排序比人物低,当然这样绘制会比较麻烦一些
在这里插入图片描述
效果
在这里插入图片描述

方法二

使用透视排序。也就是“伪造”透视图。根据直觉,玩家希望角色在立方体前面时首先绘制角色,而角色在立方体后面时最后绘制角色。

如果用更技术性的语言来说,你需要做的是指示 Unity 根据游戏对象的 y 坐标来绘制游戏对象。屏幕上位置较低的游戏对象(y 坐标较小)应在屏幕上位置较高的游戏对象(y 坐标较大)之后绘制。这样将使位置较低的对象显示在上层。

要指示 Unity 根据游戏对象的 y 坐标来绘制游戏对象,请执行以下操作

  • 选择 Edit > Project Settings。

  • 在左侧类别菜单中,单击 Graphics

  • 在 Camera Settings 中,找到 Transparency Sort Mode (透明度排序模式)字段。此字段决定了精灵的绘制顺序。使用下拉菜单将此设置从 Default 更改为 Custom Axis(自定义轴),修改Transparency Sort Axis(透明排序轴)为(0,1,0),告诉Unity y轴绘制精灵

在这里插入图片描述

  • 找到树木的 Sprite Sort Point (Sprite 排序点)字段。目前,此字段设置为 Center,这意味着会使用精灵的中心点来决定这个游戏对象应该在另一个游戏对象的前面还是后面。将 Sprite Sort Point (Sprite 排序点)更改为 Pivot(轴心)
    注意:记得树木图层排序顺序要和主角人物设置为一样
    在这里插入图片描述
  • 修改树木图片的轴心位置到树木根部
    在这里插入图片描述
    这样就实现了人物在树木轴心下面,先绘制树木,人物在轴心以上,先绘制角色
    在这里插入图片描述

四、绘制拿工具的角色动画

记得配置好动画后,修改为横定曲线,让动画过渡更加丝滑
在这里插入图片描述
效果
在这里插入图片描述
其他配置同理,并加入攻击动画和代码控制切换

if(Input.GetKeyDown(KeyCode.F)){animator.SetBool("isAttack", true);
}
if(Input.GetKeyUp(KeyCode.F)){animator.SetBool("isAttack", false);
}

动画控制器
在这里插入图片描述

最终效果
在这里插入图片描述

五、砍树实现

给树木添加代码,我已经加了详细注释就不过多解释了,其中使用了DOTween实现生成资源的弹出动画,不懂DOTween的小伙伴可以看我之前的文章

using UnityEngine;
using DG.Tweening;public class TreeController : MonoBehaviour
{public GameObject woodPrefab;   // 木头预制体public int minWoodCount = 2;    // 随机掉落的最小木头数量public int maxWoodCount = 3;    // 随机掉落的最大木头数量public int maxAttackCount = 3;  // 最大攻击次数public int maxCreateCount = 3;  // 最大生成物品次数public float maxOffsetDistance = 1f;  // 随机偏移的最大距离private int currentCreateCount; // 生成物品次数private int currentAttackCount; // 当前攻击次数void Start(){currentAttackCount = 0;currentCreateCount = 0;}private void OnTriggerEnter2D(Collider2D other) {//碰到带Axe标签物体if(other.CompareTag("Axe")){//攻击三次生成物品if(currentAttackCount >= maxAttackCount){TakeDamage();currentAttackCount = 0;}currentAttackCount ++;}}public void TakeDamage(){// 每次受到攻击时,生成随机数量的木头int woodCount = Random.Range(minWoodCount, maxWoodCount + 1);for (int i = 0; i < woodCount; i++){Vector3 randomOffset = new Vector3(Random.Range(-maxOffsetDistance, maxOffsetDistance), Random.Range(-maxOffsetDistance, maxOffsetDistance), 0f);GameObject wood = Instantiate(woodPrefab, transform.position + randomOffset, Quaternion.identity);// 使用DOJump方法实现物体的弹跳wood.transform.DOJump(wood.transform.position + new Vector3(randomOffset.x, randomOffset.y, 0f), 2, 1, 1).SetEase(Ease.OutSine);}currentCreateCount++;// 如果生成物品次数达到最大值,则销毁木头并重置生成物品次数if (currentCreateCount >= maxCreateCount){Destroy(gameObject);currentCreateCount = 0;}}
}

效果
在这里插入图片描述

六、存储拾取物品

使用ScriptableObject定义物品

using UnityEngine;[CreateAssetMenu(fileName = "Resource", menuName = "GathererTopDownRPG/Resource")]
public class Resource : ScriptableObject
{[field: SerializeField] public string DisplayName { get; private set; }[field: SerializeField] public Sprite Icon { get; private set; }[field: SerializeField] public string Description { get; private set; }[field: SerializeField] public float Value { get; private set; }
}

新建ScriptableObject物品,配置参数
在这里插入图片描述
物品脚本

using UnityEngine;public class ResourcePickup : MonoBehaviour
{[field: SerializeField] public Resource ResourceType { get; private set; }
}

木头挂载脚本,并配置对应的ScriptableObject
在这里插入图片描述

引入Unity 的可序列化字典类

Unity 无法序列化标准词典。这意味着它们不会在检查器中显示或编辑,
也不会在启动时实例化。一个经典的解决方法是将键和值存储在单独的数组中,并在启动时构造字典。

我们使用gitthub大佬的源码即可,此项目提供了一个通用字典类及其自定义属性抽屉来解决此问题。
源码地址:https://github.com/azixMcAze/Unity-SerializableDictionary

你可以选择下载源码,也可以直接复制我下面的代码,我把主要代码提出来了
SerializableDictionary.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using UnityEngine;public abstract class SerializableDictionaryBase
{public abstract class Storage {}protected class Dictionary<TKey, TValue> : System.Collections.Generic.Dictionary<TKey, TValue>{public Dictionary() {}public Dictionary(IDictionary<TKey, TValue> dict) : base(dict) {}public Dictionary(SerializationInfo info, StreamingContext context) : base(info, context) {}}
}[Serializable]
public abstract class SerializableDictionaryBase<TKey, TValue, TValueStorage> : SerializableDictionaryBase, IDictionary<TKey, TValue>, IDictionary, ISerializationCallbackReceiver, IDeserializationCallback, ISerializable
{Dictionary<TKey, TValue> m_dict;[SerializeField]TKey[] m_keys;[SerializeField]TValueStorage[] m_values;public SerializableDictionaryBase(){m_dict = new Dictionary<TKey, TValue>();}public SerializableDictionaryBase(IDictionary<TKey, TValue> dict){	m_dict = new Dictionary<TKey, TValue>(dict);}protected abstract void SetValue(TValueStorage[] storage, int i, TValue value);protected abstract TValue GetValue(TValueStorage[] storage, int i);public void CopyFrom(IDictionary<TKey, TValue> dict){m_dict.Clear();foreach (var kvp in dict){m_dict[kvp.Key] = kvp.Value;}}public void OnAfterDeserialize(){if(m_keys != null && m_values != null && m_keys.Length == m_values.Length){m_dict.Clear();int n = m_keys.Length;for(int i = 0; i < n; ++i){m_dict[m_keys[i]] = GetValue(m_values, i);}m_keys = null;m_values = null;}}public void OnBeforeSerialize(){int n = m_dict.Count;m_keys = new TKey[n];m_values = new TValueStorage[n];int i = 0;foreach(var kvp in m_dict){m_keys[i] = kvp.Key;SetValue(m_values, i, kvp.Value);++i;}}#region IDictionary<TKey, TValue>public ICollection<TKey> Keys {	get { return ((IDictionary<TKey, TValue>)m_dict).Keys; } }public ICollection<TValue> Values { get { return ((IDictionary<TKey, TValue>)m_dict).Values; } }public int Count { get { return ((IDictionary<TKey, TValue>)m_dict).Count; } }public bool IsReadOnly { get { return ((IDictionary<TKey, TValue>)m_dict).IsReadOnly; } }public TValue this[TKey key]{get { return ((IDictionary<TKey, TValue>)m_dict)[key]; }set { ((IDictionary<TKey, TValue>)m_dict)[key] = value; }}public void Add(TKey key, TValue value){((IDictionary<TKey, TValue>)m_dict).Add(key, value);}public bool ContainsKey(TKey key){return ((IDictionary<TKey, TValue>)m_dict).ContainsKey(key);}public bool Remove(TKey key){return ((IDictionary<TKey, TValue>)m_dict).Remove(key);}public bool TryGetValue(TKey key, out TValue value){return ((IDictionary<TKey, TValue>)m_dict).TryGetValue(key, out value);}public void Add(KeyValuePair<TKey, TValue> item){((IDictionary<TKey, TValue>)m_dict).Add(item);}public void Clear(){((IDictionary<TKey, TValue>)m_dict).Clear();}public bool Contains(KeyValuePair<TKey, TValue> item){return ((IDictionary<TKey, TValue>)m_dict).Contains(item);}public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex){((IDictionary<TKey, TValue>)m_dict).CopyTo(array, arrayIndex);}public bool Remove(KeyValuePair<TKey, TValue> item){return ((IDictionary<TKey, TValue>)m_dict).Remove(item);}public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator(){return ((IDictionary<TKey, TValue>)m_dict).GetEnumerator();}IEnumerator IEnumerable.GetEnumerator(){return ((IDictionary<TKey, TValue>)m_dict).GetEnumerator();}#endregion#region IDictionarypublic bool IsFixedSize { get { return ((IDictionary)m_dict).IsFixedSize; } }ICollection IDictionary.Keys { get { return ((IDictionary)m_dict).Keys; } }ICollection IDictionary.Values { get { return ((IDictionary)m_dict).Values; } }public bool IsSynchronized { get { return ((IDictionary)m_dict).IsSynchronized; } }public object SyncRoot { get { return ((IDictionary)m_dict).SyncRoot; } }public object this[object key]{get { return ((IDictionary)m_dict)[key]; }set { ((IDictionary)m_dict)[key] = value; }}public void Add(object key, object value){((IDictionary)m_dict).Add(key, value);}public bool Contains(object key){return ((IDictionary)m_dict).Contains(key);}IDictionaryEnumerator IDictionary.GetEnumerator(){return ((IDictionary)m_dict).GetEnumerator();}public void Remove(object key){((IDictionary)m_dict).Remove(key);}public void CopyTo(Array array, int index){((IDictionary)m_dict).CopyTo(array, index);}#endregion#region IDeserializationCallbackpublic void OnDeserialization(object sender){((IDeserializationCallback)m_dict).OnDeserialization(sender);}#endregion#region ISerializableprotected SerializableDictionaryBase(SerializationInfo info, StreamingContext context) {m_dict = new Dictionary<TKey, TValue>(info, context);}public void GetObjectData(SerializationInfo info, StreamingContext context){((ISerializable)m_dict).GetObjectData(info, context);}#endregion
}public static class SerializableDictionary
{public class Storage<T> : SerializableDictionaryBase.Storage{public T data;}
}[Serializable]
public class SerializableDictionary<TKey, TValue> : SerializableDictionaryBase<TKey, TValue, TValue>
{public SerializableDictionary() {}public SerializableDictionary(IDictionary<TKey, TValue> dict) : base(dict) {}protected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info, context) {}protected override TValue GetValue(TValue[] storage, int i){return storage[i];}protected override void SetValue(TValue[] storage, int i, TValue value){storage[i] = value;}
}[Serializable]
public class SerializableDictionary<TKey, TValue, TValueStorage> : SerializableDictionaryBase<TKey, TValue, TValueStorage> where TValueStorage : SerializableDictionary.Storage<TValue>, new()
{public SerializableDictionary() {}public SerializableDictionary(IDictionary<TKey, TValue> dict) : base(dict) {}protected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info, context) {}protected override TValue GetValue(TValueStorage[] storage, int i){return storage[i].data;}protected override void SetValue(TValueStorage[] storage, int i, TValue value){storage[i] = new TValueStorage();storage[i].data = value;}
}

库存基类

TryGetValue 方法会尝试从字典中获取指定键 type 对应的值。如果找到了该键,它会将对应的值赋值给 currentCount 变量,并返回 true。

using UnityEngine;public class Inventory : MonoBehaviour
{[field: SerializeField] private SerializableDictionary <Resource, int> Resources { get; set;}//<summary>// 查找并返回字典中某个资源的数量///</summary>//<param name="type">要检查的资源类型</param>//<returns>如果有资源则返回其数量,否则返回0</returns>public int GetResourceCount(Resource type){if (Resources.TryGetValue(type, out int currentCount))return currentCount;elsereturn 0;}/// <summary>/// 向字典中添加资源/// </summary>/// <param name="type">要添加的资源类型</param>/// <param name="count">要添加的数量</param>/// <returns>成功添加的数量</returns>public int AddResources(Resource type, int count){if (Resources.TryGetValue(type, out int currentCount)){return Resources[type] += count;}else{Resources.Add(type, count);return count;}}
}

拾取物品
在这里插入图片描述
拾取物品代码,挂载在人物身上

using UnityEngine;public class PickupResources : MonoBehaviour
{[field: SerializeField] public Inventory Inventory { get; private set; }private void OnTriggerEnter2D(Collider2D collision){ResourcePickup pickup = collision.gameObject.GetComponent<ResourcePickup>();if (pickup){Inventory.AddResources(pickup.ResourceType, 1);Destroy(pickup.gameObject);}}
}

在这里插入图片描述
运行效果
在这里插入图片描述

检测是否入库,数量是否增加
在这里插入图片描述

七、实现靠近收获物品自动吸附

新增两个节点,一个为吸附范围,一个为拾取物品范围,同时去除原本角色的PickupResources脚本挂载
在这里插入图片描述
在这里插入图片描述

编写吸附脚本PickupGravity

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickupGravity : MonoBehaviour
{public float GravitySpeed = 5f;private List<ResourcePickup> _nearbyResources = new();private void FixedUpdate(){foreach (ResourcePickup pickup in _nearbyResources){Vector2 directionToCenter = (transform.position - pickup.transform.position).normalized;pickup.transform.Translate(directionToCenter * GravitySpeed * Time.fixedDeltaTime);}}private void OnTriggerEnter2D(Collider2D collision){ResourcePickup pickup = collision.gameObject.GetComponent<ResourcePickup>();if (pickup) _nearbyResources.Add(pickup);}private void OnTriggerExit2D(Collider2D collision){ResourcePickup pickup = collision.gameObject.GetComponent<ResourcePickup>();if (pickup) _nearbyResources.Remove(pickup);}
}

也可以使用DOtween实现,会更加简单

using UnityEngine;
using DG.Tweening;
public class PickupGravity : MonoBehaviour
{public float speed = 0.6f;private void OnTriggerEnter2D(Collider2D collision){ResourcePickup pickup = collision.gameObject.GetComponent<ResourcePickup>();if (pickup){pickup.transform.DOMove(transform.position, speed);}}
}

挂载脚本
在这里插入图片描述
在这里插入图片描述
运行效果
在这里插入图片描述

八、树木被砍掉的粒子效果

粒子效果配置,材质和颜色选择自己想要的就行
在这里插入图片描述

在这里插入图片描述
代码调用

public GameObject cutDownParticleSystem;//粒子效果//生成特效
Instantiate(cutDownParticleSystem, transform.position, Quaternion.identity);

效果
在这里插入图片描述

九、新增更多可收集物

直接按前面的树木生成预制体变体
在这里插入图片描述
修改相应的配置即可,最终效果
在这里插入图片描述

十、更多工具切换

新增ScriptableObject Tool类,定义不同类型的工具

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;[CreateAssetMenu(fileName = "Tool", menuName = "GathererTopDownRPG/Tool")]
public class Tool : ScriptableObject
{[field: SerializeField] public string DisplayName { get; private set; }[field: SerializeField] public Sprite sprite { get; private set; }[field: SerializeField] public Sprite Icon { get; private set; }[field: SerializeField] public string Description { get; private set; }[field: SerializeField] public ToolType toolType { get; private set; }[field: SerializeField] public int minCount { get; private set; } = 1;//随机掉落的最小资源数量[field: SerializeField] public int maxCount { get; private set; } = 1;//随机掉落的最大资源数量
}// 在此添加其他工具类型
public enum ToolType
{Axe,//斧头Pickaxe,//镐子
}

在这里插入图片描述
新增工具代码,挂载在工具节点上

public class ToolController : MonoBehaviour
{public Tool tool;
}

修改可破坏物(树,石头)的代码,及TreeController脚本

public ToolType toolType;private void OnTriggerEnter2D(Collider2D other)
{//碰到带Axe标签物体if (other.CompareTag("Axe")){//只有对应的工具才可以if (other.GetComponent<ToolController>().tool.toolType == toolType){//。。。TakeDamage(other.GetComponent<ToolController>().tool);//。。。}else{Debug.Log("工具不对");}}
}public void TakeDamage(Tool tool)
{int minWoodCount = tool.minCount;    // 随机掉落的最小木头数量int maxWoodCount = tool.maxCount;    // 随机掉落的最大木头数量
}

ps:我这里只配置了不同武器的 随机掉落的数量,你可以将其他数据也进行不同的工具配置,如最大攻击次数,最大生成物品次数

页面绘制不同武器的切换按钮
在这里插入图片描述
切换工具按钮脚本,脚本挂载各个按钮上即可

using UnityEngine;
using UnityEngine.UI;//切换工具
public class SwitchTool : MonoBehaviour
{public Tool tool;private ToolController toolController;private void Start(){toolController = FindObjectOfType<PlayerController>().GetComponentInChildren<ToolController>();//修改当前按钮icon图片GetComponent<Image>().sprite = tool.Icon;//绑定点击事件GetComponent<Button>().onClick.AddListener(ChangeTool);}//切换工具public void ChangeTool(){if (toolController != null){if(toolController.GetComponent<SpriteRenderer>().sprite != tool.sprite){toolController.tool = tool;//修改工具角色手拿工具图片toolController.GetComponent<SpriteRenderer>().sprite = tool.sprite;}else{toolController.tool = null;toolController.GetComponent<SpriteRenderer>().sprite = null;}}else{Debug.LogWarning("没找到目标ToolController组件");}}
}

最终效果
在这里插入图片描述

十一、扩展

音乐音效系统
背包系统
经验血量系统
制作系统,不同等级可以解锁不同品质的工具
种植系统
建造系统
钓鱼、烹饪系统
天气、四季变化系统
任务系统
打怪系统
种子、商城系统

后续大家可以自行扩展,这里就不过多赘述了。至于后续是否继续完善开发,就看大家想不想看了,点赞越多更新越快哦!

源码

整理好后我会放上来

完结

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

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

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

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

相关文章

视频编解码器H.264和H265有什么区别?

对于大型视频文件来说&#xff0c;视频编解码器至关重要&#xff0c;它可以将文件压缩为较小的尺寸&#xff0c;从而可以更轻松地存储和加快传输速度。而两种最常用的编解码器是H.264和H.265&#xff0c;那么它们两者之间有什么区别&#xff0c;哪一个更好呢&#xff1f; 1. 什…

手摸手图解 CodeWhisperer 的安装使用

CodeWhisperer 是亚⻢逊出品的一款基于机器学习的通用代码生成器&#xff0c;可实时提供代码建议。 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术&#xff0c;观点…

JCEF中js与java交互、js与java相互调用

jcef中js与java相互调用&#xff0c;java与js相互调用&#xff0c;chrome与java相互调用&#xff0c;java与chrome相互调用、jcef与java相互调用 前提&#xff1a;https://blog.csdn.net/weixin_44480167/article/details/133170970&#xff08;java内嵌浏览器CEF-JAVA、jcef、…

车辆检测:An Efficient Wide-Range Pseudo-3D Vehicle Detection Using A Single Camera

论文作者&#xff1a;Zhupeng Ye,Yinqi Li,Zejian Yuan 作者单位&#xff1a;Xian Jiaotong University 论文链接&#xff1a;http://arxiv.org/abs/2309.08369v1 项目链接&#xff1a;https://www.youtube.com/watch?v1gk1PmsQ5Q8 内容简介&#xff1a; 1&#xff09;方…

【数据结构】二叉树之堆的实现

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;数据结构 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、二叉树的顺序结构 &#x1f4d2;1.1顺序存储 &#x1f4d2;1.2堆的性质…

【LeetCode75】第六十二题 多米诺和托米诺平铺

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我一个数字n&#xff0c;表示我们有2*n大小的地板需要铺。 我们拥有两种瓷砖&#xff0c;一种的长度为2的多米诺&#xff0c;另一…

CFCA证书 申请 流程(一)

跳过科普&#xff0c;可直接进入申请&#x1f449;https://blog.csdn.net/Ximerr/article/details/133169391 CFCA证书 CFCA证书是指由中国金融认证中心颁发的证书&#xff0c;包括普通数字证书、服务器数字证书和预植证书等&#xff0c;目前&#xff0c;各大银行和金融机构都…

STM32F407 串口使用DMA方式通信

DMA的原理&#xff0c;就是利用寄存器方式进行读写&#xff0c;这样的好处就是相对于中断触发&#xff08;往往一个字节字节的就中断一次&#xff09;&#xff0c;CPU中断次数大大降少&#xff0c;提高了效率&#xff0c;但也影响了实时性。总体来说&#xff0c;对于一般的应用…

滚动轴承 调心球轴承 外形尺寸

声明 本文是学习GB-T 281-2013 滚动轴承 调心球轴承 外形尺寸. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了符合 GB/T 273.3—1999 的调心球轴承及带紧定套的调心球轴承(以下简称轴承)的 外形尺寸。 本标准适用于调心球轴承…

【AI语言大模型】文心一言功能使用介绍

一、前言 文心一言是一个知识增强的大语言模型&#xff0c;基于飞桨深度学习平台和文心知识增强大模型&#xff0c;持续从海量数据和大规模知识中融合学习具备知识增强、检索增强和对话增强的技术特色。 最近收到百度旗下产品【文心一言】的产品&#xff0c;抱着试一试的心态体…

【教程】视频汇聚/视频监控管理平台EasyCVR录像存储功能如何优化?具体步骤是什么?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。视频监控系统EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、…

API(十一) 获取openresty编译信息

一 ngx.config 说明&#xff1a; 不常用,了解即可 ngx.config.subsystem 说明&#xff1a; 用的四层还是七层代理 ngx.config.debug 说明&#xff1a; 返回的是boolean类型, openresty rpm安装一般没有 --with-debug编译选项对比&#xff1a; nginx rpm 安装一般携带 --wi…

Gateway网关

网关GateWay 官方文档&#xff1a;https://docs.spring.io/spring-cloud-gateway/docs/3.1.2/reference/html/#gateway-how-it-works 核心概念 路由: 网关的核心数据结构&#xff0c;定义了网关如何处理请求. 一条路由信息包含路由的唯一标识ID,目的地URI, 一组断言&#xf…

Vue3 封装 element-plus 图标选择器

一、实现效果 二、实现步骤 2.1. 全局注册 icon 组件 // main.ts import App from ./App.vue; import { createApp } from vue; import * as ElementPlusIconsVue from element-plus/icons-vueconst app createApp(App);// 全局挂载和注册 element-plus 的所有 icon app.con…

基于Java+SpringBoot+Vue物流管理小程序系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

什么是领域驱动设计(DDD): 领域驱动设计和实践如何做

引言 软件系统面向对象的设计思想可谓历史悠久&#xff0c;20 世纪 70 年代的 Smalltalk 可以说是面向对象语言的经典&#xff0c;直到今天我们依然将这门语言视为面向对象语言的基础。随着编程语言和技术的发展&#xff0c;各种语言特性层出不穷&#xff0c;面向对象是大部分…

大数据 Hive 数据仓库介绍

目录 一、​​数据仓库概念 二、场景案例&#xff1a;数据仓库为何而来&#xff1f; 2.1 操作型记录的保存 2.2 分析型决策的制定 2.3 OLTP 环境开展分析可行吗&#xff1f; 2.4 数据仓库的构建 三、数据仓库主要特征 3.1 面向主题性&#xff08;Subject-Orient…

【Web开发 | Django】数据库分流之道:探索Django多数据库路由最佳实践

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

iOS线上闪退问题解决方案

iOS线上闪退问题的收集工具是关键&#xff0c;它们可以帮助你及时发现和解决应用程序中的崩溃问题。以下是一些常用的iOS线上闪退问题收集工具及其使用方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合…

绘图系统六:动态三维展示

文章目录 时间轴单帧跳转动图绘制函数接口优化 &#x1f4c8;一 三维绘图系统 &#x1f4c8;二 多图绘制系统&#x1f4c8;三 坐 标 轴 定 制&#x1f4c8;四 定制绘图风格 &#x1f4c8;五 数据生成导入源码地址 Python打造动态绘图系统 时间轴 三维并不是人类理解的极限&am…