[Unity Demo]从零开始制作空洞骑士Hollow Knight第十七集:制作第一个BOSS苍蝇之母

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

文章目录

  • 前言
  • 一、战斗场景Battle Scene相关逻辑处理
    • 1.防止玩家走出战斗场景的门
    • 2.制作一个简单的战斗场景
  • 二、制作游戏第一个BOSS苍蝇之母
    • 1.导入素材和制作相关动画
    • 2.制作相应的行为控制和生命系统管理
    • 3.制作BOSS尸体Corpse
  • 总结


前言

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

        废话少说,接下来我将介绍我做的第一个BOSS苍蝇之母,其实你看到这个名字可能想不到对应的是游戏里的哪一个BOSS,没事你往下看就知道了,因为我忘了它叫什么名字了好像叫格鲁兹啥的,那么本期内容分为两个部分:一是制作战斗场景Battle Scene,二是制作游戏第一个BOSS苍蝇之母。

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

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


一、制作战斗场景Battle Scene相关逻辑处理

1.制作防止玩家走出战斗场景的门

        我们在玩空洞骑士的时候会遇到一些遭遇战,除了进入BOSS房间的时候会有这种门的关闭,我们在一些特定关卡也会遇到这种关,所以在介绍第一个BOSS之前,我们先来做这个战斗场景的逻辑处理:

        OK首先我们把门的素材导入tk2dSprtie和tk2dSpriteAnimator当中,

然后我们来先创建好那个特定的场景,每个萌新都知道就是会打两波阿斯匹德的场景:

现在我们做好了场景就该把我们的敌人拖入指定的位置,我们创建一个名字叫Battle Scene的,先给它制作好collider2d,根据设定当玩家进入这个trigger后就相当于进入战斗场景

然后它还有两个子对象Wave1,Wave2分别代表两波攻势

Wave1自然就是场景中央的这只阿斯匹德了:

Wave2则是我们提前生成好放在地图外边的阿斯匹德:

或许你看到上张图就会提问:你怎么创建了一个空的阿斯匹德,原因是在空洞骑士中这里第二波会有两个小蜜蜂看似从场景外飞进来的,说以需要制作从颜色黑色到白色这种视觉效果,而真正的本地则是地图外的这两只阿斯匹德:在完成视觉动画后我们就瞬间让它们进入动画结尾的位置,达到完美的视觉过渡效果:

然后我们先来把门的逻辑处理给完成了:

如图所示做好碰撞箱,音效器,sprite和animator,然后我们还要做一些粒子系统啥的:

​ 

然后制作playmakerFSM来处理相关逻辑行为:

初始阶段这些门肯定都是Opened,不然你关上了玩家咋进去:

Collider关掉,等待Battle Scene发送BG Opened,Closed的事件后切换阶段:

关上第一阶段Close 1:这时候就要打开Collider和播放动画和音效了:

第二阶段: 

等待发送BG OPEN进入Open阶段后:

还有没有延迟的迅速关闭Quick Close和Quick Open的:

直接摧毁:

完成后做好预制体,拖到地图相应的位置,这里我们需要四个门:

2.制作一个简单的战斗场景

然后就到了制作战斗场景Battle  Scene的时候了,还是用我们最爱的playmakerFSM来制作:

 

激活阶段就是说这个战斗场景玩家已经战胜了,就不会执行重复的战斗关卡 了:

检测玩家是否就位:

第一波攻势阶段:Battle Enemies就是我们总共要战胜多少个敌人,然后向我们上面制作的门的playmakerFSM发送BG CLOSE事件,直到 Battle Enemies少了一个也就是第一波敌人清理干净了就进入到下一波

等个1.5秒到下一波:

第二波阶段:向第二波的敌人发送事件SUMMON产生视觉效果,直到Battle Enemies为0就进入下一个阶段

等个2秒中:

取消场景摄像机锁定,设置为激活状态,向门播放事件BG OPEN

怎么样?什么是不是感觉还挺简单的,逻辑也好理解,但是有个疑问,这个battle scene怎么知道battle enemies已经死了呢,这就要留给enemies公共的脚本:也就是HealthManager.cs来实现了:我们在Die函数被调用后定位到battle scene的playmakerFSM,然后找到变量battle enemies给它减一下,这样就实现了:

using System;
using System.Collections;
using HutongGames.PlayMaker;
using UnityEngine;
using UnityEngine.Audio;

