当前位置: 首页 > news >正文

[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十一集:制作游戏的金钱系统吉欧Geo和初步制作HUD Canvas的额外内容

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

文章目录

  • 前言
  • 一、制作游戏的金钱系统吉欧Geo以及HUD Camera
    • 1.制作金钱系统吉欧
    • 2.制作吉欧的脚本Geo Counter逻辑处理
    • 3.制作HUD Canvas的吉欧的UI
    • 4.在敌人的HealthManager.cs脚本中添加上吉欧的逻辑处理
  • 二、制作HUD Canvas的初始的额外内容
  • 总结


前言

        hello大家好久没见,之所以隔了这么久才更新并不是因为我又放弃了这个项目,而是接下来要制作的工作太忙碌了,每次我都花了很长的时间解决完一个部分,然后就没力气打开CSDN写文章就直接睡觉去了,而且还有就是我上一期写了一万字的内容结果系统出BUG给我没保存直接气晕了,现在刚刚醒来终于有时间整理下我新制作的内容了,还有就是感谢兄弟们的评论支持,看到你的评论我决定还是接着制作这个系列的内容。

        那么,这个HUD是什么意思呢?百度翻译就是平行显示系统,一般用于车辆上面的某个系统,但它也可以作为了一个专业的游戏术语,显示与摄像机平行的东西,如果你还没理解的话,我就直接上图吧:就是这些不会随着摄像机移动而相对移动的物品,这些看似是UI来实现的也可以使用摄像机的功能来实现:

        然后这一期我们来制作金钱系统吉欧Geo和初步制作HUD Canvas的额外内容


一、制作游戏的金钱系统吉欧Geo以及HUD Camera

1.制作金钱系统吉欧Geo

        为了贴近游戏一点,我们就不称金钱系统了,直接就叫吉欧得了,然后它的英文名叫Geo,阅读这篇文章先要注意一下这点。

        OK我们先来制作tk2dSprtite和tk2dspriteAnimator:先把素材导入吧

接下来是制作动画,这里需要注意名字不要打错,一会脚本还要用:

2.制作吉欧的脚本Geo Counter逻辑处理

        创建一个新的游戏对象就叫Geo Small,然后就是经典四件套组件:

 先来讲讲它的子对象Bug,这个就是那个捡钱的护符,当我们暂时还用不到,创建好先放一边了:

接下来这个子对象Acid Steam的粒子系统就是当钱掉进水里后的粒子系统:

接下来来看看脚本Geo Control.cs:游戏中三种面值的Geo,分别small,med,large,它们代表的面值分别是1,5,25。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GeoControl : MonoBehaviour
{public Size[] sizes = new Size[]{new Size("Small Idle", "Small Air", 1),new Size("Med Idle", "Med Air", 5),new Size("Large Idle", "Large Air", 25)};public int type;private Size size;[Space]public AudioClip[] pickupSounds;[Space]public ParticleSystem acidEffect;public GameObject getterBug;private Coroutine getterRoutine;private HeroController hero;private Transform player;private bool activated;private bool attracted; //是否触发捡钱吸入到玩家上private const float pickupStartDelay = 0.25f;private float pickupStartTime;private float defaultGravity;private tk2dSpriteAnimator anim;private AudioSource audioSource;private Renderer rend;private Rigidbody2D body;private BoxCollider2D boxCollider;private SpriteFlash spriteFlash;private void Awake(){anim = GetComponent<tk2dSpriteAnimator>();audioSource = GetComponent<AudioSource>();rend = GetComponent<Renderer>();body = GetComponent<Rigidbody2D>();boxCollider = GetComponent<BoxCollider2D>();spriteFlash = GetComponent<SpriteFlash>();defaultGravity = body.gravityScale;}private void Start(){hero = HeroController.instance;}private void OnEnable(){SetSize(type);transform.SetPositionZ(UnityEngine.Random.Range(0.001f, 0.002f));activated = false;attracted = false;body.gravityScale = defaultGravity;if (rend){rend.enabled = true;}if (getterBug){getterBug.SetActive(false);}if (acidEffect){acidEffect.gameObject.SetActive(false);}boxCollider.isTrigger = false;if (GameManager.instance.sceneName == "Crossroads_38"){return;}
//判断玩家是否装备了捡钱的护符if (GameManager.instance.GetPlayerDataBool("equippedCharm_1")){getterRoutine = StartCoroutine(Getter());}pickupStartTime = Time.time + pickupStartDelay;}private void FixedUpdate(){if (attracted){Vector2 vector = new Vector2(hero.transform.position.x - transform.position.x, hero.transform.position.y - 0.5f - transform.position.y);vector = Vector2.ClampMagnitude(vector, 1f);vector = new Vector2(vector.x * 150f, vector.y * 150f);body.AddForce(vector);Vector2 vector2 = body.velocity;vector2 = Vector2.ClampMagnitude(vector2, 20f);body.velocity = vector2;}}private void SetSize(int index){if (index >= sizes.Length){index = sizes.Length - 1;}else if (index < 0){index = 0;}size = sizes[index];if (anim){anim.Play(size.airAnim);}}private IEnumerator Getter(){yield return new WaitForSeconds(UnityEngine.Random.Range(1f, 1.7f));if (getterBug){//插值执行捡钱虫子开始时淡入淡出效果getterBug.SetActive(true);Vector3 destination = new Vector3(-0.06624349f, 0.1932119f, -0.001f);Vector3 source = destination + new Vector3(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(0.5f, 1.5f), 0f);float easeTime = UnityEngine.Random.Range(0.3f, 0.5f);for (float timer = 0f; timer < easeTime; timer += Time.deltaTime){float t = Mathf.Sin(timer / easeTime * 1.5707964f);getterBug.transform.localPosition = Vector3.Lerp(source, destination, t);yield return null;}getterBug.transform.localPosition = destination;boxCollider.isTrigger = true;body.gravityScale = 0f;attracted = true; //然后激活attracted让虫子和钱朝着玩家的方向移动destination = default(Vector3);source = default(Vector3);}}/// <summary>/// 当吉欧碰到地面上/// </summary>/// <param name="collision"></param>private void OnCollisionEnter2D(Collision2D collision){if (anim){tk2dSpriteAnimationClip clipByName = anim.GetClipByName(size.idleAnim);if(clipByName != null){anim.PlayFromFrame(clipByName, UnityEngine.Random.Range(0, clipByName.frames.Length));}}}private void OnTriggerEnter2D(Collider2D collision){if (activated || Time.time < pickupStartTime){return;}bool flag = false;float num = 0f;if(collision.tag == "HeroBox") //如果碰到玩家了就加钱{Debug.LogFormat("size.value = " + size.value);hero.AddGeo(size.value);num = Mathf.Max(num, PlayCollectSound());flag = true;}else if (collision.tag == "Acid") //如果掉到酸水里了就播放特效{if (acidEffect){acidEffect.gameObject.SetActive(true);num = Mathf.Max(num, acidEffect.main.duration + acidEffect.main.startLifetime.constant);}flag = true;}if (flag) //暂停捡钱的虫子的routine,销毁该物体{if (getterRoutine != null){StopCoroutine(getterRoutine);}Disable(num);}}/// <summary>/// 获取随机一个音频的播放时间/// </summary>/// <returns></returns>private float PlayCollectSound(){if (audioSource && pickupSounds.Length != 0){AudioClip audioClip = pickupSounds[UnityEngine.Random.Range(0, pickupSounds.Length)];if (audioClip){audioSource.PlayOneShot(audioClip);return audioClip.length;}Debug.LogError("GeoControl encountered missing audio!", this);}return 0f;}/// <summary>/// 关闭捡钱和自身的renderer/// </summary>/// <param name="waitTime"></param>private void Disable(float waitTime){activated = true;if (rend){rend.enabled = false;}if (getterBug){getterBug.SetActive(false);}StartCoroutine(DisableAfterTime(waitTime));}private IEnumerator DisableAfterTime(float waitTime){yield return new WaitForSeconds(waitTime);gameObject.Recycle();}public void SetFlashing(){if (spriteFlash){spriteFlash.GeoFlash();}}[Serializable]public struct Size{public string idleAnim;public string airAnim;public int value;public Size(string idleAnim, string airAnim, int value){this.idleAnim = idleAnim;this.airAnim = airAnim;this.value = value;}}
}

然后我们来制作三个面值的吉欧,首先是最小的small,它的type设置为0,注意动画名字的tk2dspriteanimator保持一致,

然后是5块钱面值的中型吉欧,记得改下碰撞箱的大小和type类型

 

最后是最大面值的吉欧。 

回到HeroController.cs中,创建新的方法:

  public void AddGeo(int amount){playerData.AddGeo(amount);geoCounter.AddGeo(amount);}public void AddGeoQuietly(int amount){playerData.AddGeo(amount);}public void ToZero(){geoCounter.ToZero();}public void AddGeoToCounter(int amount){geoCounter.AddGeo(amount);}public void TakeGeo(int amount){playerData.TakeGeo(amount);geoCounter.TakeGeo(amount);}public void UpdateGeo(){geoCounter.UpdateGeo();}

先看PlayerData.cs这边:说白了我们就是存储了两个int类型和一个bool类型,并打算在HUD屏幕上显示我们拥有的Geo数量。

    public int geo; //玩家拥有的吉欧public int geoPool; //玩家死后存储死前拥有的吉欧public bool firstGeo; //是否是得到的第一块钱public void AddGeo(int amount){geo += amount;if (geo > 9999999){geo = 9999999;}}public void TakeGeo(int amount){geo -= amount;}

那么这个脚本GeoCounter.cs是什么呢?自然就是我们下一部分的内容HUD Canvas上的吉欧的UI!

3.制作HUD Canvas的吉欧的UI

在HUD Canvas下,我们创建一个新的对象Geo Counter:

它只有几个子对象,首先是吉欧的图标Geo Sprite:

先来讲讲它的两个playmakerFSM 

这个playmakerFSM 上几期都讲过了就不浪费时间了。

再来看看另一个playmakerFSM:Geo Sprite,先添加上事件和变量

第一个状态就是初始化,先记录自己一开始的位置,判断是否是firstGeo,

如果是的话,就进入First Geo Uncollected状态,获取自己的父对象,以及对对象Geo Text发送事件DOWN,

然后设置playerdata的bool变量:firstGeo为true,播放动画Coin Appear,向Geo Text发送事件UP  

回到Init状态,如果不是,我们就来判断是否完成初始化,还有是否是第一次游玩游戏isFirstGame,这个我们在上一期讲生命的时候也提到过。

如果没有完成初始化或者是第一次游玩游戏,那么就进入pause状态:初始化变量以及向geo text发送事件DOWN INSTANT,等个4秒进入下一个状态:

这个和First Get状态的内容差不多: 

来到Idle状态,设置自身回到初始位置,播放动画Coin Get,开启自身renderer。 

它身上有两个事件都是通过脚本GeoCotontrol.cs来发送的,所以我们先看看其它状态,如果发送TAKE事件进入take状态:

花钱了话就通过行为Object Jitter.cs来进行丝滑的移动动画

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;}}
}
using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Transform)][Tooltip("Jitter an object around using its Transform.")]public class ObjectJitterLocal : FsmStateAction{[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;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 = Fsm.GetOwnerDefaultTarget(gameObject);if (ownerDefaultTarget == null){return;}startX = ownerDefaultTarget.transform.localPosition.x;startY = ownerDefaultTarget.transform.localPosition.y;startZ = ownerDefaultTarget.transform.localPosition.z;}public override void OnFixedUpdate(){DoTranslate();}private void DoTranslate(){GameObject ownerDefaultTarget = Fsm.GetOwnerDefaultTarget(gameObject);if (ownerDefaultTarget == null){return;}Vector3 localPosition = 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.localPosition = localPosition;}}
}

 当关卡变换的时候就会发送LEVEL LOADED事件,这里我不知道我前面讲过没,还有就是IDLE事件也是Geo Control.cs发送的。

