当前位置: 首页 > news >正文

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十七集:制作第二个BOSS燥郁的毛里克

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、制作游戏第二个BOSS燥郁的毛里克
    • 1.导入素材和制作相关动画
    • 1.5处理玩家受到战吼相关行为逻辑处理
    • 2.制作相应的行为控制和生命系统管理
    • 3.制作战斗场景和战斗门
    • 4.制作BOSS死亡行为
  • 总结


前言

         hello大家好久没见,之所以隔了这么久才更新并不是因为我又放弃了这个项目,而是接下来要制作的工作太忙碌了,每次我都花了很长的时间解决完一个部分,然后就没力气打开CSDN写文章就直接睡觉去了,现在终于有时间整理下我这半个月都做了什么内容

        废话少说,接下来我将介绍我做的第二个BOSS苍蝇之母,因为我上一期制作的苍蝇之母反响还不错,大晚上的还有一百来人在看,那么直接乘胜追击制作第二个BOSS燥郁的毛里克。

        另外,我的Github已经更新了,想要查看最新的内容话请到我的Github主页下载工程吧:

GitHub - ForestDango/Hollow-Knight-Demo: A new Hollow Knight Demo after 2 years!


一、制作游戏第二个BOSS燥郁的毛里克

1.导入素材和制作相关动画 

还是先制作好tk2dsprite和tk2dspriteanimator吧:

  

可能你已经注意到了,提供的tk2dsprite有毛里克的不同躯干,没错,我们要做的就是类似于骨骼动画一样的,每一个部分处理对应部分的行为,在这里我们分为四个部分:分别是左右手臂,嘴巴,身体,先来制作隐藏状态下的动画:

  

  

  

   

然后是手臂:

  

  

   

   身体方面的动画:

  

   

头部动画:

     

还有就是作为整体的跳跃动画:

  

  

作为整体的喷射子弹动画:

  

  

 作为整体的战吼动画:

  

死亡动画;

  1.5处理玩家受到战吼相关行为逻辑处理

        可能你看到这标题不知道我想表达什么,其实这也是我自己打这个BOSS的时候突然发现的,就是玩家在面临强大的BOSS时,会被BOSS的气场给震住,被迫失去控制的朝向BOSS,然后BOSS发射阵阵战吼冲击波,其实你看到下图小骑士的动画你就明白是什么了:

然后给小骑士一个playmakerFSM叫“Roar Lock”来实现相关行为:

 

初始阶段找到特效文件夹和里面需要的特效:

 

 通过Tag名字叫 Roar的判断是否进入下一个状态

 判断是否允许进入Roar行为:

玩家锁定状态下,发送事件,同时RelinquishControl取消控制以及AffectedByGravity受到重力影响,停止播放其他动画,使玩家朝向敌人

 自定义playmakerFSM脚本:

using System;
using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Logic)][Tooltip("Tests if all the given Bool Variables are are equal to thier Bool States.")]public class BoolTestMulti : FsmStateAction{[RequiredField][UIHint(UIHint.Variable)][Tooltip("This must be the same number used for Bool States.")]public FsmBool[] boolVariables;[RequiredField][Tooltip("This must be the same number used for Bool Variables.")]public FsmBool[] boolStates;public FsmEvent trueEvent;public FsmEvent falseEvent;[UIHint(UIHint.Variable)]public FsmBool storeResult;public bool everyFrame;public override void Reset(){boolVariables = null;boolStates = null;trueEvent = null;falseEvent = null;storeResult = null;everyFrame = false;}public override void OnEnter(){DoAllTrue();if (!everyFrame){Finish();}}public override void OnUpdate(){DoAllTrue();}private void DoAllTrue(){if (boolVariables.Length == 0 || boolStates.Length == 0){return;}if (boolVariables.Length != boolStates.Length){return;}bool flag = true;for (int i = 0; i < boolVariables.Length; i++){if(boolVariables[i].Value != boolStates[i].Value){flag = false;break;}}storeResult.Value = flag;if (flag){Fsm.Event(trueEvent);return;}Fsm.Event(falseEvent);}}
}