public class HealthManager : MonoBehaviour, IHitResponder
{
    [Header("Scene")]
    [SerializeField] private GameObject battleScene;

    public void Die(float? attackDirection, AttackTypes attackType, bool ignoreEvasion)
    {
	if (isDead)
	{ 
	    return;
	}
	if (sprite)
	{
	    sprite.color = Color.white;
	}
	FSMUtility.SendEventToGameObject(gameObject, "ZERO HP", false);
	if (hasSpecialDeath)
	{
	    NonFatalHit(ignoreEvasion);
	    return;
	}
	isDead = true;
	if(damageHero != null)
	{
	    damageHero.damageDealt = 0;
	}
	if(battleScene != null && !notifiedBattleScene)
	{
	    PlayMakerFSM playMakerFSM = FSMUtility.LocateFSM(battleScene, "Battle Control");
	    if(playMakerFSM != null)
	    {
		FsmInt fsmInt = playMakerFSM.FsmVariables.GetFsmInt("Battle Enemies");
		if(fsmInt != null)
		{
		    fsmInt.Value--;
		    notifiedBattleScene = true;
		}
	    }
	}
	if (deathAudioSnapshot != null)
	{
	    deathAudioSnapshot.TransitionTo(6f);
	}
	if (sendKilledTo != null)
	{
	    FSMUtility.SendEventToGameObject(sendKilledTo, "KILLED", false);
	}
	if(attackType == AttackTypes.Splatter)
	{
	    GameCameras.instance.cameraShakeFSM.SendEvent("AverageShake");
	    Debug.LogWarningFormat(this, "Instantiate!", Array.Empty<object>());
	    //TODO:
	    Instantiate<GameObject>(corpseSplatPrefab, transform.position + effectOrigin, Quaternion.identity);
	    if (enemyDeathEffects)
	    {
		enemyDeathEffects.EmitSound();
	    }
	    Destroy(gameObject);
	    return;
	}
	if(attackType != AttackTypes.RuinsWater)
	{
	    float angleMin = megaFlingGeo ? 65 : 80;
	    float angleMax = megaFlingGeo ? 115 : 100;
	    float speedMin = megaFlingGeo ? 30 : 15;
	    float speedMax = megaFlingGeo ? 45 : 30;
	    int num = smallGeoDrops;
	    int num2 = mediumGeoDrops;
	    int num3 = largeGeoDrops;
	    bool flag = false;
	    if(GameManager.instance.playerData.equippedCharm_24 && !GameManager.instance.playerData.brokenCharm_24)
	    {
		num += Mathf.CeilToInt(num * 0.2f);
		num2 += Mathf.CeilToInt(num2 * 0.2f);
		num3 += Mathf.CeilToInt(num3 * 0.2f);
		flag = true;
	    }
	    GameObject[] gameObjects = FlingUtils.SpawnAndFling(new FlingUtils.Config
	    {
		Prefab = smallGeoPrefab,
		AmountMin = num,
		AmountMax = num,
		SpeedMin = speedMin,
		SpeedMax = speedMax,
		AngleMin = angleMin,
		AngleMax = angleMax
	    },base.transform,effectOrigin);
	    if (flag)
	    {
		SetGeoFlashing(gameObjects, smallGeoDrops);
	    }
	    gameObjects = FlingUtils.SpawnAndFling(new FlingUtils.Config
	    {
		Prefab = mediumGeoPrefab,
		AmountMin = num2,
		AmountMax = num2,
		SpeedMin = speedMin,
		SpeedMax = speedMax,
		AngleMin = angleMin,
		AngleMax = angleMax
	    }, transform, effectOrigin);
	    if (flag)
	    {
		SetGeoFlashing(gameObjects, mediumGeoDrops);
	    }
	    gameObjects = FlingUtils.SpawnAndFling(new FlingUtils.Config
	    {
		Prefab = largeGeoPrefab,
		AmountMin = num3,
		AmountMax = num3,
		SpeedMin = speedMin,
		SpeedMax = speedMax,
		AngleMin = angleMin,
		AngleMax = angleMax
	    }, transform, effectOrigin);
	    if (flag)
	    {
		SetGeoFlashing(gameObjects, largeGeoDrops);
	    }
	}
	if (enemyDeathEffects != null)
	{
	    if (attackType == AttackTypes.Generic)
	    {
		enemyDeathEffects.doKillFreeze = false;
	    }
	    enemyDeathEffects.RecieveDeathEvent(attackDirection, deathReset, attackType == AttackTypes.Spell, false);
	}
	SendDeathEvent();
	Destroy(gameObject); //TODO:
    }
}

