[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场景去体验一番吧!
相关文章:
[Unity Demo]从零开始制作空洞骑士Hollow Knight第十七集:制作第一个BOSS苍蝇之母
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、战斗场景Battle Scene相关逻辑处理 1.防止玩家走出战斗场景的门2.制作一个简单的战斗场景二、制作游戏第一个BOSS苍蝇之母 1.导入素材和制作相关动画2.制作…...
【Nginx系列】499错误
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
Springboot项目控制层注释
Springboot主流的 ----------------------- 简略写法 package com.dx.wlmq.controller;import com.dx.wlmq.domain.Address; import com.dx.wlmq.service.AddresssService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.b…...
从Docker容器中备份整个PostgreSQL
问题 现在需要从Docker容器中备份整个PostgreSQL后,然后,使用备份文件在另外一个pg的docker容器中恢复过来。 步骤 备份旧容器中的PG # 登录到旧的PG容器中 docker exec -it postgres bash # 备份数据库 pg_dumpall -c -U postgres > dump_date %…...
从小需求看大格局:如何用技术智慧赢得客户信任
时间:2024年 10月 26日 作者:小蒋聊技术 邮箱:wei_wei10163.com 微信:wei_wei10 音频:从小需求看大格局:如何用技术智慧赢得客户信任 欢迎大家回到“小蒋聊技术”,这是一个不只是教你如何写…...
模型 支付矩阵
系列文章 分享 模型,了解更多👉 模型_思维模型目录。策略选择的收益分析工具。 1 支付矩阵的应用 1.1 支付矩阵在市场竞争策略分析中的应用 支付矩阵是一种强大的决策工具,它在多个领域的应用中都发挥着重要作用。以下是一个具体的应用案例…...
擎创科技声明
近日,我司陆续接到求职者反映,有自称是擎创科技招聘人员,冒用“上海擎创信息技术有限公司”名义,用“126.com”的邮箱向求职者发布招聘信息,要求用户下载注册APP,进行在线测评。 对此,我司郑重…...
二叉树习题其六【力扣】【算法学习day.13】
前言 书接上篇文章二叉树习题其四,这篇文章我们将基础拓展 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向(例如想要掌握基础用法,该刷哪些题?)我的解析也不会做的非常详细,只会提供思路和一…...
互联网的无形眼睛:浏览器指纹与隐私保护攻略
你是否曾有过这样的经历:在某个电商网站上搜索了某件商品,随后无论你打开哪个网页,都能看到与之相关的广告?或者当你再次访问某个网站时,它居然记得你之前的浏览记录?这一切,背后都有一只“看不…...
后端技术:有哪些常见的应用场景?
篇一、 原文链接:https://www.zhihu.com/question/642709585/answer/3388752666 1、数据处理和存储 后端技术可用于处理和存储大量数据,例如构建数据库系统、设计高效的数据结构、实现算法等。常见的数据库技术有关系型数据库(如MySQL、O…...
【Unity 安装教程】
Unity 中国官网地址链接 Unity - 实时内容开发平台 | 3D、2D、VR & AR可视化https://unity.cn/首先我们想要安装Unity之前,需要安装Unity Hub: Unity Hub 是 Unity Technologies 开发的一个集成软件,它为使用 Unity 引擎的开发者提供了一…...
C++ 二级测试卷及答案
1.与指定数字相同的数的个数 题目描述:输出一个整数序列中与指定数字相同的数的个数。 输入 输入包含三行: 第一行为N,表示整数序列的长度(N≤100); 第二行为N个整数,整数之间以一个空格分开; 第三行包含一个整数,为指定的数字m。 输出 输出为…...
Java基础(7)图书管理系统
目录 1.前言 2.正文 2.1思路 2.2Book包 2.3people包 2.4operation包 2.5主函数 3.小结 1.前言 哈喽大家好吖,今天来给前面Java基础的学习来一个基础的实战,做一个简单的图书管理系统,这里边综合利用了我们之前学习到的类和对象&…...
使用 Spring Boot 实现图片上传
目录 一、前言 二、项目准备 2.1、创建SpringBoot项目 2.2、项目结构 2.3、配置文件 2.4、创建控制器 2.5、创建服务 2.6创建前端界面 2.7、静态资源 三、运行项目 四、测试上传功能 总结 一、前言 在现代 web 开发中,图片上传功能是一个…...
深度解析跨境支付之产品架构
跨境支付企业有能力开放更多的底层能力接口给到外界合作伙伴。其中包括购汇及申报、结汇及申报、换汇(包含汇率查询和外汇兑换、远期锁汇等功能)、境外本地下单、查询、退款、外汇跨境收款、海外代发、VA账户开户及余额查询、VCC发卡及查询等能力。 在这…...
Linux下的线程同步与死锁避免
文章目录 死锁的四个必要条件破坏死锁条件的方法破坏互斥条件使用读写锁(pthread_rwlock_t) 破坏持有并等待条件一次性申请所有资源 破坏不可剥夺条件使用超时锁定机制可重入锁(递归锁) 破坏循环等待条件统一锁顺序 在 Linux 下进…...
【Python爬虫实战】Selenium自动化网页操作入门指南
#1024程序员节|征文# 🌈个人主页:易辰君-CSDN博客 🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、准备工作 (一)安装 Selenium 库 ࿰…...
mono源码交叉编译 linux arm arm64全过程
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...
矩阵的可解性:关于Ax=b的研究
上一篇文章讲解了如何求解 A x 0 Ax0 Ax0,得到 A A A的零空间。 类似的,我们今天学习的是如何求解 A x b Axb Axb,并以此加强你对线性代数中,代数与空间的理解。 同样的,我们举与上一次一样的例子,矩阵 …...
10.22.2024刷华为OD C题型(三)--for循环例子
脚踝动了手术,现在宾馆恢复,伤筋动骨一百天还真不是说笑的,继续努力吧。 文章目录 靠谱的车灰度图恢复灰度图恢复 -- for循环使用例子 靠谱的车 https://www.nowcoder.com/discuss/564514429228834816 这个题目思路不难,就是要自…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