接下来这两个事件都是会在玩家死亡后发送的,所以当我讲到那一期的时候就会用到这两个事件:

然后TO ZERO事件作为一个全球传送Global Transition发送到Lose状态,

SHATTER事件也是作为一个全球传送Global Transition发送到Shatter状态

关闭掉自身的renderer的同时发送Geo Text的DOWN事件 

当进入新的关卡,也就是回到复活场景的时候,发geo text的UP事件 

来到另一个子对象Geo Text,这里我们用的组件是Text Mesh:注意设置好Material类型,还有字体大小Font Size,它的playmakerFSM也是color_fader,之前讲过了就不谈了直接复制粘贴就完事了

另一个子对象就是Add Text,说明白了就是加钱的效果,它也有个text mesh,然后它的playmakerFSM也是很简单: 

Down状态就是用Ease渐变的方式设置material的color和它的alpha值为0 

 UP状态就是完全反过来。

最后一个Subtract Text就是花钱的效果动画,和上面同理这里就不多bb了

 回到父对象Geo Counter来,我还漏了一个忘了讲,就是设置一个playmaker的全球变量Geo Counter,然后新建一个playmakerFSM让全球变冷指向自己:

最后我们终于可以讲讲传说中的脚本GeoCounter.cs了:这段代码有点难理解,我这里简单解释一下吧,首先增加吉欧和减少吉欧都分为两个int类型的阶段表示,分别是addRollerState和takeRollerState,当它们的state为0的时候就什么都不做,当state为1的时候就延迟准备进入各自的动画逻辑处理状态,当state为2的时候进入逻辑处理状态,播放动画,发送事件,更改textmesh的text等等。。。然后我们在第一部分讲到AddGeo和TakeGeo都是切换不同的状态以及更改changePerTick的值。