接下来我们来做第二波攻势的两个阿斯匹德的视觉效果:

 

 

来波位置和Scale 的平替:

效果我会放到总结处,接下来我们直接来个大的,制作我们这个系列的第一个BOSS——苍蝇之母。

二、制作游戏第一个BOSS苍蝇之母

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

我们先来制作tk2dsprite和tk2dspriteanimator,看到这里你应该这个是什么BOSS了

然后就来制作好场景:

动画有点多,我就一张一张贴出来吧,不然可能大伙看不懂什么意思;

然后就到了五件套的时候了:

除此之外,我们还要预先做的一个笼子,把BOSS死后从肚子里生成的7只苍蝇关起来,等BOSS死后生成在BOSS的位置上:

记得给这7只都添加好Battle Scene:

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

首先我们来制作boss睡觉阶段生成的粒子系统Snore:

它的状态也很简单,就是播放粒子系统,让boss在循环播放睡眠动画的时候再设置好粒子系统的emission排放量:

 

 

 看到右上角的ID:47没?我们就在播放Sleep的最后一帧设置排放量为40,否则设置为0.

 再来把它的剩下两个子对象给完成了:

这里还要设置好string变量,就是拿来判断玩家是否在范围内的。 

 

回到主体中,我们来制作boss相应的行为控制,当然还是用playmakerFSM来实现的:

 

初始阶段:除了初始化一些变量外,我们还需要额外判断这是否是神居里面的BOSS,当然我不可能做那么快,所以肯定不会发送这个事件的,但我们先写个雏形:

using HutongGames.PlayMaker;

[ActionCategory("Hollow Knight/GG")]
public class GGCheckIfBossScene : FsmStateAction
{
    public FsmEvent bossSceneEvent;
    public FsmEvent regularEvent;

    public override void Reset()
    {
	bossSceneEvent = null;
	regularEvent = null;
    }

    public override void OnEnter()
    {
	Fsm.Event(regularEvent);
	base.Finish();
    }
}

直到受到角色骨钉伤害TAKE DAMAGE事件才切换到Wake Sound阶段:

这里本来有个介绍敌人名字的叫Area Title预制体,但我还没做到UI所以先放着,向相机发送事件抖动(别急我还没讲到先放着),然后向battle scene发送START事件,播放动画设置初始速度

 下一个阶段就播放Fly动画然后处理音效相关:

先等个2秒多然后就到了决定状态的时候了:

然后就到了二选一攻击行为阶段 了:这个boss有两种攻击方式,分别是冲向玩家charge和上下撞击slam,

我们先来将charge攻击行为:

准备阶段:还是判断是否连续三次使用了这个招式,朝向玩家

冲刺攻击阶段:然后还要检测哪个方向碰到墙或地面的层级了:

 

从被左边碰撞的恢复阶段:播放音效,生成效果,设置反方向的速度,等待0.3秒进入下一个阶段 

其他三个方向都是同理的:

 

恢复结束阶段:

结束攻击阶段:播放会Fly动画播放音效等会回到Buzz状态

再来看看另一个攻击行为Slam上下撞击:还是连续行为判断,然后朝向玩家,获取玩家方向和距离,自己再抖动一小点距离,设置好撞击持续时间slam time等待进入下一个阶段。