看看是否要翻转玩家X方向:

 处理粒子系统相关:

判断玩家是否在地面:

在空中锁定:

在地面被锁定:

等待发送ROAR EXIT事件:玩家重新获得输入和动画控制

取消粒子播放效果:回到Detect状态中。

2.制作相应的行为控制和生命系统管理

终于来到我们最爱的处理BOSS相应行为的时候,但在此之前还是把该要的组件导入来:

然后再来看看它有什么子对象,首先自然是它的身体部件了,首先介绍头部:

然后是左右臂:

 

还有一个子对象Attack Range,给左右两只手检测玩家是否进入攻击范围的:

 右手同理:

接着是身体部分:

Boss的警戒范围:Alert Range New

 然后是一些粒子系统:

喷射效果:

 一个简单的playmakerFSM,叫你PLAY的时候你再play:

 我们先来看看毛里克的头部是怎么控制行为的,其实也很简单,就是不停的喷子弹就完事了,

 这个特别说明的变量是喷射速度,你可以根据情况自己调:

初始化阶段就是获得玩家,获得自己孩子和父母 

初始化完整后就进入待苏醒阶段:

苏醒阶段:设置攻击间隔为0.3-0.6秒之间 

 喷射准备阶段:播放动画,等一下会

检测玩家位置:

开喷! 

这里有个预制体叫做Shot Mawlek No Drip,也就是毛里克头部发射的子弹,

记得添加DamageHero.cs脚本,这样它才能伤害到玩家 

这里有一个新脚本叫:EnemyBullet.cs,代码段如下:主要分为Trigger碰到玩家,以及Collision碰到Terrian层级的墙壁和地面的行为

