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

news2024/11/26 19:33:48

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

文章目录

  • 前言
  • 一、制作游戏第二个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终于做完一期了,刚好最近有空整理一下最近做的内容。

 

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

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

相关文章

springboot-整合多数据源配置

简介 主要介绍两种整合方式,分别是 springboot+mybatis 使用分包方式整合,和 springboot+druid+mybatisplus 使用注解方式整合。 一、表结构 在本地新建两个数据库,名称分别为db1和db2,新建一张user表,表结构如下: SQL代码: CREATE TABLE `user` (`id` int(11) NOT …

LeetCode 热题100 之 回溯1

1.全排列 思路分析1&#xff08;回溯&#xff09;&#xff1a;要生成一个不含重复数字的数组 nums 的所有可能全排列&#xff0c;我们可以使用回溯算法。这种算法通过递归的方法探索所有可能的排列组合&#xff0c;并在合适的时机进行回溯&#xff0c;确保不会遗漏任何排列。回…

笔记整理—linux驱动开发部分(4)驱动框架

内核中&#xff0c;针对每种驱动都设计了一套成熟的、标准的、典型的驱动框架&#xff0c;实现将相同部分实现&#xff0c;不同部分留出接口给工程师自行发挥。具有以下特点&#xff1a;①简单化&#xff1b;②标准化&#xff1b;③统一管控系统资源&#xff1b;④特定化接口函…

靠谷歌广告赚了100美刀,程序员可以照这个思路去干

复制网站盈利尝试&#xff1a;谷歌广告收入之路的挑战与反思 背景介绍 在互联网的浩瀚海洋中&#xff0c;网站复制现象屡见不鲜。近期&#xff0c;我尝试复制了一个名为网站B的核心代码&#xff0c;并成功发布了自己的网站。通过谷歌搜索引擎的优化&#xff08;SEO&#xff0…

Windows 10/11 设置锁屏密码的方法以及设置PIN密码

Windows 10/11 设置锁屏密码的方法 一、打开设置&#xff1a; 按 Win I 快捷键打开“设置”。 二、进入账户设置&#xff1a; 在设置窗口中点击“账户”。 三、选择登录选项&#xff1a; 在左侧菜单中选择“登录选项”。 四、添加密码&#xff1a; …

Python并发编程库:Asyncio的异步编程实战

Python并发编程库&#xff1a;Asyncio的异步编程实战 在现代应用中&#xff0c;并发和高效的I/O处理是影响系统性能的关键因素之一。Python的asyncio库是专为异步编程设计的模块&#xff0c;提供了一种更加高效、易读的并发编程方式&#xff0c;适用于处理大量的I/O密集型任务…

当软件质量遇上计划性报废:测试行业该如何应对?

那天&#xff0c;我像往常一样开车在路上&#xff0c;车窗外的风景飞快掠过。就在这时&#xff0c;我在听的一档播客里&#xff0c;突然提到了一个让我不得不停下来思考的词——“计划性报废”。这个词让我愣了一下&#xff0c;伴随着车轮的转动&#xff0c;我的思绪也随之转了…

【Seed-Labs】SQL Injection Attack Lab

Overview SQL 注入是一种代码注入技术&#xff0c;利用的是网络应用程序与数据库服务器之间接口的漏洞。当用户输入的信息在发送到后端数据库服务器之前没有在网络应用程序中进行正确检查时&#xff0c;就会出现这种漏洞。 许多网络应用程序从用户那里获取输入&#xff0c;然…

linux笔记(DNS)

一、概念 DNS&#xff08;Domain Name System&#xff09;DNS 是一种分布式网络目录服务&#xff0c;主要用于将人类易于记忆的域名&#xff08;如 www.example.com&#xff09;转换为计算机可识别的 IP 地址&#xff08;如 192.168.1.1&#xff09;。它就像是互联网的电话簿&a…

【计网】实现reactor反应堆模型 --- 框架搭建

没有一颗星&#xff0c; 会因为追求梦想而受伤&#xff0c; 当你真心渴望某样东西时&#xff0c; 整个宇宙都会来帮忙。 --- 保罗・戈埃罗 《牧羊少年奇幻之旅》--- 实现Reactor反应堆模型 1 前言2 框架搭建3 准备工作4 Reactor类的设计5 Connection连接接口6 回调方法 1 …

minikube 的 Kubernetes 入门教程--(五)

本文记录 Minikube 在 Kubernetes 上安装 WordPress 和 MySQL。 这两个应用都使用 PersistentVolumes 和 PersistentVolumeClaims 保存数据。 在深入这些步骤之前&#xff0c;先分享来自kubernetes.io教程。 链接>>使用持久卷部署 WordPress 和 MySQL | Kubernetes 获…

算法详解——链表的归并排序非递归解法

算法详解——链表的归并排序非递归解法 本文使用倍增法加上归并排序操作实现了对链表的快速排序&#xff0c;比起一般的递归式归并排序要节省空间并且实现要简单的多&#xff0c;比起一般的迭代式归并排序实现也要简单。 1. 题目假设 给定链表的头结点 head &#xff0c;请将其…

【网络-交换机】生成树协议、环路检测

路由优先级 路由优先级决定了在多种可达的路由类型中&#xff0c;哪种路由将被用来转发数据包。路由优先级值越低&#xff0c;对应路由的优先级越高&#xff0c;优先级值255表示对应的路由不可达。一般情况下&#xff0c;静态路由的优先级为1&#xff0c;OSPF路由优先级为110&a…

确定图像的熵和各向异性 Halcon entropy_gray 解析

1、图像的熵 1.1 介绍 图像熵&#xff08;image entropy&#xff09;是图像“繁忙”程度的估计值&#xff0c;它表示为图像灰度级集合的比特平均数&#xff0c;单位比特/像素&#xff0c;也描述了图像信源的平均信息量。熵指的是体系的混乱程度&#xff0c;对于图像而言&#…

数字后端零基础入门系列 | Innovus零基础LAB学习Day9

Module 16 Wire Editing 这个章节的学习目标是学习如何在innovus中手工画线&#xff0c;切断一根线&#xff0c;换孔&#xff0c;更改一条net shape的layer和width等等。这个技能是每个数字IC后端工程师必须具备的。因为项目后期都需要这些技能来修复DRC和做一些手工custom走线…

除草机器人算法以及技术详解!

算法详解 图像识别与目标检测算法 Yolo算法&#xff1a;这是目标检测领域的一种常用算法&#xff0c;通过卷积神经网络对输入图像进行处理&#xff0c;将图像划分为多个网格&#xff0c;每个网格生成预测框&#xff0c;并通过非极大值抑制&#xff08;NMS&#xff09;筛选出最…

ProCalun卡伦纯天然万用膏,全家的皮肤健康守护

受季节交替、生活环境变化、空气污染等方面因素的影响&#xff0c;加上作息不规律导致的免疫力降低&#xff0c;我们或多或少会出现一些如湿疹、痤疮、瘙痒之类的皮肤问题&#xff0c;且反复概率很高。很多人盲目用药&#xff0c;甚至诱发激素依赖性皮炎。所以近年来&#xff0…

Vue 自定义icon组件封装SVG图标

通过自定义子组件CustomIcon.vue使用SVG图标&#xff0c;相比iconfont下载文件、重新替换更节省时间。 子组件包括&#xff1a; 1. Icons.vue 存放所有SVG图标的path 2. CustomIcon.vue 通过icon的id索引对应的图标 使用的时候需要将 <Icons></Icons> 引到使用的…

wireshark工具使用

复制数据 1.右键展开整帧数据 2.复制“所有可见项目” mark标记数据 标记&#xff1a; 跳转&#xff1a; 保存成文件&#xff1a; 文件–>导出特定分组—>Marked packets only

【SpringCloud】SpringBoot集成Swagger 常用Swagger注解

概述&#xff1a;SpringBoot集成Swagger 常用Swagger注解 导语 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前…