自定义playmakerFSM内容:

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Transform)]
    [Tooltip("Jitter an object around using its Transform.")]
    public class ObjectJitter : RigidBody2dActionBase
    {
	[RequiredField]
	[Tooltip("The game object to translate.")]
	public FsmOwnerDefault gameObject;

	[Tooltip("Jitter along x axis.")]
	public FsmFloat x;
	[Tooltip("Jitter along y axis.")]
	public FsmFloat y;
	[Tooltip("Jitter along z axis.")]
	public FsmFloat z;

	[Tooltip("If true, don't jitter around start pos")]
	public FsmBool allowMovement;

	private float startX;
	private float startY;
	private float startZ;

	public override void Reset()
	{
	    gameObject = null;
	    x = new FsmFloat
	    {
		UseVariable = true
	    };
	    y = new FsmFloat
	    {
		UseVariable = true
	    };
	    z = new FsmFloat
	    {
		UseVariable = true
	    };
	}

	public override void OnPreprocess()
	{
	    Fsm.HandleFixedUpdate = true;
	}

	public override void OnEnter()
	{
	    GameObject ownerDefaultTarget = base.Fsm.GetOwnerDefaultTarget(gameObject);
	    if (ownerDefaultTarget == null)
	    {
		return;
	    }
	    startX = ownerDefaultTarget.transform.position.x;
	    startY = ownerDefaultTarget.transform.position.y;
	    startZ = ownerDefaultTarget.transform.position.z;
	}

	public override void OnFixedUpdate()
	{
	    DoTranslate();
	}

	private void DoTranslate()
	{
	    GameObject ownerDefaultTarget = Fsm.GetOwnerDefaultTarget(gameObject);
	    if (ownerDefaultTarget == null)
	    {
		return;
	    }
	    if (allowMovement.Value)
	    {
		ownerDefaultTarget.transform.Translate(Random.Range(-x.Value, x.Value), Random.Range(-y.Value, y.Value), Random.Range(-z.Value, z.Value));
		return;
	    }
	    Vector3 position = new Vector3(startX + Random.Range(-x.Value, x.Value), startY + Random.Range(-y.Value, y.Value), startZ + Random.Range(-z.Value, z.Value));
	    ownerDefaultTarget.transform.position = position;
	}
    }
}

向上发射阶段,也就是第一次撞击是向上还是向下:

向下发射阶段:

还是根据撞击的方向决定下一次撞击是往上还是往下,还有就是到达战斗区域的边缘后转向 

向下撞击:

 

向上撞击: 

 

Slam结束阶段:设置好速度为0,播放动画slam end,减速,等0.几秒回到super end阶段

其实这样下来感觉这个BOSS的难度还没上一期制作的龙牙哥难度高,攻击行为也是两种,唯一不同的是接下来的死亡尸体需要制作了,

3.制作BOSS尸体Corpse

老规矩拖入Coillider,Sprite和animator:

初始阶段先判断好尸体的Scale有没有问题,因为我们是放大了1.25倍的

找到Battle Scene的playmakerFSM的变量Activated,设置为激活状态,证明玩家已经战胜了这一个BOSS 

然后到了生成玩家死亡的特效:

这里为什么我关掉了Set Particle Emission Speed这个行为呢,是因为我才发现这个版本的Unity 不能够在外面通过代码设置粒子系统的speed播放速度,所以只能这样了 

准备进入爆炸阶段: 

超级大爆阶段Blow:

销毁自己: 

然后我们这里生成了第二个尸体预制体就是Corpse Big Fly Burster,这里就是控制生成小苍蝇之类的:

先来看看粒子系统:

 

一个小小的动画;

 

死亡后不断上升的蒸汽粒子系统:

然后是主体,这里需要rigibody2d来落地和检测地面了:

新脚本Corpse.cs来处理玩家死亡相关行为的:

using System;
using System.Collections;
using UnityEngine;

public class Corpse : MonoBehaviour
{
    private States state;

    protected MeshRenderer meshRenderer;
    protected tk2dSprite sprite;
    protected tk2dSpriteAnimator spriteAnimator;
    protected SpriteFlash spriteFlash;
    protected Rigidbody2D body;
    protected Collider2D bodyCollider;
    [SerializeField] protected ParticleSystem corpseFlame;
    [SerializeField] protected ParticleSystem corpseSteam;
    [SerializeField] protected GameObject landEffects;
    [SerializeField] protected AudioSource audioPlayerPrefab;
    [SerializeField] protected GameObject deathWaveInfectedPrefab;
    [SerializeField] protected GameObject spatterOrangePrefab;

    [SerializeField] private AudioEvent startAudio;

    [SerializeField] private bool resetRotaion;
    [SerializeField] private bool massless;
    [SerializeField] private bool instantChunker;
    [SerializeField] private bool breaker;

    private bool noSteam;
    protected bool spellBurn;
    protected bool hitAcid;

    private float landEffectsDelayRemaining;

    private void Awake()
    {
	meshRenderer = GetComponent<MeshRenderer>();
	sprite = GetComponent<tk2dSprite>();
	spriteAnimator = GetComponent<tk2dSpriteAnimator>();
	spriteFlash = GetComponent<SpriteFlash>();
	body = GetComponent<Rigidbody2D>();
	bodyCollider = GetComponent<Collider2D>();
    }

    public void Setup(bool noSteam, bool spellBurn)
    {
	this.noSteam = noSteam;
	this.spellBurn = spellBurn;
    }