using System.Collections;
using UnityEngine;[RequireComponent(typeof(Rigidbody2D))]
public class EnemyBullet : MonoBehaviour
{public float scaleMin = 1.15f;public float scaleMax = 1.45f;private float scale;[Space]public float stretchFactor = 1.2f;public float stretchMinX = 0.75f;public float stretchMaxY = 1.75f;[Space]public AudioSource audioSourcePrefab;public AudioEvent impactSound;private bool active;private Rigidbody2D body;private tk2dSpriteAnimator anim;private Collider2D col;private void Awake(){body = GetComponent<Rigidbody2D>();anim = GetComponent<tk2dSpriteAnimator>();col = GetComponent<Collider2D>();}private void OnEnable(){active = true;scale = Random.Range(scaleMin, scaleMax);col.enabled = true;body.isKinematic = false;body.velocity = Vector2.zero;body.angularVelocity = 0f;anim.Play("Idle");}private void Update(){if (active){float rotation = Random.Range(body.velocity.y,body.velocity.x) * 57.295776f;transform.SetRotation2D(rotation);float num = 1f - body.velocity.magnitude * stretchFactor * 0.01f;float num2 = 1f + body.velocity.magnitude * stretchFactor * 0.01f;if (num2 < stretchMinX){num2 = stretchMinX;}if (num > stretchMaxY){num = stretchMaxY;}num *= scale;num2 *= scale;transform.localScale = new Vector3(num2, num, transform.localScale.z);}}private void OnCollisionEnter2D(Collision2D collision){if (active){active = false;StartCoroutine(Collision(collision.GetSafeContact().Normal, true));}}private void OnTriggerEnter2D(Collider2D collision){if(active && collision.tag == "HeroBox"){active = false;StartCoroutine(Collision(Vector2.zero, false));}}public void OrbitShieldHit(Transform shield){if (active){active = false;Vector2 normal = transform.position - shield.position;normal.Normalize();StartCoroutine(Collision(normal, true));}}private IEnumerator Collision(Vector2 normal, bool doRotation){transform.localScale = new Vector3(scale, scale, transform.localScale.z);body.isKinematic = true;body.velocity = Vector2.zero;body.angularVelocity = 0f;tk2dSpriteAnimationClip impactAnim = anim.GetClipByName("Impact");anim.Play(impactAnim);if (!doRotation || (normal.y >= 0.75f && Mathf.Abs(normal.x) < 0.5f)){transform.SetRotation2D(0f);}else if (normal.y <= 0.75f && Mathf.Abs(normal.x) < 0.5f){transform.SetRotation2D(180f);}else if (normal.x >= 0.75f && Mathf.Abs(normal.y) < 0.5f){transform.SetRotation2D(270f);}else if (normal.x <= 0.75f && Mathf.Abs(normal.y) < 0.5f){transform.SetRotation2D(90f);}impactSound.SpawnAndPlayOneShot(audioSourcePrefab, transform.position);yield return null;col.enabled = false;yield return new WaitForSeconds((impactAnim.frames.Length - 1) / impactAnim.fps);Destroy(gameObject);//TODO:}}

我们把它的tk2dSprite和tk2dSpriteAnimator完成一下吧:

最后再加个子对象,给它一点亮光:

然后我们来处理它的身体Dummy行为:没啥好说的,就是攻击的时候身体flash发光一下

所以它需要SpriteFlash.cs脚本。

然后是它的左右手行为处理:这里我以左手为例

 

我们通过SetProperty开启左右手臂的MeshRenderer

 在待苏醒阶段我们肯定要关闭它的Collider2d,否则你待苏醒阶段突然给玩家扣了一滴血玩家肯定要喷你的

 判断是否进入攻击距离:

 准备攻击:

攻击阶段:这时候就到了开启Collider2d的时候了

 然后再攻击冷却阶段再关上collider2d

 准备下一次攻击,回到Idle状态。

然后右手臂也同理,我就不贴出来了。

处理完各个部分的行为逻辑,接下来就是整体的逻辑行为处理了:

 

看到下面这两个变量你可能会好奇,我头部不是已经设置了Shot Speed了吗,怎么这里还有,这是因为头部的那个只能一次喷一颗子弹,而这个是作为整体开大招喷半个屏幕的子弹的,两者还是有差别的。

初始化阶段:找自己,找孩子,停止自己移动,播放动画 

休眠阶段:播放动画,如果是神居里的BOSS,不用等WAKE事件直接苏醒,否则就要等到Alert Range New发送WAKE事件 

回到Alert Range New,和上一期讲到的一样,还是等到Trigger2d检测到后发送WAKE事件给父对象

 

 苏醒阶段:给Battle Scene发送事件START,假身Dummy播放动画

 

苏醒跳跃阶段: 给正Y轴一个向上的速度

 苏醒在空中阶段:改变Z轴大小,检测是否落地面

 

 苏醒落地阶段:

 

 介绍BOSS阶段,但我还没做到这里,所以直接跳过

 苏醒战吼阶段:向小骑士发送事件ROAR ENTER,设置Roar Object为自己

战吼结束阶段:向小骑士发送事件ROAR EXIT

音乐起:

相关代码如下:

using System;
using UnityEngine;
using UnityEngine.Audio;[CreateAssetMenu(fileName = "MusicCue", menuName = "Hollow Knight/Music Cue", order = 1000)]
public class MusicCue : ScriptableObject
{[SerializeField] private string originalMusicEventName;[SerializeField] private int originalMusicTrackNumber;[SerializeField] private AudioMixerSnapshot snapshot;[SerializeField][ArrayForEnum(typeof(MusicChannels))]private MusicCue.MusicChannelInfo[] channelInfos;[SerializeField] private MusicCue.Alternative[] alternatives;public string OriginalMusicEventName{get{return originalMusicEventName;}}public int OriginalMusicTrackNumber{get{return originalMusicTrackNumber;}}public AudioMixerSnapshot Snapshot{get{return snapshot;}}public MusicChannelInfo GetChanelInfo(MusicChannels channel){if (channel < MusicChannels.Main || channel >= (MusicChannels)channelInfos.Length){return null;}return channelInfos[(int)channel];}public MusicCue ResolveAlternatives(PlayerData playerData){if (alternatives != null){int i = 0;while (i < alternatives.Length){MusicCue.Alternative alternative = alternatives[i];if (playerData.GetBool(alternative.PlayerDataBoolKey)){MusicCue cue = alternative.Cue;if (!(cue != null)){return null;}return cue.ResolveAlternatives(playerData);}else{i++;}}}return this;}[Serializable]public class MusicChannelInfo{[SerializeField] private AudioClip clip;[SerializeField] private MusicChannelSync sync;public AudioClip Clip{get{return clip;}}public bool IsEnabled{get{return clip != null;}}public bool IsSyncRequired{get{if(sync == MusicChannelSync.Implicit){return clip != null;}return sync == MusicChannelSync.ExplicitOn;}}}[Serializable]public struct Alternative{public string PlayerDataBoolKey;public MusicCue Cue;}
}

 

using UnityEngine;
using System;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Audio)][ActionTarget(typeof(MusicCue), "musicCue", false)][Tooltip("Plays music cues.")]public class ApplyMusicCue : FsmStateAction{[Tooltip("Music cue to play.")][ObjectType(typeof(MusicCue))]public FsmObject musicCue;[Tooltip("Delay before starting transition")]public FsmFloat delayTime;[Tooltip("Transition duration.")]public FsmFloat transitionTime;public override void Reset(){musicCue = null;delayTime = 0f;transitionTime = 0f;}public override void OnEnter(){MusicCue x = musicCue.Value as MusicCue;GameManager instance = GameManager.instance;if (!(x == null)){if (instance == null){Debug.LogErrorFormat(Owner, "Failed to play music cue, because the game manager is not ready", Array.Empty<object>());}else{instance.AudioManager.ApplyMusicCue(x, delayTime.Value, transitionTime.Value, false);}}Finish();}}}

