[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十集:制作专门渲染HUD的相机HUD Camera和画布HUD Canvas
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、制作HUD Camera以及让两个相机同时渲染屏幕
- 二、制作HUD Canvas
- 1.制作法力条Soul Orb引入库
- 2.制作生命条Health读入数据
- 3.制作吉欧统计数Geo Counter
- 4.制作其它内容Extra
- 总结
前言
hello大家好久没见,之所以隔了这么久才更新并不是因为我又放弃了这个项目,而是接下来要制作的工作太忙碌了,每次我都花了很长的时间解决完一个部分,然后就没力气打开CSDN写文章就直接睡觉去了,现在终于有时间整理下我这半个月都做了什么内容。
那么,这个HUD是什么意思呢?百度翻译就是平行显示系统,一般用于车辆上面的某个系统,但它也可以作为了一个专业的游戏术语,显示与摄像机平行的东西,如果你还没理解的话,我就直接上图吧:就是这些不会随着摄像机移动而相对移动的物品,这些看似是UI来实现的也可以使用摄像机的功能来实现:
另外,我的Github已经更新了,想要查看最新的内容话请到我的Github主页下载工程吧:
GitHub - ForestDango/Hollow-Knight-Demo: A new Hollow Knight Demo after 2 years!
一、制作HUD Camera以及让两个相机同时渲染屏幕
OK我们到_GameCameras中创建一个新的相机名字就叫HudCamera,并按照如下设置好camera的各个参数。
作为playmakerFSM的公共变量,我们还需要初始化这个公共变量:
创建一个同名脚本HUDCamera.cs,目前只用处理在HUD Camra激活以后将menu的camera移动到hud camera上:由于我们还没做到暂停界面所以可以完全不管。
using System;
using UnityEngine;public class HUDCamera : MonoBehaviour
{private GameCameras gc;private InputHandler ih;private bool shouldEnablePause;private void OnEnable(){if (!gc){gc = GameCameras.instance;}if (!ih){ih = GameManager.instance.inputHandler;}if (ih.pauseAllowed){shouldEnablePause = true;ih.PreventPause();}else{shouldEnablePause = false;}Invoke("MoveMenuToHUDCamera", 0.5f);}private void MoveMenuToHUDCamera(){gc.MoveMenuToHUDCamera();if (shouldEnablePause){ih.AllowPause();shouldEnablePause = false;}}
}
我们还需要创建一个类似于UI画布的子对象名字就叫HUD Canvas,它负责我们第二节讲到的四个部分的位置,控制它们的大小之类的。
同样它也是一个公共变量:(另一个playmakerFSM我们下一期再讲。)
创建一个相机不难,但是我们都知道一个相机会渲染一个屏幕上的所有东西,那么怎么让两个摄像机渲染各自需要渲染的东西呢?很简单,就是找到每个摄像机的Culling Mask,选择想要渲染的东西,比如HudCamera就渲染UI层即可:
主摄像机就渲染其它的层级:
但这些还远远不够,其实做到这里我也遇到瓶颈了,上网搜索解决方法都是毫不相关的内容:
但是功夫不负有心人。我居然在CSDN找到了解决方法:内容的原文如下:
【Unity】双摄像机叠加渲染_unity两个摄像机画面叠加-CSDN博客
主要是分为四个部分:
第一部分就是我上面设置的Culling Mask,
第二部分,设置好两个摄像机的Clear Flags:渲染UI的就用Depth Only,
主摄像机用SkYBOX
第三部分设置好两个深度,保证HUD Camera的深度大于主摄像机:
然后它们两个摄像机的target display都保证是display 1就没问题了,还有就是记得把UI的游戏对象的layer都设置成UI,这样HUD Camera才能渲染到:
为了保证HUD Camera和主摄像机的culling mask没有问题,来到脚本GameCameras.cs中,添加上刚刚创建好的camera参数:
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.ImageEffects;public class GameCameras : MonoBehaviour
{private static GameCameras _instance;public static GameCameras instance{get{if (_instance == null){_instance = FindObjectOfType<GameCameras>();if (_instance == null){Debug.LogError("Couldn't find GameCameras, make sure one exists in the scene.");}DontDestroyOnLoad(_instance.gameObject);}return _instance;}}[Header("Cameras")]public Camera hudCamera;public Camera mainCamera;[Header("Controllers")]public CameraController cameraController;public CameraTarget cameraTarget;[Header("FSMs")]public PlayMakerFSM cameraShakeFSM;public PlayMakerFSM cameraFadeFSM;public PlayMakerFSM soulOrbFSM;public PlayMakerFSM soulVesselFSM;[Header("Other")]public tk2dCamera tk2dCam;public GameObject hudCanvas;public Transform cameraParent;public GeoCounter geoCounter;private bool init;private GameManager gm;private void Awake(){if(_instance == null){_instance = this;Debug.LogFormat("GameCameras DontDestroyOnLoad");DontDestroyOnLoad(this);return;}if(this != _instance){DestroyImmediate(gameObject);return;}}private void OnDestroy(){DestroyImmediate(sceneParticles);}public void SceneInit(){if(this == _instance){StartScene();}}private void StartScene(){if (!init){SetupGameRefs();}if(gm.IsGameplayScene() || gm.ShouldKeepHUDCameraActive()){MoveMenuToHUDCamera();if (!hudCamera.gameObject.activeSelf){hudCamera.gameObject.SetActive(true);}}else{DisableHUDCamIfAllowed();}if (gm.IsMenuScene()){cameraController.transform.SetPosition2D(14.6f, 8.5f);}cameraController.SceneInit();cameraTarget.SceneInit();sceneParticles.SceneInit();}public void MoveMenuToHUDCamera(){int cullingMask = mainCamera.cullingMask;int cullingMask2 = hudCamera.cullingMask;UIManager.instance.UICanvas.worldCamera = hudCamera; //让uimanager的canvas相机设置成hudcameraUIManager.instance.UICanvas.renderMode = RenderMode.ScreenSpaceCamera;mainCamera.cullingMask = (cullingMask ^ 134217728);hudCamera.cullingMask = (cullingMask2 | 134217728);}public void DisableHUDCamIfAllowed(){if (gm.IsNonGameplayScene() && !gm.ShouldKeepHUDCameraActive()){hudCamera.gameObject.SetActive(false);}}private void SetupGameRefs(){gm = GameManager.instance;if(cameraController != null){cameraController.GameInit();}else{Debug.LogError("CameraController not set in inspector.");}if (cameraTarget != null){cameraTarget.GameInit();}else{Debug.LogError("CameraTarget not set in inspector.");}init = true;}}
回到编辑器都添加上去参数:没有的参数都是后面要讲的。
二、制作HUD Canvas
这一节的内容会非常繁多,因为毕竟是一个顶级2D类银河恶魔城游戏,只能说樱桃工作室是会极致的打磨产品的,所以我做起来也非常费时,你们能看多少就看多少吧。首先我们从法力条开始讲起。
1.制作法力条Soul Orb
我们先来讲子对象再来聊聊这个playmakerFSM
首先是提醒玩家可以治疗的粒子系统:
白光,White Flash:它有一个playmakerFSM用来控制什么时候发光
Orb Full:这是当法力条满了的时候的sprite,它的子对象Pulse Sprite是加强白光的效果
Eyes:眼睛,当玩家的法力条达到三分之二的时候,这个眼睛就会显示出来,感觉就像外星人一样
然后它需要一个playmakerFSM来控制显示的动画:
这里本来如果没有spell法术的话就会进入Off状态,但我觉得没必要所以就不连Off了:
不得不说这个iTween确实好用,选择合适的Ease Type就可以很流畅的改变你想要改变的值,比如我们的这个colour,还有眼睛的大小scale,让它看起来像播放了一个睁开的动画一样
这些OVER , UNDER事件都是由Soul Orb的playmakerFSM来发送的。
HUD Frame:也就是法力条的头部,他需要动画和一个playmakerFSM控制动画的播放:
一个是Idle动画,另一个是Appear动画
创建一个名字为Load Animation的playmakerFSM:
对于HUD Frame的动画,我们由四款不一样的需要选择,所以我们使用FsmString记录需要使用哪一款的HUD Frame,这四款分别是正常的,破碎的,钢魂的,寻神者的,所以我们才需要判断使用哪一款
这个就是寻神者情况下的:
这里需要判断模式以及是否是第一次进入教学关卡enteredTutorialFirstTime:
在等待个1.75秒后,我们还需要判断是否是破碎的HUD Frame:
等到发送事件SOUL LIMITER DOWN以后就回到最原始的款式,
这个也是同理:
然后就是每当场景发生变换的时候就调用一次。
Burst Anim:当满了以后播放这个动画,提示一下玩家该使用法力条了
Soul Orb Break Effect:也就是法力条破碎后播放的粒子效果
SoulOrb_fill:这个就是涉及到shader的mask相关了:
第二个子对象是一个mask,其实就是透明的shader作用在上面
第一个Liquid则是有些tk2dAnimator动画,它的动画看起来非常奇怪,你可能会好奇这些动画是怎么实现上面的效果的:
所以这就是shader牛逼的地方了,可以看到,随着移动它的mask区域也发生了改变,这个liquid仿佛成为了一个mask附着在mask上面了,当它的位置发生改变时里面的mask的范围也会发生改变,
这个shader其实是Unity资源商店的一个商品名字叫做:Alpha Masking,以下是该作者给出的使用教程注意这段话:If an object uses the Default Sprite or Unlit/Transparent shader, the mask will be applied to it,所以这就是为什么我们需要一个mask使用Unlit/Transparent。这样它就会附着在这个mask上面了,而这个mask的大小要和HUD_Frame的大小一样,这样看起来好像SoulOrb_fill附着HUD_Frame一样。我们就使用它的shader:Sprites-Alpha-Mask-WorldCoords即可实现这个很牛的功能。
HOW TO USE:- To apply the mask to objects, attach those objects to the same parent (may have more than one level of hierarchy) and put the mask directly under that parent, too.- Clicking "Apply Mask to Siblings in Hierarchy" will detect all siblings (including their children) and attempt to apply the mask to all them. If an object uses the Default Sprite or Unlit/Transparent shader, the mask will be applied to it.- The mask can be moved, scaled and rotated freely in the Editor, but it can only be rotated over a chosen axis (depending on what mapping axis is selected).MASK PARAMETERS:- Mask Mapping World Axis: defines, over which axis the mask should be applied. This is usually the axis, which corresponds with the camera direction.
- Invert Axis: in case you need to map the mask over an inverted axis.
- Clamp Alpha Horizontally: if the texture isn't clamped by Unity (in import settings), then you can choose to clamp it horizontally only (it will be repeated vertically, unless chosen otherwise).
- Clamp Alpha Vertically: if the texture isn't clamped by Unity (in import settings), then you can choose to clamp it vertically only (it will be repeated horizontally, unless chosen otherwise).
- Clamping Border: if one of the two bove settings are enabled, you can use this variable to tweak the "edge" with of clamping. Depending on the alpha texture size and its usage, you might run into texture clamping issues. In that case, try increasing (or lowering) the Clamping Border value.
- Use Mask A Channel (not RGB): the mask uses the texture RGB channels by default. Toggle "Use Mask A Channel (not RGB)" to use the Alpha channel of the texture instead.
- Display Mask: toggle this setting to enable or disable the visibility of the mask. This setting is only available in the Editor (and while not running the player).THING TO HAVE IN MIND:- You can either create your own materials for masked Sprites/3D objects manually, or the Mask will create its own.- When using prefabs of masked objects, it's better to manually create materials.
接下来是这四个容器,但我们目前还用不多所以我先不讲了,暂时贴出来给大伙看看吧:
经常打四锁五门的朋友都知道,这个就是禁止使用容器的法力条的meshrenderer
这是限制法力条后的burst anim:
这两个是在寻神者模式的,懂的都懂:
(写到这里的时候这网页居然崩溃了,真的给我整无语了,没办法只能收拾心情再写一遍了)
回到Soul Orb我们来写一个playmakerFSMSoul Orb Control:
初始阶段清除MP,HeroController的方法ClearMP,取消激活寻神者里的binding cap,设置孩子的引用,设置好liquid Y轴方向的位置,通过GetPlayerDataInt来获得当前MP判断是否为0,以及设置好LiquidY初始的位置加上Mp*每消耗一次Mp Liquid的Y轴偏移量。
如果开始时MP是满的就激活orb full
设置可以播放Can Focus Anim.
Check Can Heal判断能否使用回复术:就是在玩家的playmakerFSM叫Spell Control,通过里面的FSMBool变量Focusing存储在自身的Hero Focusing变量上,如果正在focusing就取消,反之则通过MPCharge和focusMP_amount判断够不够法力值来回血
如果不能够的情况下,就告诉Liquid的事件:CANT HEAL
如果能够治疗的话,就发送Liquid事件CAN HEAL,播放动画以及音效,设置好eFFect范围,同时在玩家的SpriteFlash中调用方法:flashFocusGet()
private void flashFocusGet(){Start();flashColour = new Color(1f, 1f, 1f);amount = 0.5f;timeUp = 0.1f;stayTime = 0.01f;timeDown = 0.35f;block.Clear();block.SetColor("_FlashColor", flashColour);flashingState = 1;flashTimer = 0f;repeatFlash = false;}
然后通过MP的值是否大于50来判断是否显示眼睛
然后再Idle状态等待外面物体发送这些事件:
如果发送的是MP LOSE事件,首先判断是否是寻神者模式锁住法力条
再检查一遍MP 有没有小于50判断是否显示眼睛:
MP LOSE阶段:Liquid播放动画Shrink,设置Orb Full为非激活,设置好Liquid的Y轴方向的位置
然后就是用我们最爱的ITween Move To改变Liquid的位置
Check MP:检查我们的MP的值是否小于1,小于1则Mp Is Zero事件发送
在MP <= 1阶段,我们得让Liquid的Y轴离远点保证不会再mask的上面
让我们回到Idle状态,这个MP SET老实说我也没想到怎么用,可以先放在一边:
然后是事件MP GAIN SPA在温泉中获得MP的事件,这个我也没做到也先放着。
当发送获得MP事件MP GAIN之后,通过MPCharge和maxMP判断MP是否满了,满的话就给子对象Get Flash发送FLASH事件闪一下提示玩家。
这个promptFocus是提示玩家使用回复术的,这也是下一期的内容:
在MP Gain阶段,也是获得当前Mp和满Mp的值,然后设置好Liquid的Y轴方向,
老实说我还没想要当mp为一半的时候到底该怎么样,所以先放着这个HALF FULL。
如果Mp满了的话执行和我上面说的差不多的操作
这个寻神者模式下满法力条的情况:
最后是法力条被吸收消耗的时候状态MP DRAIN:设置orb full为非激活,判断Mp是否已经为0,设置好Liquid的Y轴的位置
那么讲了这么多有关Mp的事件,会在哪里调用呢?
首先是在HeroController.cs函数当中:
public void AddMPCharge(int amount){int mpreserve = playerData.MPReserve;playerData.AddMPCharge(amount);GameCameras.instance.soulOrbFSM.SendEvent("MP GAIN");if (playerData.MPReserve != mpreserve && gm){gm.soulVessel_fsm.SendEvent("MP RESERVE UP");}}public void SoulGain(){int num;if(playerData.MPCharge < playerData.maxMP){num = 11;}else{num = 6;}int mpreverse = playerData.MPReserve;playerData.AddMPCharge(num);GameCameras.instance.soulOrbFSM.SendEvent("MP GAIN");if(playerData.MPReserve != mpreverse){gm.soulVessel_fsm.SendEvent("MP RESERVE UP");}}public void AddMPChargeSpa(int amount){TryAddMPChargeSpa(amount);}public bool TryAddMPChargeSpa(int amount){int mpreserve = playerData.MPReserve;bool result = playerData.AddMPCharge(amount);gm.soulOrb_fsm.SendEvent("MP GAIN SPA");if(playerData.MPReserve != mpreserve){gm.soulVessel_fsm.SendEvent("MP RESERVE UP");}return result;}
然后就是在hero的playmakerFSM当中我们会调用herocontroller.cs的TakeMp和TakeMpQuick两个函数:
public void TakeMp(int amount){Debug.LogFormat("Take MP");if(playerData.MPCharge > 0){playerData.TakeMP(amount);if(amount > 1){GameCameras.instance.soulOrbFSM.SendEvent("MP LOSE");}}}public void TakeMPQuick(int amount){if (playerData.MPCharge > 0){playerData.TakeMP(amount);if (amount > 1){GameCameras.instance.soulOrbFSM.SendEvent("MP DRAIN");}}}
终于整完第一部分了,接下来该讲讲生命条Health了。
2.制作生命条Health
其实我做了11个生命条,不知道你数了没有:
还是来一个一个介绍下它们的功能吧,首先是只有一滴血的时候的视觉效果们:Low Health Vignette
创建一个名字为Vignette Control的playmakerFSM:
很简单,通过UP DOWN两个事件改编它的scale,还有淡入淡出的手段改变依赖他的spriteRenderers,TextMeshPro,InvAnimateUpAndDown们
这里需要一个新的脚本FadeGroup.cs:
using System;
using TMPro;
using UnityEngine;public class FadeGroup : MonoBehaviour
{public SpriteRenderer[] spriteRenderers;public TextMeshPro[] texts;public InvAnimateUpAndDown[] animators;public float fadeInTime = 0.2f;public float fadeOutTime = 0.2f;public float fadeOutTimeFast = 0.2f;public float fullAlpha = 1f;public float downAlpha;public bool activateTexts;private int state;private float timer;private Color currentColour;private Color fadeOutColour = new Color(1f, 1f, 1f, 0f);private Color fadeInColour = new Color(1f, 1f, 1f, 1f);private float currentAlpha;public bool disableRenderersOnEnable;private void OnEnable(){if (disableRenderersOnEnable){DisableRenderers();}}private void Update(){if (state != 0){float t = 0f;if (state == 1) //将所有spriteRenderers和texts的alpha设置为upalpha{timer += Time.deltaTime;if (timer > fadeInTime){timer = fadeInTime;state = 0;for (int i = 0; i < spriteRenderers.Length; i++){if (spriteRenderers[i] != null){Color color = spriteRenderers[i].color;color.a = fullAlpha;spriteRenderers[i].color = color;}}for (int j = 0; j < texts.Length; j++){if (texts[j] != null){Color color2 = texts[j].color;color2.a = fullAlpha;texts[j].color = color2;}}}t = timer / fadeInTime;}else if (state == 2) //将所有spriteRenderers和texts的alpha设置为downalpha{timer -= Time.deltaTime;if (timer < 0f){timer = 0f;state = 0;if (downAlpha > 0f){for (int k = 0; k < spriteRenderers.Length; k++){if (spriteRenderers[k] != null){Color color3 = spriteRenderers[k].color;color3.a = downAlpha;spriteRenderers[k].color = color3;}}for (int l = 0; l < texts.Length; l++){if (texts[l] != null){Color color4 = texts[l].color;color4.a = downAlpha;texts[l].color = color4;}}}else{DisableRenderers();}}t = timer / fadeOutTime;}if (state != 0){currentAlpha = Mathf.Lerp(downAlpha, fullAlpha, t);for (int m = 0; m < spriteRenderers.Length; m++){if (spriteRenderers[m] != null){Color color5 = spriteRenderers[m].color;color5.a = currentAlpha;spriteRenderers[m].color = color5;}}for (int n = 0; n < texts.Length; n++){if (texts[n] != null){Color color6 = texts[n].color;color6.a = currentAlpha;texts[n].color = color6;}}}}}/// <summary>/// 将所有的spriterender和text都设置为透明alpha = 0/// </summary>public void FadeUp(){timer = 0f;state = 1;for (int i = 0; i < spriteRenderers.Length; i++){if (spriteRenderers[i] != null){Color color = spriteRenderers[i].color;color.a = 0f;spriteRenderers[i].color = color;spriteRenderers[i].enabled = true;}}for (int j = 0; j < texts.Length; j++){if (texts[j] != null){Color color2 = texts[j].color;color2.a = 0f;texts[j].color = color2;texts[j].gameObject.GetComponent<MeshRenderer>().SetActiveWithChildren(true);}}for (int k = 0; k < animators.Length; k++){if (animators[k] != null){animators[k].AnimateUp();}}}public void FadeDown(){timer = fadeOutTime;state = 2;for (int i = 0; i < animators.Length; i++){if (animators[i] != null){animators[i].AnimateDown();}}}public void FadeDownFast(){timer = fadeOutTimeFast;state = 2;for (int i = 0; i < animators.Length; i++){if (animators[i] != null){animators[i].AnimateDown();}}}private void DisableRenderers(){for (int i = 0; i < spriteRenderers.Length; i++){if (spriteRenderers[i] != null){spriteRenderers[i].enabled = false;}}for (int j = 0; j < texts.Length; j++){if (texts[j] != null){Color color = texts[j].color;color.a = 0f;texts[j].color = color;//texts[j].gameObject.GetComponent<MeshRenderer>().SetActiveWithChildren(false);}}}
}
通过playmakerFSM调用里面的方法FadeUp和FadeDown就可以轻松改变所依赖对象的值。
using System;
using HutongGames.PlayMaker;
using UnityEngine;[ActionCategory("Hollow Knight")]
public class FadeGroupUp : FsmStateAction
{[UIHint(UIHint.Variable)]public FsmOwnerDefault target;public override void Reset(){target = new FsmOwnerDefault();}public override void OnEnter(){GameObject gameObject = (target.OwnerOption == OwnerDefaultOption.UseOwner) ? Owner : target.GameObject.Value;if (gameObject != null){FadeGroup component = gameObject.GetComponent<FadeGroup>();if (component != null){component.FadeUp();}}base.Finish();}
}
using System;
using HutongGames.PlayMaker;
using UnityEngine;[ActionCategory("Hollow Knight")]
public class FadeGroupDown : FsmStateAction
{[UIHint(UIHint.Variable)]public FsmOwnerDefault target;public FsmBool fast;public override void Reset(){target = new FsmOwnerDefault();}public override void OnEnter(){GameObject gameObject = (target.OwnerOption == OwnerDefaultOption.UseOwner) ? Owner : target.GameObject.Value;if(gameObject != null){FadeGroup component = gameObject.GetComponent<FadeGroup>();if (component != null){if (fast.Value){component.FadeDownFast();}else{component.FadeDown();}}}base.Finish();}}
Low Health Particles
Low Health Light灯:也是外面发送事件UP 和DOWN接收后做出行为
然后就是第一条血了:
除了常规的health_display的playmakerFSM以外,它还有一个复仇之魂效果的playmaker叫Fury Effects,不过这也不是我们关心的,同时它需要订阅事件HERO DAMAGED。
它的子对象第一个:Idle状态下的生命:
第二个是Max Up,回血到这个血条的时候播放的粒子系统:
这个就是护符复仇之魂播放的粒子系统:
这个是使用蜂巢回血的时候的替换的生命:
回到playmaker : health_display当中,我们先创建好事件以及变量:
这个Health Number很关键,你得说明这是第几个生命条,比如我这是第一个就写1.
初始化阶段,找到孩子的引用,关闭idle sprite
这个equippedCharm_29就是蜂巢之血,检查玩家是否装配该护符。
普通情况就是普通情况的动画名:
这是在蜂巢之血情况下的,我们先去把动画制作了吧:
我们先去把他们的动画给做了:记住名字一定要和set string value里面的名字一样:
制作完成动画后让我们回到playmaker,下一个状态是检查当前的血条是否是满血的血条:就是获取gammanager当中的maxHealth的int类型变量并比较
如果超过了满血的话,我们就取消显示它的meshrenderer,
这里是获取新生命值后的再一次检查:
获取新生命值我们就播放这个血条的动画,
在Idle状态下,我们只需要显示它的子对象Idle的meshrenderer即可:
当玩家收到伤害以后会发送HERO DAMAGED事件,
这个是判断是否要销毁这个血条的meshrenderer:
如果是的话就播放动画Empty
那么这时候在治疗状态下呢?就是小骑士的playmaker:“Spell Control”发送了HERO HEALED事件呢,有可能在受到伤害的同时治疗回血了,这也是我们要考虑的,所以Heal和Break时互通的两个状态
当然还有回到满血的状态时,我们还是有一些动画和粒子系统要处理的:
剩下的这些事很后面的内容了,你们还记得寻神者模式的四锁五门吗,我们这里先做个大概的,反正不会执行到这个状态:首先是检查是否寻神者锁生命值状态
using System;
using HutongGames.PlayMaker;[ActionCategory("Hollow Knight/GG")]
public class GGCheckBoundHeart : FSMUtility.CheckFsmStateAction
{public FsmInt healthNumber;public CheckSource checkSource;public override void Reset(){healthNumber = null;checkSource = CheckSource.Regular;base.Reset();}public override bool IsTrue{get{int num = -1;CheckSource checkSource = this.checkSource;if (checkSource != CheckSource.Regular){if (checkSource == CheckSource.Joni){num = (int)(healthNumber.Value * 0.71428573f) + 1;}}else{num = healthNumber.Value;}//TODO:return false;}}public enum CheckSource{Regular,Joni}
}
如果是,则播放动画锁生命:当然这里不会执行到 的
如果不是,就判断是否要播放动画:
首先是是否是第一次游玩该游戏,这里会有个延迟显示的动画,就是依次延迟的显示第1,2,...,n个血条
如果是第一次玩的话,就等一会:播放动画Anim Appear,
设置完成初始化,如果该血条是最后一个满血条,那么就广播事件LAST HP ADDED,我忘记给谁接收了先接着写吧。
回到Load Animation?状态,当发送事件FULL到达,Check if Full检查当前血条是否是满的:
最后一个是复活状态:我们获取的gamemanager的属性RespawningHero来判断当前是否在复活玩家。
using System;
using HutongGames.PlayMaker;[ActionCategory("Hollow Knight")]public class GetRespawningHero : FsmStateAction
{[RequiredField][UIHint(UIHint.Variable)]public FsmBool variable;public override void Reset(){variable = new FsmBool();}public override void OnEnter(){if (GameManager.instance){variable.Value = GameManager.instance.RespawningHero;}base.Finish();}}
如果是真正正在复活玩家的话,就等1秒:
设置初始化为真,
再检查一遍是否装备了蜂巢之血:
当然这第一个血条还有特殊的地方,大家肯定还记得亡者之怒吧,就是当你只有一滴血的时候伤害翻倍,这其中当然有一些视觉效果和听觉效果要完成,但我还没做到护符,先随便写写吧:
做完了第一个血条剩下的不是随随便便,需要注意更改的是,其它血条没有亡者之怒fury particle的效果和playmaker,但是它们有寻神者的绑定血条的子对象Idle Bound:(记得关掉它的meshrenderer,我展示给你们看看的)
其它要改的是它们的playmakerFSM,你是第几个血条就输入Health Number为几
OK复制粘贴我们就做了11个血条,还有它们的完整行为:
你们肯定注意到我有个子对象Joni Health没讲,其实这个是生命水来的,但我还没做到,先搭个对象剩下的以后来做:
还有两个内容我打算到下一期再讲,这期先做到这里吧。
总结
OK我们来展示一下Soul Orb和Health的效果:游戏刚开始,系统判断玩家第一次游玩游戏,所以会延迟显示
由于我们在playerdata设置的满血为5,所以他就显示5个血条:
当你的法力值soulorb满了以后,播放粒子效果,显示眼睛eye,你这时候再打法力值也不会接着增加了。
然后最厉害的回血的时候,法力条犹如一个容器里液体的涌动,
当法力条低于一般的时候,眼睛就会消失,
OK下一期哦我们来制作游戏的金钱系统和额外内容的初始搭建。
相关文章:
[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十集:制作专门渲染HUD的相机HUD Camera和画布HUD Canvas
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、制作HUD Camera以及让两个相机同时渲染屏幕二、制作HUD Canvas 1.制作法力条Soul Orb引入库2.制作生命条Health读入数据3.制作吉欧统计数Geo Counter4.制作…...
《AI大模型开发笔记》——Prompt提示词最佳实践
为什么需要提示词优化 什么是 Prompt enginnering? 提示词:解释一下什么是大语言模型的Prompt enginnering 图1. 什么是Prompt Enginnering? 通过上面ChatGPT的说明,我们可以看到,prompt工程化就是如何写prompt提示…...
Frontend - 防止多次请求,避免重复请求
目录 一、避免重复执行的多种情况 (一)根据用途 (二)根据用户操作 二、具体实现 (一)“Ajax ”结合disabled (防止多次请求),避免多次点击重复请求 1. 适用场景 2. 解决办法 3. 示例 &…...
MongoDB 简介
MongoDB 简介 MongoDB 是一种流行的 NoSQL 数据库管理系统,以其灵活的数据模型、高性能和易于扩展的特点而闻名。本文将详细介绍 MongoDB 的基本概念、特点、使用场景以及如何在实践中应用。 1. MongoDB 基本概念 1.1 文档数据库 MongoDB 是一个文档数据库,它使用 JSON 风…...
Ngrok实现内网穿透(Windows)
Ngrok实现内网穿透(Windows) 什么是内网穿透,内网穿透有什么用 内网穿透(NAT traversal)是一种技术手段,使得位于内网或防火墙后面的设备能够通过外网访问。例如,如果你的计算机、服务器等设备…...
【Python-办公自动化】实现自动化输出模板表格报告
import pandas as pd import numpy as np# 定义时间范围 date_range = pd.date_range(start=2023-11-01, end=2024-10-31, freq=M...
医学AI公开课·第一期|Machine LearningTransformers in Med AI
小罗碎碎念 从这周开始,我计划每个周末录一个视频,分享一些医学人工智能领域的进展。 作为第一期视频,我打算介绍一下机器学习和Transformer在医学AI领域中的应用。 为了准备这期视频,总共做了24页PPT(三部分内容&…...
ESP8266 STA模式TCP服务器 电脑手机网络调试助手
STA模式TCP服务器和手机电脑网络调试助手多连接...
Transformer架构笔记
Attention is All You Need. 3.Model Architecture 3.1 整体架构如图 3.2 Encoder与Decoder Encoder:由 N 6 N6 N6个相同的Block/Layer堆叠而成。每个Block有两个子层sub-layer:多头注意力和MLP(FFN,前馈神经网络)&…...
初学 flutter 环境变量配置
一、jdk(jdk11) 1)配置环境变量 新增:JAVA_HOMEC:\Program Files\Java\jdk-11 //你的jdk目录 在path新增:%JAVA_HOME%\bin2)验证是否配置成功(cmd运行命令) java java -version …...
【大数据技术基础】 课程 第8章 数据仓库Hive的安装和使用 大数据基础编程、实验和案例教程(第2版)
第8章 数据仓库Hive的安装和使用 8.1 Hive的安装 8.1.1 下载安装文件 访问Hive官网(http://www.apache.org/dyn/closer.cgi/hive/)下载安装文件apache-hive-3.1.2-bin.tar.gz 下载完安装文件以后,需要对文件进行解压。按照Linux系统使用的…...
Postman之newman
系列文章目录 1.Postman之安装及汉化基本使用介绍 2.Postman之变量操作 3.Postman之数据提取 4.Postman之pm.test断言操作 5.Postman之newman Postman之newman 1.基础环境node安装1.1.配置环境变量1.2.安装newman和html报告组件 2.newman运行 newman可以理解为,没有…...
Lua 实现继承的一种方式
以下代码来自Loxodon Framework,截取自其中的一段 function class(classname, super)local cls {}cls.__classname classnamecls.__class clscls.base function (self)return cls.superendcls.__type 0cls.super supercls.__index clsif super thensetmetat…...
相机网卡开启巨型帧和关闭节能模式方法
2022 年 8 月 2 日 Tank 阅读次数(ip/1年): 26,796 win10为例子 首先在开始菜单搜索:网络连接 对想要设置的网络右键:属性 点 配置 高级里面找到这三个选项,参考下图设置,螃蟹网卡建议关掉所有节能有关的…...
如何在 Ubuntu 22.04 上安装带有 Nginx 的 ELK Stack
今天我们来聊聊如何在 Ubuntu 22.04 服务器上安装 ELK Stack,并集成 Nginx 作为 Web 服务器,同时使用 Let’s Encrypt Certbot 进行 SSL 认证。ELK Stack,包括 Elasticsearch、Logstash 和 Kibana,是一套强大的工具,用…...
Android中的依赖注入(DI)框架Hilt
Hilt 是 Android 提供的一种依赖注入(DI)框架,它基于 Dagger,目的是简化依赖注入的使用,提供更易用的接口和与 Android 生命周期组件的紧密集成。下面是 Hilt 的详细介绍。 为什么选择 Hilt? 依赖注入的优势…...
笔记记录 k8s操作
docker下载arm架构的镜像 docker pull centos --platform arm64 其中华为鲲鹏、飞腾CPU采用的是ARM架构,龙芯采用的是MIPS架构,而兆芯、海光CPU采用的是X86架构,申威采用的是Alpha架构 docker查看容器的日志文件目录 docker inspect --format={{.LogPath}} containername…...
掌握Go语言中的异常控制:panic、recover和defer的深度解析
掌握Go语言中的异常控制:panic、recover和defer的深度解析 在Go语言的编程世界中,异常处理是一个不可忽视的话题。Go语言提供了panic、recover和defer三个关键字来处理程序中的异常情况。本文将深入探讨这三个关键字的工作原理、使用场景和最佳实践,帮助读者在实际编程中更…...
SpringBoot项目部署到云服务器全流程
文章目录 一、前期准备(一)云服务器选择(二)本地环境准备(三)数据库准备(若项目需要) 二、服务器配置(一)获取服务器信息(二)重置实例…...
对传统加密算法降维打击?!——量子计算
量子计算 声明! 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无…...
什么是Lodash,有什么特点
什么是 Lodash? Lodash 是一个 JavaScript 工具库,提供了一系列实用的函数来处理常见的编程任务,如数组操作、对象操作、字符串处理等。Lodash 由 John-David Dalton 创建,旨在提供一个更全面、更高效的替代方案,以弥…...
Simulink中Model模块的模型保护功能
在开发工作过程中,用户为想要知道供应商的开发能力,想要供应商的模型进行测试。面对如此要求,为了能够尽快拿到定点项目,供应商会选择一小块算法或是模型以黑盒的形式供客户测试。Simulink的Model模块除了具有模块引用的功能之外&…...
【电子通识】LED的一些基础知识
什么是LED LED是被称为"发光二极管"的半导体,名称取至 "Light Emitting Diode" 的首字母。 从爱迪生1879年发明白炽灯后,白炽灯统治了照明一百多年,为世界带来光明。而在21世纪,白炽灯却早已被取代࿰…...
React Native 基础
React 的核心概念 定义函数式组件 import组件 要定义一个Cat组件,第一步要使用 import 语句来引入React以及React Native的 Text 组件: import React from react; import { Text } from react-native; 定义函数作为组件 const CatApp = () => {}; 渲染Text组件...
C语言 蓝桥杯某例题解决方案(查找完数)
蓝桥杯原题: 一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6 1 2 3.编程找出1000以内的所有完数。 这个题没有很大的难点,与我们上一个解决的问题“质因数分解”不同,它不需要判断因数是否是质数,因此…...
【shodan】(三)vnc漏洞利用
shodan基础(三) 声明:该笔记为up主 泷羽的课程笔记,本节链接指路。 警告:本教程仅作学习用途,若有用于非法行为的,概不负责。 count count命令起到一个统计计数的作用。 用上节的漏洞指纹来试…...
蚁群算法(Ant Colony Optimization, ACO)
简介 蚁群算法(Ant Colony Optimization, ACO)是一种基于自然启发的优化算法,由意大利学者马可多里戈(Marco Dorigo)在1992年首次提出。它受自然界中蚂蚁觅食行为的启发,用于解决离散优化问题。 在自然界…...
python可视化将多张图整合到一起(画布)
这周有点事忙着,没时间重温刚结束的Mathurcup数学建模,这两天也是再看了下,论文还是赶紧挺烂的,但比国赛又有进步(说起国赛又不得不抱怨了,基本其余省份都发了,但江西......哎)。哎&…...
计算机网络socket编程(5)_TCP网络编程实现echo_server
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 计算机网络socket编程(5)_TCP网络编程实现echo_server 收录于专栏【计算机网络】 本专栏旨在分享学习计算机网络的一点学习笔记,欢迎大家在评论区交…...
w055基于web的服装生产管理的设计与实现
🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…...
hadoop集群搭建
一、环境准备⼯作: 确保已经按照以下文档安装完毕:虚拟机和远程工具的安装(hadoop集群安装01)-CSDN博客 Linux设置以及软件的安装(hadoop集群安装02)-CSDN博客 虚拟机的克隆和免密(hadoop集群03)-CSDN博…...
JVM中TLAB(线程本地分配缓存区)是什么
JVM中TLAB(线程本地分配缓存区)是什么 简单来说 TLAB,线程本地分配缓存区,是在 Java 堆内存中的一块线程专属的内存区域,每个线程在创建对象时,首先会尝试在自己的 TLAB 区域内分配内存,这样多…...
如何使用 MMPreTrain 框架
如何使用 MMPreTrain 框架进行预训练模型的微调和推理 MMPreTrain 是一个基于 PyTorch 的开源框架,专注于图像分类和其他视觉任务的预训练模型。它提供了丰富的预训练模型和便捷的接口,使得研究人员和开发者可以轻松地进行模型微调和推理。本文将详细介…...
Python的tkinter如何把日志弄进文本框(Text)
当我们用python的Tkinter包给程序设计界面时,在有些时候,我们是希望程序的日志显示在界面上的,因为用户也需要知道程序目前运行到哪一步了,以及程序当前的运行状态是否良好。python的通过print函数打印出来的日志通常显示在后台&a…...
bash笔记
0 $0 是脚本的名称,$# 是传入的参数数量,$1 是第一个参数,$BOOK_ID 是变量BOOK_ID的内容 1 -echo用于在命令窗口输出信息 -$():是命令替换的语法。$(...) 会执行括号内的命令,并将其输出捕获为一个字符串ÿ…...
嵌入式工程师面试题 - 2024/11/19
欢迎找我进行职业规划,超值的自我投资 -> 嵌入式软件工程师一对一指导 转载请注明来源 1.以下叙述中正确的是() A 程序必须包含所有三种基本结构才能成为一种算法 B 我们所写的每条C语句,经过编译最终都将转换成二进制的机器…...
springboot配置https,并使用wss
学习链接 springboot如何将http转https SpringBoot配置HTTPS及开发调试 可借鉴的参考: springboot如何配置ssl支持httpsSpringBoot配置HTTPS及开发调试的操作方法springboot实现的https单向认证和双向认证(java生成证书)SpringBoot配置Https访问的详细步骤Sprin…...
【C语言】连接陷阱探秘(3):形参、实参与返回值
目录 一、形参的 “迷障” 1.1. 定义与功能 1.2. 类型不匹配 1.3. 数量不一致 1.4. 顺序不一致 1.5. 数组形参退化 二、实参的 “暗礁” 2.1. 定义与功能 2.2. 求值顺序 “谜题” 2.3. 悬空指针 “深渊” 三、返回值的 “陷阱” 3.1. 定义与功能 3.2. 陷阱与缺陷 …...
学习笔记030——若依框架中定时任务的使用
定时任务是软件开发中经常使用一个功能。 Java定时任务广泛应用于各种需要定时执行或周期性执行任务的场景,如: 数据备份:定期备份数据库中的数据,确保数据的安全性和可靠性。数据同步:如果有多个数据源需要同步数据…...
基于SpringBoot的数据结构系统设计与实现(源码+定制+开发)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
利用 Python 和 Selenium 高效启动和管理 Chrome 浏览器
在自动化测试和网页抓取的过程中,Selenium 是最常用的工具之一。其强大的功能可以与浏览器无缝集成,实现复杂的操作。然而,为了提高效率和扩展性,尤其在处理大量任务时,我们可以通过定制化的方法启动 Chrome 浏览器并与…...
外卖系统开发实战:从架构设计到代码实现
开发一套外卖系统,需要在架构设计、技术选型以及核心功能开发等方面下功夫。这篇文章将通过代码实例,展示如何构建一个基础的外卖系统,从需求梳理到核心模块的实现,帮助你快速掌握开发要点。 一、系统架构设计 一个完整的外卖系…...
类和对象(上)--类、类的实例化(对象)、this指针
1.类 1.1定位: 和namespace一样,类也有类域。同样起到既保护又限制的功能。别人不能随意访问类里的东西,得通过特定的方式来访问(访问方法和命名空间域一样,三种方法)。 1.2作用 在C语言中,…...
使用ENSP实现浮动静态路由
一、项目拓扑 二、项目实现 1.路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1关闭信息中心 undo info-center enable 进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为1.1.1.1/24 ip address 1.1.1.1 24进入g0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置为2.…...
JavaEE 【知识改变命运】02 多线程(1)
文章目录 线程是什么?1.1概念1.1.1 线程是什么?1.1.2 为什么要有线程1.1.3 进程和线程的区别1.1.4 思考:执行一个任务,是不是创建的线程或者越多是不是越好?(比如吃包子比赛)1.1.5 ) Java 的线程…...
pytorch官方FasterRCNN代码详解
本博文转自捋一捋pytorch官方FasterRCNN代码 - 知乎 (zhihu.com),增加了其中代码的更详细的解读,以帮助自己理解该代码。 代码理解的参考Faster-RCNN全面解读(手把手带你分析代码实现)---前向传播部分_手把手faster rcnn-CSDN博客 1. 代码结构 作为 to…...
【线程】Java多线程编程
【线程】Java多线程编程 一、前言一个最简单的多线程编程示例可以使用的工具 二、创建线程的方式三、Thread类中重要的属性和方法3.1 构造方法3.2 常见属性 一、前言 当有多个线程的时候,这些线程的执行顺序是不确定的。这一点,是我们之前提到的操作系统…...
4.2 MySQL存储过程
存储过程是MySQL中用于封装一组 SQL 语句的数据库对象,便于简化重复任务、增强性能和逻辑复用。本文将从多个角度详细解析存储过程的功能、语法和应用场景。 1. 存储过程概述 1.1 什么是存储过程 存储过程(Stored Procedure)是一段在数据库…...
archlinux安装waydroid
目录 参考资料 注意 第一步切换wayland 第二步安装binder核心模组 注意 开始安装 AUR安裝Waydroid 启动waydroid 设置网络(正常的可以不看) 注册谷歌设备 安装Arm转译器 重启即可 其他 参考资料 https://ivonblog.com/posts/archlinux-way…...
C语言数学函数库<math.h>的常用函数讲解
math函数 一. 基础数学函数1. fabs(double x) — 绝对值2. fmod(double x, double y) — 余数3. pow(double x, double y) — 幂运算4. sqrt(double x) — 平方根5. cbrt(double x) — 立方根6. hypot(double x, double y) — 计算斜边 二. 对数和指数函数1. exp(double x) — …...