    protected virtual void Start()
    {
	startAudio.SpawnAndPlayOneShot(audioPlayerPrefab, transform.position);
	if (resetRotaion)
	{
	    transform.SetRotation2D(0f);
	}
	if(noSteam && corpseSteam != null)
	{
	    corpseSteam.gameObject.SetActive(false);
	}
	if (spellBurn)
	{
	    if(sprite != null)
	    {
		sprite.color = new Color(0.19607843f, 0.19607843f, 0.19607843f, 1f);
	    }
	    if(corpseFlame != null)
	    {
		corpseFlame.Play();
	    }
	}
	if (massless)
	{
	    state = States.DeathAnimation;
	}
	else
	{
	    state = States.InAir;
	    if(spriteAnimator != null)
	    {
		tk2dSpriteAnimationClip clipByName = spriteAnimator.GetClipByName("Death Air");
		if(clipByName != null)
		{
		    spriteAnimator.Play(clipByName);
		}
	    }
	}
	if (instantChunker && !breaker)
	{
	    Land();
	}
	StartCoroutine(DisableFlame());
    }

    protected void Update()
    {
	if(state == States.DeathAnimation)
	{
	    if(spriteAnimator == null || !spriteAnimator.Playing)
	    {
		Complete(true, true);
		return;
	    }
	}
	else if(state == States.InAir)
	{
	    if (transform.position.y < -10f)
	    {
		Complete(true, true);
		return;
	    }
	}
	else if(state == States.PendingLandEffects)
	{
	    landEffectsDelayRemaining -= Time.deltaTime;
	    if(landEffectsDelayRemaining <= 0f)
	    {
		Complete(false,false);
	    }
	}
    }

    private void Complete(bool detachChildren, bool destroyMe)
    {
	    state = States.Complete;
	    enabled = false;
	if (corpseSteam != null)
	{
	    corpseSteam.Stop();
	}
	if (corpseFlame != null)
	{
	    corpseFlame.Stop();
	}
	if (detachChildren)
	{
	    transform.DetachChildren();
	}
	if (destroyMe)
	{
	    Destroy(gameObject);
	}
    }

    protected void OnCollisionEnter2D(Collision2D collision)
    {
	    OnCollision(collision);
    }

    protected void OnCollisionStay2D(Collision2D collision)
    {
	    OnCollision(collision);
    }

    private void OnCollision(Collision2D collision)
    {
	    if(state == States.InAir)
	    {
	        Sweep sweep = new Sweep(bodyCollider, 3, 3, 0.1f);
	        float num;
	        if(sweep.Check(transform.position,0.08f,LayerMask.GetMask("Terrain"),out num))
	        {
		    Land();
	        }
	    }
    }

    private void Land()
    {
	    if (breaker)
	    {

	    }
	else
	{
	    if(spriteAnimator != null && !hitAcid)
	    {
		tk2dSpriteAnimationClip clipByName = spriteAnimator.GetClipByName("Death Land");
		if(clipByName != null)
		{
		    spriteAnimator.Play(clipByName);
		}
	    }
	    landEffectsDelayRemaining = 1f;
	    if(landEffects != null)
	    {
		landEffects.SetActive(true);
	    }
	    state = States.PendingLandEffects;
	    if (!hitAcid)
	    {
		LandEffects();
	    }
	}
    }

    protected virtual void LandEffects()
    {
	
    }

    private IEnumerator DisableFlame()
    {
	    yield return new WaitForSeconds(5f);
	    if (corpseFlame)
	    {
	        corpseFlame.Stop();
	    }
    }

    private enum States
    {
	    NotStarted,
	    InAir,
	    DeathAnimation,
	    Complete,
	    PendingLandEffects
    }
}

然后用playmakerFSM来控制它的相关行为:

首先是变量,原来我上面数错数了,要生成8只小苍蝇

初始化阶段:

生成50块钱(这里我们还没讲到这么快所以先放着)

检测是否碰到地面了:

生成效果和播放动画,苍蝇之母开始挣扎:

进入停止动画阶段:

播放苍蝇之母腹部蠕动阶段:

第二次腹部蠕动:

第三次腹部蠕动:

播放爆炸动画:

这里为什么有个叫CHINESE的事件,没办法为了过审是要这样的,如果是CHINESE build就直接隐藏爆后的尸体:先暂时设置不是CHINESE build

using System;