然后假身利用完了就设置为空白的动画 ,此时就从整体变成各个分部,自身设置为Body Idle动画

等个2到3秒钟准备开大招:

那么到底要到什么时候才能进入开大招阶段呢?你总不可能一边左右手攻击一边飞起来吧,所以我打算让它们三分之二同时满足条件,也就是active同时激活的时候才进入开大招阶段

关闭头部collider,同时经典二选一:

作为整体拥有两种攻击行为,一是吐半个屏幕的子弹,还有一种是跳到玩家头上,这里我先介绍前一种:

又是判断是否重复执行同一种攻击行为多次,和判断玩家位置

设置好假身大小朝向:

喷半个屏幕行为:这里的SLEEP事件是发送给是自己的其他分体部件的playmakerFSM上的,就是当整体在执行行为的时候其他部分是不能动的 

 

喷射子弹 

大招结束后的冷却时间:

这里我给了四分之一的概率是否重复释放大招 

重复执行大招:

否则的话回到Start状态重新  WAKE身体的各个部件,然后从此整体又变成了各个部件 

然后再来看看跳跃攻击行为:

判断玩家方向:

超级大跳行为状态: 

在空中行为状态:Collision判断是否落地

落地行为状态:

然后还要回到起跳点的位置: 

然后就是二段跳阶段:我们在前面记录的第一次起跳前x方向的位置,然后我们就故技重施重新调回去就好了,别忘了来点粒子效果和音效啥的

至此我们完成了一个完整的BOSS相应的行为控制

3.制作战斗场景和战斗门

我觉得战斗门就不用我多说了,因为我上一期已经讲过了,直接预制体一拖然后设置好位置就OK,

我们重点是制作战斗场景,首先创建同名,设置好tag,创建playmakerFSM名字叫:Battle Control

 

 这个heart piece就是游戏的面具碎片,由于我还没做到所以先空着不管:

如果是已经击败过的BOSS就进入activate状态,生成面具碎片以及效果boss房的camera lock

战斗开始后,设置好Camera lock和敌人数量为1,还有就是关门

战斗结束后设置为激活状态,等个五秒多:

 

 

结束后记得开门 

 

4.制作BOSS死亡行为

 最后我们还要制作BOSS的尸体:

首先我们来制作BOSS尸体爆炸后变成一堆躯干:

还有一些躯干掉落后的橙汁蒸汽之类的:

我们先隐藏好上述的躯干,回到主体当中,需要rb2d和box2d:同是别忘了设置好layer

它的playmaker也很简单,跟我们上期讲到苍蝇之母一样:

告诉battle Scene战斗结束的事件:BATTLE END