using System;
using UnityEngine;public class GeoCounter : MonoBehaviour
{private const float ROLLER_START_PAUSE = 1.5f;private const float DIGIT_CHANGE_TIME = 0.025f;[HideInInspector]public PlayerData playerData;public GameObject geoSprite;public TextMesh geoTextMesh;public TextMesh subTextMesh;public TextMesh addTextMesh;private PlayMakerFSM geoSpriteFsm;private PlayMakerFSM addTextFsm;private PlayMakerFSM subTextFsm;private int counterCurrent; //当前拥有的吉欧private int geoChange; //需要改变的geo数量private int addCounter;//需要增加的吉欧的计数器private int takeCounter;//需要减少的吉欧的计数器private int addRollerState; //增加吉欧的动画的阶段private int takeRollerState;//减少吉欧的动画的阶段private int changePerTick; //每次执行tick动画减少的吉欧数量private float addRollerStartTimer; //开始增加吉欧前的计时器private float takeRollerStartTimer;//开始减少吉欧前的计时器private float digitChangeTimer; //每次当这个变量digitChangeTimer小于0的时候就播放一次加钱的动画private bool toZero; //将吉欧归零private void Awake(){geoSpriteFsm = FSMUtility.GetFSM(geoSprite);subTextFsm = FSMUtility.GetFSM(subTextMesh.gameObject);addTextFsm = FSMUtility.GetFSM(addTextMesh.gameObject);}private void Start(){playerData = PlayerData.instance;counterCurrent = playerData.geo;geoTextMesh.text = counterCurrent.ToString();}private void Update(){if (toZero){if(digitChangeTimer >= 0f){digitChangeTimer -= Time.deltaTime;return;}if(counterCurrent > 0){counterCurrent += changePerTick;if (counterCurrent <= 0){counterCurrent = 0;geoSpriteFsm.SendEvent("SHATTER");toZero = false;}geoTextMesh.text = counterCurrent.ToString();digitChangeTimer += DIGIT_CHANGE_TIME;return;}}else{if (addRollerState == 1) //延迟进入吉欧增加的状态{if (addRollerStartTimer > 0f){addRollerStartTimer -= Time.deltaTime;}else{addRollerState = 2;}}if (addRollerState == 2 && addCounter > 0) //进入吉欧增加的动画,切要保证增加的吉欧数量大于0{geoSpriteFsm.SendEvent("GET");if (digitChangeTimer < 0f){addCounter -= changePerTick;//这里的changePerTick应该是正数,最小不小于-1counterCurrent += changePerTick;geoTextMesh.text = counterCurrent.ToString();if (addTextMesh != null){addTextMesh.text = "+ " + addCounter.ToString();}if (addCounter <= 0)//如果累计增加的addCounter小于等于0以后,就说明状态执行完毕已完成整个增加吉欧的行为,就回到addRollerState = 0状态{geoSpriteFsm.SendEvent("IDLE");addCounter = 0;addTextMesh.text = "+ 0";addRollerState = 0;counterCurrent = playerData.geo;geoTextMesh.text = counterCurrent.ToString();addTextFsm.SendEvent("DOWN");}digitChangeTimer += DIGIT_CHANGE_TIME;}else{digitChangeTimer -= Time.deltaTime;}}if (takeRollerState == 1)//延迟进入吉欧减少的状态{if (takeRollerStartTimer > 0f){takeRollerStartTimer -= Time.deltaTime;}else{takeRollerState = 2;}}if (takeRollerState == 2 && takeCounter < 0)//进入吉欧减少的动画状态,切要保证减少的吉欧数量大于0{geoSpriteFsm.SendEvent("TAKE"); //向geoSpriteFsm发送事件TAKEif (digitChangeTimer < 0f) //每一次digitChangeTimer的时间小于0的时候就改变changePerTick数量的takeCounter和counterCurrent{takeCounter -= changePerTick; //这里的changePerTick应该是负数,最多不大于-1counterCurrent += changePerTick;geoTextMesh.text = counterCurrent.ToString();if (subTextMesh != null){subTextMesh.text = "- " + (-takeCounter).ToString();}if (takeCounter >= 0) //如果累计减少的takeCounter大于等于0以后,就说明状态执行完毕已完成整个减少吉欧的行为,就回到takeRollerState = 0状态{geoSpriteFsm.SendEvent("IDLE");takeCounter = 0;subTextMesh.text = "- 0";takeRollerState = 0;counterCurrent = playerData.geo;geoTextMesh.text = counterCurrent.ToString();subTextFsm.SendEvent("DOWN");}digitChangeTimer += DIGIT_CHANGE_TIME;return;}digitChangeTimer -= Time.deltaTime;}}}public void UpdateGeo(){}/// <summary>/// 该函数在playmakerFSM的LEVEL LOADED事件接收者Reset Coutner状态调用,用于场景切换/// </summary>public void NewSceneRefresh(){counterCurrent = playerData.geo;geoTextMesh.text = counterCurrent.ToString();toZero = false;takeRollerState = 0;addRollerState = 0;}//以下都是在HeroController.cs代码中被调用,顾名思义的都是public void AddGeo(int geo){geoSpriteFsm.SendEvent("CHECK FIRST");if(takeRollerState > 0){geoChange = geo;addCounter = geoChange;takeRollerState = 0;geoSpriteFsm.SendEvent("IDLE");subTextFsm.SendEvent("DOWN");counterCurrent = playerData.geo + -addCounter;geoTextMesh.text = counterCurrent.ToString();}if (addRollerState == 0){geoChange = geo;addCounter = geoChange;addTextFsm.SendEvent("UP");addTextMesh.text = "+ " + addCounter.ToString();addRollerStartTimer = ROLLER_START_PAUSE;addRollerState = 1;}else if (addRollerState == 1){geoChange = geo;addCounter += geoChange;addTextMesh.text = "+ " + addCounter.ToString();addRollerStartTimer = ROLLER_START_PAUSE;}else if (addRollerState == 2){geoChange = geo;addCounter = geoChange;geoSpriteFsm.SendEvent("IDLE");counterCurrent = playerData.geo;geoTextMesh.text = counterCurrent.ToString();addTextMesh.text = "+ " + addCounter.ToString();addRollerState = 1;addRollerStartTimer = ROLLER_START_PAUSE;}changePerTick = (int)(addCounter * DIGIT_CHANGE_TIME * 1.75);if (changePerTick < 1){changePerTick = 1;}}public void TakeGeo(int geo){if (addRollerState > 0){geoChange = -geo;takeCounter = geoChange;addRollerState = 0;geoSpriteFsm.SendEvent("IDLE");addTextFsm.SendEvent("DOWN");counterCurrent = playerData.geo + -takeCounter;geoTextMesh.text = counterCurrent.ToString();}if (takeRollerState == 0){geoChange = -geo;takeCounter = geoChange;subTextFsm.SendEvent("UP");subTextMesh.text = "- " + (-takeCounter).ToString();takeRollerStartTimer = ROLLER_START_PAUSE;takeRollerState = 1;}else if (takeRollerState == 1){geoChange = -geo;takeCounter += geoChange;subTextMesh.text = "- " + (-takeCounter).ToString();takeRollerStartTimer = ROLLER_START_PAUSE;}else if (takeRollerState == 2){geoChange = -geo;takeCounter = geoChange;geoSpriteFsm.SendEvent("IDLE");counterCurrent = playerData.geo;geoTextMesh.text = counterCurrent.ToString();subTextMesh.text = "- " + (-takeCounter).ToString();takeRollerState = 1;takeRollerStartTimer = ROLLER_START_PAUSE;}changePerTick = (int)((double)((float)takeCounter * DIGIT_CHANGE_TIME) * 1.75);if (changePerTick > -1){changePerTick = -1;}}public void ToZero(){if(counterCurrent == 0){geoSpriteFsm.SendEvent("SHATTER");return;}changePerTick = -(int)((float)counterCurrent * DIGIT_CHANGE_TIME * 1.75f);if(changePerTick > -1){changePerTick = -1;}geoSpriteFsm.SendEvent("TO ZERO");toZero = true;}
}