namespace HutongGames.PlayMaker.Actions
{
    public class CheckIsChineseBuild : FSMUtility.CheckFsmStateAction
    {
	public override bool IsTrue
	{
	    get
	    {
		return false;
	    }
	}
    }
}

生成橙汁: 

爆橙汁阶段: 

激活所有的小苍蝇并设置到boss尸体的位置上: 

最后我们来制作battle scene:

检测知道苍蝇之母向他发送START事件: 

没有音乐:

开始的时候设置好战斗敌人数量和向门发送BG CLOSE,每帧检测是否敌人数量已经小于等于0了

 等个2秒钟,发送开门事件并设置好激活状态。

 然后就是两个门,跟本篇文章上半边的battle gate一模一样直接拿来用然后设置好位置即可


总结

OK我们先来看看上半段的效果:

进入Battle Scene后:

击败第一波敌人后:

突然出现两个敌人:视觉效果

击败两个敌人后:

门就开了

然后再来看看BOSS战:

首先是睡觉阶段:粒子系统播放没问题的

A它一下后:苏醒然后门关上

第一个攻击行为:

第二个攻击行为:

转身:

死亡后直接开爆:

爆金币加死亡落地:

腹部蠕动:

超级大爆阶段:生成小苍蝇

全部battle enemies清理完成后,等2秒门就开了:

至此我们制作了一个反正我比较满意的原版第一个BOSS,它已经是我尽力复刻原版的高度了,喜欢的小伙伴赶紧来github下载demo然后点开Crossroads_04场景去体验一番吧!

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

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

相关文章

C#从零开始学习(GameObject实例)(unity Lab3)

这是书本中第三个unity Lab 在这次实验中,将学习如何使用C#编写代码用unity编写C#代码 GameObject实例 本次将完成的工作 将游戏资产配置在文件夹中创建材质把GameObject变成预制件脚本控制游戏防止球体重叠 将游戏资产配置在文件夹中 Script放代码 Prefabs放预制件 MAteria…

PostgreSQL的学习心得和知识总结(一百五十五)|[performance]优化期间将 WHERE 子句中的 IN VALUES 替换为 ANY

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

RabbitMQ系列学习笔记(八)--发布订阅模式

文章目录 一、发布订阅模式原理二、发布订阅模式实战1、消费者代码2、生产者代码3、查看运行结果 本文参考&#xff1a; 尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmq RabbitMQ 详解 Centos7环境安装Erlang、RabbitMQ详细过程(配图) 一、发布订阅模式原理 在开发过程中&…

大数据治理的核心思想

目录 ​编辑1.1 大数据治理的定义与重要性 1.2 大数据治理的关键要素 1.2.1 数据质量管理 1.2.2 数据安全管理 1.2.3 合规性管理 1.2.4 数据共享与协作 1.2.5 数据驱动的决策 二、对未来趋势的看法 2.1 技术发展趋势 2.1.1 人工智能与机器学习 2.1.2 云计算与边缘计…

Python数据处理工具笔记 - matplotlib, Numpy, Pandas

matplotlib, Numpy, Pandas 由于有很多例子是需要运算后的图表看着更明白一些&#xff0c;很明显csdn不支持 所以用谷歌的Colab(可以理解为白嫖谷歌的云端来运行的jupyter notebook)来展示&#xff1a; Colab链接(需要梯子)&#xff1a;Python数据挖掘 当然如果实在没有梯子&…

算法打卡 Day43(动态规划)-背包问题 + 分割等和子集

文章目录 0-1 背包问题理论基础0-1 背包问题滚动数组Leetcode 416-分割等和子集题目描述解题思路 0-1 背包问题理论基础 0-1 背包一般的题目要求是给定不同重量不同价值的物品&#xff0c;每个物品只有一个&#xff0c;已知背包中最大的负重&#xff0c;求在此限制条件下背包中…

达那福发布新品音致系列:以顶尖降噪技术,开启清晰聆听新篇章

近日&#xff0c;国际知名助听器品牌达那福推出其最新研发的音致系列助听器。该系列产品旨在通过顶尖的声音处理技术&#xff0c;直面助听器市场中普遍存在的挑战——如何在噪声环境中提供清晰的语音辨识。 根据助听器行业协会2022年的调查数据&#xff0c;高达86%的佩戴者认为…

数据结构——二叉树的基本操作及进阶操作