初始化生成好波澜:

冒气Steam阶段和上面的差不多:

 

 

准备阶段: 

 

 最后阶段:boss尸体爆炸,生成躯干和果冻:

 

果冻就是这个orange Glob:

 我们先把它的tk2dsprite和tk2dspriteanimator做好:

 

 

 这里为什么有两个collider2d呢?因为我要一个来判断果冻是否落地,另一个判断玩家是否用骨钉攻击它:

这个是用来判断有无敌人踩到它:踩到了就执行动画wobbleAnim

 攻击它后的效果:

 我们来写一个脚本名字就叫:

GlobControl.cs

using System.Collections;
using UnityEngine;[RequireComponent(typeof(Collider2D))]
public class GlobControl : MonoBehaviour
{public Renderer rend;[Space]public float minScale = 0.6f;public float maxScale = 1.6f;[Space]public string landAnim = "Glob Land";public string wobbleAnim = "Glob Wobble";public string breakAnim = "Glob Break";[Space]public AudioSource audioPlayerPrefab;public AudioEvent breakSound;public Color bloodColorOverride = new Color(1f, 0.537f, 0.188f);[Space]public GameObject splatChild;[Space]public Collider2D groundCollider;private bool landed;private bool broken;private tk2dSpriteAnimator anim;private void Awake(){anim = GetComponent<tk2dSpriteAnimator>();}private void OnEnable(){float num = Random.Range(minScale, maxScale);transform.localScale = new Vector3(num, num, 1f);if (splatChild){splatChild.SetActive(false);}landed = false;broken = false;}private void Start(){CollisionEnterEvent collision = GetComponent<CollisionEnterEvent>();if (collision){collision.OnCollisionEnteredDirectional += delegate (CollisionEnterEvent.Direction direction, Collision2D col){if (!landed){if(direction == CollisionEnterEvent.Direction.Bottom){landed = true;collision.doCollisionStay = false;if (CheckForGround()) //检测是否碰到地面{anim.Play(landAnim);return;}StartCoroutine(Break());return;}else{collision.doCollisionStay = true;}}};}TriggerEnterEvent componentInChildren = GetComponentInChildren<TriggerEnterEvent>();if (componentInChildren){componentInChildren.OnTriggerEntered += delegate (Collider2D col, GameObject sender){if (!landed || broken){return;}if (col.gameObject.layer == LayerMask.NameToLayer("Enemies")){anim.Play(wobbleAnim); //检测如果敌人碰到了执行动画wobbleAnim}};}}private void OnTriggerEnter2D(Collider2D col){if (!landed || broken){return;}if (col.tag == "Nail Attack") //如果是骨钉攻击,执行break函数{StartCoroutine(Break());return;}if (col.tag == "HeroBox") //如果是玩家碰到了执行wobble的动画{anim.Play(wobbleAnim);}}private IEnumerator Break(){broken = true;breakSound.SpawnAndPlayOneShot(audioPlayerPrefab, transform.position);GlobalPrefabDefaults.Instance.SpawnBlood(transform.position, 4, 5, 5f, 20f, 80f, 100f, new Color?(bloodColorOverride));if (splatChild){splatChild.SetActive(true); //生成一些效果和子对象splatChild}yield return anim.PlayAnimWait(breakAnim);if (rend){rend.enabled = false;}yield break;}private bool CheckForGround(){if (!groundCollider){return true;}Vector2 vector = groundCollider.bounds.min;Vector2 vector2 = groundCollider.bounds.max;float num = vector2.y - vector.y;vector.y = vector2.y;vector.x += 0.1f;vector2.x -= 0.1f;RaycastHit2D raycastHit2D = Physics2D.Raycast(vector, Vector2.down, num + 0.25f, LayerMask.GetMask("Terrain"));RaycastHit2D raycastHit2D2 = Physics2D.Raycast(vector2, Vector2.down, num + 0.25f, LayerMask.GetMask("Terrain"));return raycastHit2D.collider != null && raycastHit2D2.collider != null;}}

别忘了回到编辑器添加好参数:


总结

        至此我们完成了制作的第二个BOSS燥郁的毛里克,就在这里展示一下成果吧:

(顺便提一下我把血量设置太高了没打过,只能自己打二周目了)

这里我的spit speed设置太低了,因为我后面改了一下游戏的重力大小,所以喷不远,你们记得根据实际情况来填参数: 

右边的爪子也没有问题:

 

 

这里没生成躯干是因为不知道为什么我的相机不会渲染的sprite-default的material了,所以没办法只能换个material:

OK终于做完一期了,刚好最近有空整理一下最近做的内容。

 

相关文章:

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十七集:制作第二个BOSS燥郁的毛里克

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作游戏第二个BOSS燥郁的毛里克 1.导入素材和制作相关动画1.5处理玩家受到战吼相关行为逻辑处理2.制作相应的行为控制和生命系统管理3.制作战斗场景和战斗…...

深度解析阿里的Sentinel

1、前言 这是《Spring Cloud 进阶》专栏的第五篇文章&#xff0c;这篇文章介绍一下阿里开源的流量防卫兵Sentinel&#xff0c;一款非常优秀的开源项目&#xff0c;经过近10年的双十一的考验&#xff0c;非常成熟的一款产品。 文章目录如下&#xff1a; 2、什么是sentinel&…...

Linux系统-日志轮询(logrotate)

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 这是Linux进阶部分的最后一大章。讲完这一章以后&#xff0c;我们Linux进阶部分讲完以后&#xff0c;我们的Linux操作部分就…...

机器学习在时间序列预测中的应用与实现——以电力负荷预测为例(附代码)

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 随着数据采集技术的发展&#xff0c;时间序列数据在各个领域中的应用越来越广泛。时间序列预测旨在基于过去的时间数据来…...

白杨SEO:百度在降低个人备案类网站搜索关键词排名和流量?怎样应对?【参考】

很久没有写百度或者网站这块内容了&#xff0c;一是因为做百度网站朋友越来越少&#xff0c;不管是个人还是企业&#xff1b;二是百度上用户搜索与百度给到网站的流量都越来越少。 为什么想到今天又来写这个呢&#xff1f;因为上个月有个朋友来咨询我说网站百度排名全没了&…...

前端实现json动画(附带示例)

前端实现json动画&#xff08;附带示例&#xff09; 使用lottie制作动画。1.json动画2.实现效果3.git仓库4.运行5.json动画天堂6.代码7. 经常使用的方法 使用lottie制作动画。 1.json动画 废话不多说&#xff0c;直接看效果图2.实现效果 3.git仓库 https://gitee.com/chaiach…...

AI 写作(一):开启创作新纪元(1/10)

一、AI 写作&#xff1a;重塑创作格局 在当今数字化高速发展的时代&#xff0c;AI 写作正以惊人的速度重塑着创作格局。AI 写作在现代社会中占据着举足轻重的地位&#xff0c;发挥着不可替代的作用。 随着信息的爆炸式增长&#xff0c;人们对于内容的需求日益旺盛。AI 写作能够…...

C#-类:索引器

索引器作用&#xff1a;可以让我们以中括号的形式访问自定义类中的元素。 规则自己定&#xff0c;访问时和数组一样 适用于&#xff0c;在类中有数组变量时使用&#xff0c;可以方便的访问、进行逻辑处理 可以重载&#xff0c;结构体也支持索引器 一&#xff1a;索引器的语法…...

Neo4j Cypher WHERE子句详解 - 初学者指南

Neo4j Cypher WHERE子句详解 - 初学者指南 前言1. WHERE子句基础1.1 WHERE子句的本质1.2 示例数据 2. 基本用法2.1 节点属性过滤2.2 关系属性过滤 3. 高级过滤技巧3.1 字符串匹配3.2 正则表达式3.3 属性存在性检查 4. 列表和范围操作4.1 IN操作符4.2 范围查询 5. 空值处理5.1 默…...

【CSS】标准怪异盒模型

概念 CSS 盒模型本质上是一个盒子&#xff0c;盒子包裹着HTML 元素&#xff0c;盒子由四个属性组成&#xff0c;从内到外分别是&#xff1a;content 内容、padding 内填充、border 边框、外边距 margin 盒模型的分类 W3C 盒子模型(标准盒模型) IE 盒子模型(怪异盒模型) 两种…...

栈详解

目录 栈栈的概念及结构栈的实现数组栈的实现数组栈功能的实现栈的初始化void STInit(ST* pst)初始化情况一初始化情况二 代码栈的插入void STPush(ST* pst, STDataType x)代码 栈的删除void STPop(ST* pst)代码 栈获取数据STDataType STTop(ST* pst)代码 判断栈是否为空bool ST…...

硬盘 <-> CPU, CPU <-> GPU 数据传输速度

1. 硬盘 <-> CPU 数据传输速度 import time import os# 定义文件大小和测试文件路径 file_size 1 * 1024 * 1024 * 100 # 100 MB 的文件大小 file_path "test_file.bin"# 创建一个测试文件并测量写入速度 def test_write_speed():data os.urandom(file_si…...

数据编排与ETL有什么关系?

数据编排作为近期比较有热度的一个话题&#xff0c;讨论度比较高&#xff0c;同时数据编排的出现也暗示着数字化进程的自动化发展。在谈及数据编排时&#xff0c;通常也会谈到ETL&#xff0c;这两个东西有相似点也有不同点。 数据编排和ETL&#xff08;提取、转换、加载&#x…...

来了解一下!!!——React

React 是一个用于构建用户界面的 JavaScript 库&#xff0c;特别适合用于创建单页面应用程序&#xff08;SPA&#xff09;。它由 Facebook 维护&#xff0c;并且拥有一个活跃的社区&#xff0c;这使得 React 成为了目前最流行的前端框架之一。以下是关于 React 的一些重要信息和…...

用vite创建项目

一. vite vue2 1. 全局安装 create-vite npm install -g create-vite 2. 创建项目 进入你想要创建项目的文件夹下 打开 CMD 用 JavaScript create-vite my-vue2-project --template vue 若用 TypeScript 则 create-vite my-vue2-project --template vue-ts 这里的 …...

json-server的使用(根据json数据一键生成接口)

一.使用目的 在前端开发初期&#xff0c;后端 API 可能还未完成&#xff0c;json-server 可以快速创建模拟的 RESTful API&#xff0c;帮助前端开发者进行开发和测试。 二.安装 npm install json-server //局部安装npm i json-server -g //全局安装 三.使用教程 1.准备一…...

半波正弦信号的FFT变换

目录 Hello&#xff0c; 大家好&#xff0c;这一期我们谈谈半波正弦信号的FFT变化长什么样子。本文硬件使用GFARM02硬件模块[1]&#xff0c;文章最后有其淘宝链接。核心器件为STM32F103RCT6&#xff0c;为Cortex-M3核&#xff0c;采用的CMSIS版本为CMSIS_5-5.6.0。 如图1所示&…...

Python数据分析NumPy和pandas(二十三、数据清洗与预处理之五:pandas的分类类型数据)

pandas的分类类型数据&#xff08;Categorical Data&#xff09; 这次学习使用Categorical Data&#xff0c;在某些 pandas 操作中使用分类类型能实现更好的性能和减少内存使用。另外还学习一些工具&#xff0c;这些工具有助于在统计和机器学习应用程序中使用分类数据。 一.背…...

redis源码系列--(二)--multi/exec/eval命令执行流程

本文主要记录multi/exec、eval、redis执行lua脚本的源码流程 redis在exec之前&#xff0c;所有queued的命令是没有执行的&#xff0c;&#xff01;&#xff01;&#xff01;在执行时会通过检测client是否被打上CLIENT_DIRTY_CAS标记来判断[watch后,exec时]时间段内是否有key被…...

【力扣打卡系列】移动零(双指针)

坚持按题型打卡&刷&梳理力扣算法题系列&#xff0c;语言为go&#xff0c;Day19 移动零&#xff08;双指针&#xff09; 题目描述 解题思路 p和q同时从起点移动&#xff0c;p每次都&#xff0c;q仅在交换时&#xff0c;p遇到非零数时与p值交换&#xff01;&#xff01;…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...