顺便更下静态脚本FSMUtility,这个脚本是更方便的使用playmakerFSM内置的一些方法,我之前讲过但太久没更新了我就把最新版的贴出来。  

using System;
using System.Collections.Generic;
using HutongGames.PlayMaker;
using UnityEngine;public static class FSMUtility
{private static List<List<PlayMakerFSM>> fsmListPool;private const int FsmListPoolSizeMax = 20;static FSMUtility(){fsmListPool = new List<List<PlayMakerFSM>>();}public static PlayMakerFSM LocateFSM(GameObject go, string fsmName){if (go == null){return null;}List<PlayMakerFSM> list = ObtainFsmList();go.GetComponents<PlayMakerFSM>(list);PlayMakerFSM result = null;for (int i = 0; i < list.Count; i++){PlayMakerFSM playMakerFSM = list[i];if (playMakerFSM.FsmName == fsmName){result = playMakerFSM;break;}}ReleaseFsmList(list);return result;}private static List<PlayMakerFSM> ObtainFsmList(){if (fsmListPool.Count > 0){List<PlayMakerFSM> result = fsmListPool[fsmListPool.Count - 1];fsmListPool.RemoveAt(fsmListPool.Count - 1);return result;}return new List<PlayMakerFSM>();}public static bool ContainsFSM(GameObject go, string fsmName){if (go == null){return false;}List<PlayMakerFSM> list = ObtainFsmList();go.GetComponents<PlayMakerFSM>(list);bool result = false;for (int i = 0; i < list.Count; i++){if (list[i].FsmName == fsmName){result = true;break;}}ReleaseFsmList(list);return result;}private static void ReleaseFsmList(List<PlayMakerFSM> fsmList){fsmList.Clear();if (fsmListPool.Count < FsmListPoolSizeMax){fsmListPool.Add(fsmList);}}public static PlayMakerFSM GetFSM(GameObject go){return go.GetComponent<PlayMakerFSM>();}public static GameObject GetSafe(this FsmOwnerDefault ownerDefault, FsmStateAction stateAction){if (ownerDefault.OwnerOption == OwnerDefaultOption.UseOwner){return stateAction.Owner;}return ownerDefault.GameObject.Value;}public static void SendEventToGameObject(GameObject go, string eventName, bool isRecursive = false){if (go != null){SendEventToGameObject(go, FsmEvent.FindEvent(eventName), isRecursive);}}public static void SendEventToGameObject(GameObject go, FsmEvent ev, bool isRecursive = false){if (go != null){List<PlayMakerFSM> list = ObtainFsmList();go.GetComponents<PlayMakerFSM>(list);for (int i = 0; i < list.Count; i++){list[i].Fsm.Event(ev);}ReleaseFsmList(list);if (isRecursive){Transform transform = go.transform;for (int j = 0; j < transform.childCount; j++){SendEventToGameObject(transform.GetChild(j).gameObject, ev, isRecursive);}}}}public static int GetInt(PlayMakerFSM fsm, string variableName){return fsm.FsmVariables.FindFsmInt(variableName).Value;}public static Vector3 GetVector3(PlayMakerFSM fsm, string variableName){return fsm.FsmVariables.FindFsmVector3(variableName).Value;}public static void SetBool(PlayMakerFSM fsm, string variableName, bool value){fsm.FsmVariables.GetFsmBool(variableName).Value = value;}public static void SetInt(PlayMakerFSM fsm, string variableName, int value){fsm.FsmVariables.GetFsmInt(variableName).Value = value;}public static void SetFloat(PlayMakerFSM fsm, string variableName, float value){fsm.FsmVariables.GetFsmFloat(variableName).Value = value;}public static void SetString(PlayMakerFSM fsm, string variableName, string value){fsm.FsmVariables.GetFsmString(variableName).Value = value;}public abstract class CheckFsmStateAction : FsmStateAction{public FsmEvent trueEvent;public FsmEvent falseEvent;public abstract bool IsTrue { get; }public override void Reset(){trueEvent = null;falseEvent = null;}public override void OnEnter(){if (IsTrue){Fsm.Event(trueEvent);}else{Fsm.Event(falseEvent);}Finish();}}
}

 

 OK完全解读以后就回到Unity编辑器中托好变量:

