[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 这个题目思路不难,就是要自…...
QT:MaintenanceTool 模块安装工具
QT的MaintenanceTool 工具对已安装的 Qt 进行卸载、修复等其他操作时提示At least one valid and enabled repository required for this action to succeed 解决方式:在设置中添加一个临时的仓库 https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepositor…...
同标签实现监听LocalStorage
使用 React 生命周期函数 useEffect来监听和处理 LocalStorage 的变化 import React, { useEffect } from react;const LocalStorageListener () > {useEffect(() > {// 注册监听器const handleStorageChange (event) > {if (event.key myKey) {console.log(注册…...
JAVA高性能缓存项目
版本一 代码实现 import java.util.HashMap; import java.util.concurrent.TimeUnit;public class CacheExample01 {private final static HashMap<String, Integer> cache new HashMap<>();public static Integer check(String userId) throws InterruptedExce…...
智慧农业大数据平台:智汇田园,数驭未来
智慧农业大数据平台 计讯物联智慧农业大数据平台是一个集管理数字化、作业自动化、生产智能化、产品绿色化、环境信息化、服务现代化于一体的多功能监管系统。它通过与硬件产品的搭配使用,实现对农业生产全过程的实时监测、精准控制和科学管理。该平台集成了多个数…...
Go语言基础教程:可变参数函数
Go 语言允许函数接收可变数量的参数,这种特性对于处理数量不确定的参数特别有用。在本教程中,我们将通过示例代码讲解如何定义和使用 Go 的可变参数函数。 package mainimport "fmt"// 定义一个可变参数函数 sum,接收任意数量的整…...
高并发场景下解决并发数据不一致
简单的场景: 全量数据更新的情况下, 不在乎同一秒的请求都必须要成功, 只留下最新的更新请求数据 方案常用的是 1、数据库增加时间戳标识实现的乐观锁, 请求参数从源头带上微秒或者毫秒时间戳数据库存储, 然后在更新SQL语句上比较 (数据库的时间 < 参数传递的时间) 例如: A…...
OpenAI GPT-o1实现方案记录与梳理
本篇文章用于记录从各处收集到的o1复现方案的推测以及介绍 目录 Journey Learning - 上海交通大学NYUMBZUAIGAIRCore IdeaKey QuestionsKey TechnologiesTrainingInference A Tutorial on LLM Reasoning: Relevant methods behind ChatGPT o1 - UCL汪军教授Core Idea先导自回归…...
Excel:vba实现生成随机数
Sub 生成随机数字()Dim randomNumber As IntegerDim minValue As IntegerDim maxValue As Integer 设置随机数的范围(假入班级里面有43个学生,学号是从1→43)minValue 1maxValue 43 生成随机数(在1到43之间生成随机数)randomNumber Application.WorksheetFunctio…...
Python | Leetcode Python题解之第506题相对名次
题目: 题解: class Solution:desc ("Gold Medal", "Silver Medal", "Bronze Medal")def findRelativeRanks(self, score: List[int]) -> List[str]:ans [""] * len(score)arr sorted(enumerate(score), …...
安全见闻(6)
声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址:安全见闻(6)_哔哩哔哩_bilibili 学无止境,开拓自己的眼界才能走的更远 本文主要讲解通讯协议涉及的安全问题。…...
中国互联网协会是什么单位/seo快照推广
一、从一个微信Bug说起 问题:在微信Android客户端,一张七牛CDN上的HTTPS链接的图片,用微信浏览器打开可以正常访问,但是,长按图片保存时,却提示下载图片失败。发现这个问题后,我用了一个简单的测…...
泰兴市网站建设/google优化排名
su [user] 和 su - [user]的区别: su [user]切换到其他用户,但是不切换环境变量,su - [user]则是完整的切换到新的用户环境。 如: [rootrac1 ~]# pwd --当前目录 /root [rootrac1 ~]# su oracle --使用su [user] [oraclerac1 root…...
人社局网站建设/网推是什么
ClassLoader翻译过来就是类加载器,普通的Java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,…...
js模版网站/重庆今天刚刚发生的重大新闻
🍯前端~CSS 简介:为页面中的元素添加样式 目录 1、基础选择器 2、组合选择器 3、属性选择器 4、伪类选择器 5、背景样式(各种样式) 使用方式:》》》 行内样式:样式定义在行内部标签上内部样式:css样式定…...
新乡模板建站/天津seo优化排名
2019独角兽企业重金招聘Python工程师标准>>> Github 转载于:https://my.oschina.net/u/612750/blog/3019795...
自己怎么做卡密网站/百度快照的作用是什么
云计算在图书馆领域的应用与研究已陆续展开,图书馆的云时代即将到来。重塑图书馆生存和发展的环境、推动图书馆自身变革是云计算环境下图书馆发展的未来趋势。 云计算是分布式处理(Distributed Computing)、并行处理(Parallel Com…...