前言 介绍 &#x1f343;数据结构专区&#xff1a;数据结构 参考 该部分知识参考于《数据结构&#xff08;C语言版 第2版&#xff09;》116 ~ 122页 及 《数据结构教程》201 ~ 213页 重点 树的基本实现并不难&#xff0c;重点在于对递归的理解才是树这部分知识带来的最大收…

jmeter学习(8)界面的使用

1、新建test plan 3、 打开文件 4、保存 5、剪切 6、复制 7、粘贴 8、所有线程组展开 9、所有线程组收缩 10、置灰&#xff0c;操作后无法使用 11、执行 13、清空当前线程组结果 14、清空所有线程组结果 15、函数助手 搜索&#xff0c;可以用于搜索某个请求&#x…

Java基于微信小程序的健身小助手打卡预约教学系统(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

使用OpenCV进行视频边缘检测:案例Python版江南style

1. 引言 本文将演示如何使用OpenCV库对视频中的每一帧进行边缘检测&#xff0c;并将结果保存为新的视频文件。边缘检测是一种图像处理技术&#xff0c;它可以帮助我们识别出图像中不同区域之间的边界。在计算机视觉领域&#xff0c;这项技术有着广泛的应用&#xff0c;比如物体…

登录时用户名密码加密传输(包含前后端代码)

页面输入用户名密码登录过程中&#xff0c;如果没有对用户名密码进行加密处理&#xff0c;可能会导致传输过程中数据被窃取&#xff0c;就算使用https协议&#xff0c;在浏览器控制台的Request Payload中也是能直接看到传输的明文&#xff0c;安全感是否还是不足。 大致流程&a…

redis—cluster集群

一&#xff1a;Redis Cluster特点 多主多从&#xff0c;去中心化&#xff1a;从节点作为备用&#xff0c;复制主节点&#xff0c;不做读写操作&#xff0c;不提供服务不支持处理多个key&#xff1a;因为数据分散在多个节点&#xff0c;在数据量大高并发的情况下会影响性能&…

Columns Page “列”页面

“列”页提供了列管理工具&#xff0c;其中包括用于添加和删除列的按钮、显示绑定数据源中字段名称的列表框以及网格列、提供对所选列属性的访问的属性网格。 Columns 页面提供 Column properties &#xff08;列属性&#xff09;、Column options &#xff08;列选项&#xff…

Electron-(三)网页报错处理与请求监听

在前端开发中&#xff0c;Electron 是一个强大的框架&#xff0c;它允许我们使用 Web 技术构建跨平台的桌面应用程序。在开发过程中&#xff0c;及时处理网页报错和监听请求是非常重要的环节。本文将详细介绍 Electron 中网页报错的日志记录、webContents 的监听事件以及如何监…

如何使用JMeter进行性能测试的保姆级教程

性能测试是确保网站在用户访问高峰时保持稳定和快速响应的关键环节。作为初学者&#xff0c;选择合适的工具尤为重要。JMeter 是一个强大的开源性能测试工具&#xff0c;可以帮助我们轻松模拟多用户场景&#xff0c;测试网站的稳定性与性能。本教程将引导你通过一个简单的登录场…

微信小程序canvas 生成二维码图片,画图片,生成图片,将两个canvas结合并保存图片

需求实现步骤如下 先定义两个canvas一个canvas myQrcode画二维码的图片另一个canvas mycanvas画一个背景图&#xff0c;并把二维码画到这个canvas上&#xff0c;mycanvas这个canvas生成一张图片&#xff0c;返回图片的临时路径最后保存图片到手机 首先wxml,新版微信小程序can…

Java之继承抽象类用法实例(三十一)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

使用Matplotlib绘制箱线图:详细指南与示例

在数据分析和可视化领域&#xff0c;箱线图&#xff08;Box Plot&#xff09;是一种强大的工具&#xff0c;用于展示数据的分布特征&#xff0c;包括中位数、四分位数、异常值等。本文将详细介绍如何使用Matplotlib库在Python中绘制箱线图&#xff0c;并通过一个实际的血压数据…

从0开始linux(13)——进程(4)进程空间地址(1)

欢迎来到博主的专栏&#xff1a;从0开始linux 博主ID&#xff1a;代码小豪 文章目录 进程空间地址 还记得博主在之前介绍子进程时说过的话吗&#xff1f;子进程与父进程共享代码&#xff0c;而数据却不共享&#xff1b;这很好理解&#xff0c;因为子进程和父进程是不同的进程&a…