4.在敌人的HealthManager.cs脚本中添加上吉欧的逻辑处理

        终于来到最后一步了,既然做了吉欧的其它逻辑处理了,那当然还差一步就是让敌人掉落钱了,我们都知道每一个敌人身上都有healthmanager.cs这个脚本的,

我们在敌人死亡也就是Die()函数中完成:

using System;
using System.Collections;
using HutongGames.PlayMaker;
using UnityEngine;
using UnityEngine.Audio;public class HealthManager : MonoBehaviour, IHitResponder
{[Header("Geo")][SerializeField] private int smallGeoDrops; //掉落多少小型geo[SerializeField] private int mediumGeoDrops;//掉落多少中型geo[SerializeField] private int largeGeoDrops;//掉落多少大型geo[SerializeField] private bool megaFlingGeo; //让geo掉落飞的范围更大[SerializeField] private GameObject smallGeoPrefab;[SerializeField] private GameObject mediumGeoPrefab;[SerializeField] private GameObject largeGeoPrefab;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>());Instantiate<GameObject>(corpseSplatPrefab, transform.position + effectOrigin, Quaternion.identity);if (enemyDeathEffects){enemyDeathEffects.EmitSound();}Destroy(gameObject);return;}if(attackType != AttackTypes.RuinsWater){//随机化生成的geo生成的角度和速度范围float angleMin = megaFlingGeo ? 65 : 80;float angleMax = megaFlingGeo ? 115 : 100;float speedMin = megaFlingGeo ? 30 : 15;float speedMax = megaFlingGeo ? 45 : 30;//各种面值geo生成的数量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;}//生成并让geo全部飞起来GameObject[] gameObjects = FlingUtils.SpawnAndFling(new FlingUtils.Config{Prefab = smallGeoPrefab,AmountMin = num,AmountMax = num,SpeedMin = speedMin,SpeedMax = speedMax,AngleMin = angleMin,AngleMax = angleMax}, 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:}/// <summary>/// 让geo闪烁起来/// </summary>/// <param name="gameObjects"></param>/// <param name="originalAmount"></param>private void SetGeoFlashing(GameObject[] gameObjects, int originalAmount){for (int i = gameObjects.Length - 1; i >= originalAmount; i--){GeoControl component = gameObjects[i].GetComponent<GeoControl>();if (component){component.SetFlashing();}}}
}

我们需要一个静态脚本FlingUtils.cs来控制如何让物体可靠的飞起来:例如设置它的初始的位置和角度,还有最主要的设置不同速度,最好的方法是用正弦余弦函数设置多物体不同的速度:

using UnityEngine;public static class FlingUtils
{public static GameObject[] SpawnAndFling(Config config,Transform spawnPoint,Vector3 positionOffset){if(config.Prefab == null){return null;}int num = Random.Range(config.AmountMin, config.AmountMax + 1);Vector3 a = (spawnPoint != null) ? spawnPoint.TransformPoint(positionOffset) : positionOffset;GameObject[] array = new GameObject[num];for (int i = 0; i < num; i++){Vector3 position = a + new Vector3(Random.Range(-config.OriginVariationX, config.OriginVariationX), Random.Range(-config.OriginVariationY, config.OriginVariationY));GameObject gameObject = config.Prefab.Spawn(position);gameObject.transform.position = position;Rigidbody2D component = gameObject.GetComponent<Rigidbody2D>();if(component != null){float d = Random.Range(config.SpeedMin, config.SpeedMax);float num2 = Random.Range(config.AngleMin, config.AngleMax);component.velocity = new Vector2(Mathf.Cos(num2 * 0.017453292f) * d, Mathf.Sin(num2 * 0.017453292f) * d);}array[i] = gameObject;}return array;}public static void FlingChildren(ChildrenConfig config, Transform spawnPoint, Vector3 positionOffset){if (config.Parent == null){return;}Vector3 a = (spawnPoint != null) ? spawnPoint.TransformPoint(positionOffset) : positionOffset;int num = (config.AmountMax > 0) ? Random.Range(config.AmountMin, config.AmountMax) : config.Parent.transform.childCount;for (int i = 0; i < num; i++){Transform child = config.Parent.transform.GetChild(i);child.gameObject.SetActive(true);child.transform.position = a + new Vector3(Random.Range(-config.OriginVariationX, config.OriginVariationX), Random.Range(-config.OriginVariationY, config.OriginVariationY), 0f);Rigidbody2D component = child.GetComponent<Rigidbody2D>();if (component != null){float d = Random.Range(config.SpeedMin, config.SpeedMax);float num2 = Random.Range(config.AngleMin, config.AngleMax);component.velocity = new Vector2(Mathf.Cos(num2 * 0.017453292f), Mathf.Sin(num2 * 0.017453292f)) * d;}}}public struct Config{public GameObject Prefab;public float SpeedMin;public float SpeedMax;public float AngleMin;public float AngleMax;public float OriginVariationX;public float OriginVariationY;public int AmountMin;public int AmountMax;}public struct ChildrenConfig{public GameObject Parent;public int AmountMin;public int AmountMax;public float SpeedMin;public float SpeedMax;public float AngleMin;public float AngleMax;public float OriginVariationX;public float OriginVariationY;}public struct SelfConfig{public GameObject Object;public float SpeedMin;public float SpeedMax;public float AngleMin;public float AngleMax;}}

二、制作HUD Canvas的初始的额外内容

这部分时比较水的,就是只是添加上额外内容的MeshRenderer没有技术含量的,做完记得关闭它们的meshrenderer:

首先是Blocker HUD Icon,说实话我都没用过这个护符,不懂是干嘛的:

然后是大部分耳熟能详的梦之钉, 这里也就做一个展示而已没有实际效果

然后是寻神者模式下的禁用图标还有寻神者模式        :

内容太水了哈哈,没事大家就看个乐吧。


总结

        然后记得为每一个敌人的healthmanager.cs添加好三个面值的geo的预制体,以及这个敌人要生成多少数量的不同面值的geo吉欧,来到测试场景,这里我就举个例子,比如这个后面要讲的敌人螳螂,我给它设置的参数是这样的:

7个一元面值的小geo,2个5元面值的中geo。运行游戏后,geo sprite播放动画

回到Idle状态下:

击杀以后掉落金币:

生成的数量也没问题:

动画也没有问题,增加的数量也没问题: 

还记得我前面讲过的逻辑处理的AddGeo的三种状态吗,当玩家在持续捡钱的时候,状态不会发生变化,直到玩家在一段时间没捡钱后,就切换状态到状态2:

这里为什么是517是因为我按了一下作弊键给自己加了500块钱,实际上应该加的17根本没有多和少,因此也算顺利展示了金钱系统Geo大多数的逻辑处理,那为什么是大多数呢?因为花费吉欧的逻辑处理我还没展示给你们看,这个要等多后续的内容才能见分晓了。 

 

相关文章:

[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十一集:制作游戏的金钱系统吉欧Geo和初步制作HUD Canvas的额外内容

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作游戏的金钱系统吉欧Geo以及HUD Camera 1.制作金钱系统吉欧2.制作吉欧的脚本Geo Counter逻辑处理3.制作HUD Canvas的吉欧的UI4.在敌人的HealthManager.c…...

底层逻辑之:极大似然方法(Maximum Likelihood Estimation, MLE)

简介&#xff1a; 极大似然方法&#xff08;Maximum Likelihood Estimation, MLE&#xff09;是一种用于估计统计模型参数的方法。其核心思想是基于观测数据来寻找最可能产生这些数据的模型参数。 早在1821年&#xff0c;德国数学家高斯&#xff08;C. F. Gauss&#xff09;就…...

笔记:Centos Nginx Jdk Mysql OpenOffce KkFile Minio安装部署

远程工具 ToDesk Nginx 解压 tar zxvf nginx-1.20.2.tar.gz进入Nginx 文件夹 cd nginx-1.20.2报错解决 ./configure: error: C compiler cc is not found yum -y install gcc gcc-c autoconf automake make./configure: error: the HTTP rewrite module requires the PC…...

【MARL】深入理解多智能体近端策略优化(MAPPO)算法与调参

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…...

深入探索Go语言中的sync.Mutex与sync.RWMutex:原理、应用与实践

深入探索Go语言中的sync.Mutex与sync.RWMutex:原理、应用与实践 在并发编程的世界里,Go语言以其独特的并发模型和简洁的语法赢得了广泛的关注。在Go语言的并发控制工具箱中,sync.Mutex和sync.RWMutex是两个至关重要的工具,它们帮助开发者保护共享资源,避免竞态条件,确保…...

15.postgresql--jsonb 数组进行打平,过滤

用jsonb_array_elements函数先展开数组&#xff0c;再用jsonb_each函数遍历元素中的键值对 例如&#xff1a; SELECT * FROM data_table, LATERAL jsonb_array_elements(json_column) WITH ORDINALITY as elem(element, idx) JOIN LATERAL jsonb_each(elem.element) as kv(ke…...

linux下i2c开发与框架源码分析

目录 1 概述 2 I2c子系统框架 3 I2C的使用流程 3.1 在驱动里使用 3.2 在应用层使用 3.3 I2ctool的使用 4 为硬件i2c注册一个适配器 5 i2c子系统源码流程分析 5.1 i2c device与driver绑定过程 5.1.1 Driver的注册与处理 5.1.2 Client device的生成 5.2 I2c的发送与接…...

[ruby on rails] 安装docker

1. docker安装 ubuntu14.04后自带docker安装包&#xff0c;可以直接安装docker.io sudo apt-get updatesudo apt-get install -y docker.io # 安装后启动sudo service docker start最新版本docker 安装docker-ce # 官方源 curl -fsSL https://download.docker.com/linux/ubun…...

I2C学习

详情学习 12. I2C通讯 — [野火]Linux基础与应用开发实战指南——基于LubanCat-RK系列板卡 文档 (embedfire.com) 问题 i2c总线中scl和sda两条线的作用以及区别 在 I2C 总线&#xff08;Inter-Integrated Circuit&#xff09;中&#xff0c;SCL 和 SDA 是两条核心信号线&am…...

VUE:基于MVVN的前端js框架

文章目录 vue框架v-show vue框架 注意是 先写函数名&#xff0c;再写function。 handle:function (){}下面是错误的 function:handle(){}3 v-show 本质上等于号后面还是判断条件&#xff0c;所以不能写赋值语句&#xff0c;下面是正确的 下面是错误的 v-show " ge…...

06、Spring AOP

在我们接下来聊Spring AOP之前我们先了解一下设计模式中的代理模式。 一、代理模式 代理模式是23种设计模式中的一种,它属于结构型设计模式。 对于代理模式的理解: 程序中对象A与对象B无法直接交互,如:有人要找某个公司的老总得先打前台登记传达程序中某个功能需要在原基…...

c语言学习26字符串的应用

字符串在stm32串口中的应用 串口控制流水灯 pc通过串口发送字符串命令控制流水灯 open 流水灯打开 close 流水灯关闭 speed 1~9速度控制 if(strcmp((char *)usart1_rec_buff,"open")0) { led_flag 1; } else if(strcmp((char *)usart1_rec_buff,"close&qu…...

法语旅游常用口语-柯桥学外语到蓝天广场泓畅学校

以下是一些实用的法语旅游常用口语&#xff0c;帮助你在法国旅行时能够进行基本的交流&#xff1a; 问候与道别 Bonjour: 用于日常问候。Au revoir: 用于告别。 请求帮助 S’il vous plat: 用于请求帮助&#xff0c;例如在需要寻找某个地点或服务时。 询问信息 Excusez-moi: 用…...

Kafka 生产者优化与数据处理经验

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…...

MySQL 主从复制之多线程复制

一、MySQL 多线程复制的背景 MySQL 的主从复制延迟一直是受开发者最为关注的问题之一&#xff0c;MySQL 从 5.6 版本开始追加了并行复制功能&#xff0c;目的就是为了改善复制延迟问题&#xff0c;并行复制称为enhanced multi-threaded slave&#xff08;简称MTS&#xff09;。…...

Linux2.6内核进程调度队列

文章目录 前言运行队列 runqueue优先级活动队列过期队列活跃队列 VS 过期队列active指针和expired指针O(1)调度算法 前言 在前面学习并认识了进程之后&#xff0c;我们会发出一个疑问&#xff1a;Linux内核是如何调度进程的呢&#xff1f; 接下来我们就以Linux2.6内核为例深入探…...

Infineon(英飞凌) TLE985xQX 芯片电机工作电流、电压AD采样

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 单片机芯片合集 文章目录 其他系列文章导航 文章目录 前言 一、选取合适的端口 1.通过 OP1、OP2 电流采集运放输入端口进行H桥驱动的电流采集。 2.通过 O_D_VBAT_AD_EN、I_A_VBAT_A…...

Sparrow系列拓展篇:对信号量应用问题的深入讨论

前言 笔者之前已经介绍过了Sparrow信号量的源码&#xff0c;但是对于信号量的使用&#xff0c;并没有讲得非常详细&#xff0c;仅仅讲了同步与互斥的概念。 本章让笔者介绍如何使用Sparrow的信号量&#xff0c;深入探讨一下信号量在同步、计数与互斥中的应用。 使用信号量解…...

图文详解Docker下配置、测试Redis

文章目录 前言实测环境&#xff1a;实验思路&#xff1a; 正文1.准备工作2. 配置、运行 Redis 容器3. 配置测试 总结 前言 配置、测试redis数据库服务器&#xff0c;首先确保正确安装docker&#xff0c;并且已启动运行&#xff0c;具体安装docker方法见笔者前面的博文《OpenEu…...

Python编程艺术:优雅与实用的完美平衡(推导式)

在Python这门优雅的编程语言中&#xff0c;处处体现着"简洁即是美"的设计哲学。今天我们深入探讨Python中那些让代码更优雅、更高效的编程技巧&#xff0c;这些技巧不仅能提升代码的可读性&#xff0c;还能让编程过程充满乐趣。 列表推导式的魔力 Python的列表推导…...

Spring Boot框架Starter组件整理

在Spring Boot框架中&#xff0c;starter是一种预定义的依赖集合&#xff0c;旨在简化Maven或Gradle等构建工具中的依赖管理。每个starter都包含了实现特定功能所需的库和组件&#xff0c;以及相应的配置文件。开发者只需在项目中引入相应的starter依赖&#xff0c;即可快速搭建…...

C/C++基础知识复习(27)

1) 移动语义和拷贝语义的区别 拷贝语义和移动语义是C中对象所有权管理的两种机制&#xff0c;主要在对象初始化、赋值或传参时体现。 拷贝语义 (Copy Semantics) 行为&#xff1a;通过深拷贝或浅拷贝&#xff0c;创建一个新对象&#xff0c;并将原对象的值或资源复制到新对象…...

IEC61850实现方案和测试-2

IEC61850实现方案和测试-1作为介绍实现方案和测试的第二篇文章&#xff0c;后续会继续更新&#xff0c;欢迎关注。 第一篇是&#xff1a;IEC61850实现方案和测试-1-CSDN博客 UCA详细测试用例下载&#xff1a; 链接: https://pan.baidu.com/s/1TTMlYRfzKITgrkWwwtcrDg 提取码:…...

flume-将日志采集到hdfs

看到hdfs大家应该做什么&#xff1f; 是的你应该去把集群打开&#xff0c; cd /export/servers/hadoop/sbin 启动集群 ./start-all.sh 在虚拟机hadoop02和hadoop03上的conf目录下配置相同的日志采集方案&#xff0c;‘ cd /export/servers/flume/conf 切换完成之后&#…...

一文学习开源框架LeakCanary

LeakCanary 简介 LeakCanary 是一个由 Square 开发的开源工具&#xff0c;主要用于检测和诊断 Android 应用中的内存泄漏问题。它通过自动化的方式帮助开发者捕捉和分析可能导致内存泄漏的对象&#xff0c;简化了内存问题的排查过程。 LeakCanary 的功能 自动检测内存泄漏&a…...

jetson orin系列开发版安装cuda的gpu版本的opencv

opencv安装包下载地址&#xff1a; https://github.com/opencv/opencv/扩展库下载地址&#xff1a; https://github.com/opencv/opencv_contrib1. 删除jetpack包中的opencv版本 原先的opencv库安装在目录/usr/lib/aarch64-linux-gnu/下&#xff08;一般其他的第三方库也都安…...

数据结构-8.Java. 七大排序算法(中篇)

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 中篇主要实现后三种排序算法: 冒泡排序,快速排序,下一篇讲 归并排序. 文章专栏: Java-数据结构 若有问题 评论区见 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作…...

数据结构C语言描述4(图文结合)--栈的实现,中序转后序表达式的实现

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…...

python基本数据类型 -- 元组tuple

在 Python 中&#xff0c;元组&#xff08;Tuple&#xff09;是一种轻量级的、不可变的数据结构。与列表类似&#xff0c;元组用于存储有序的数据集合&#xff0c;但它一旦创建&#xff0c;其内容就无法更改。这种特性让元组在某些场景下更加安全和高效。本文将从定义、操作、应…...

tcpdump交叉编译

TCPDUMP在Libpcap上开发。 首先需要编译libcap。 网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#xff0c; 你尽可以试试&#xff0c;从里面找到下载地址都要费半天时间。 \color{red}网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#…...