Unity学习笔记(六)使用状态机重构角色移动、跳跃、冲刺

devtools/2025/1/11 11:27:04/

前言

本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记

整体状态框架(简化)

在这里插入图片描述

  • Player 是操作对象的类: 继承了 MonoBehaviour 用于定义游戏对象的行为,每个挂载在 Unity 游戏对象上的脚本都需要继承自 MonoBehaviour,才能利用 Unity 的生命周期事件和功能。
  • PlayerState 是定义状态接口,这里定义了状态类的 Enter(进入),Update(更新),Exit(退出)
  • PlayerStateMachine 是定义上下文类,它持有当前状态的引用,并合适的时机调用状态的行为ChangeState
  • 具体的状态
    • PlayerMoveState(移动状态)
    • PlayerJumpState(跳跃状态)
    • PlayerIdleState(站立状态)

PlayerState

玩家状态的基类,包含状态的基本操作构造函数和三个基础抽象函数进入状态、更新状态、退出状态。

public class PlayerState
{protected Player3 player;protected PlayerStateMachine stateMachine;protected Rigidbody2D rb;protected float xInput;protected float yInput;public string animBoolName;// 记录状态的开始时间,方便做一些状态的转化protected float stateTimer;protected bool triggerCalled;public PlayerState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName){this.player = _player;this.stateMachine = _stateMachine;this.animBoolName = _animBoolName;}public virtual void Enter(){player.anim.SetBool(animBoolName, true);rb = player.rb;triggerCalled = false;}public virtual void Exit() {player.anim.SetBool(animBoolName, false);}public virtual void Update() {stateTimer -= Time.deltaTime;xInput = Input.GetAxisRaw("Horizontal");yInput = Input.GetAxisRaw("Vertical");player.anim.SetFloat("yVelocity", rb.velocity.y);}public virtual void AnimatorFinishTrigger(){triggerCalled = true;}
}

PlayerStateMachine

玩家状态的转换类,改变状态步骤

  • 退出当前状态
  • 初始化新状态
  • 进入新的状态
public class PlayerStateMachine
{public PlayerState currentState { get; private set;}public void Initialize(PlayerState _state){currentState = _state;currentState.Enter();}public void ChangeState(PlayerState _nextState){currentState.Exit();currentState = _nextState;currentState.Enter();}
}

状态类

有两个比较特殊的状态

  • PlayerAirState,为了设置玩家在空中时的动作
  • PlayerGroundedState,这个状态是为了抽象出玩家站立,跳跃,移动的通用代码。这些状态都要求玩家必须在地面上才能转换。

PlayerAirState(玩家在空中状态)

public class PlayerAirState : PlayerState
{public PlayerAirState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();// rb.velocity.y or x 静止时 都为 0,所以不需要关注当前位置,只要静止为0if (player.IsGroundDetected()){stateMachine.ChangeState(player.idleState);}// 跳起来的移动速度会慢一点if (xInput != 0){player.SetVelocity(player.moveSpeed * .8f * xInput, rb.velocity.y);}}
}

PlayerGroundedState(玩家在地面状态)

public class PlayerGroundedState : PlayerState
{public PlayerGroundedState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (Input.GetKeyDown(KeyCode.Mouse0)){stateMachine.ChangeState(player.moveState);}if (!player.IsGroundDetected()){stateMachine.ChangeState(player.airState);}if (Input.GetKeyDown(KeyCode.Space) && player.IsGroundDetected()){stateMachine.ChangeState(player.jumpState);}}}

PlayerDashState(冲刺状态)

public class PlayerDashState : PlayerState
{public PlayerDashState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();stateTimer = player.dashDuration;}public override void Exit(){base.Exit();// 冲刺结束后x轴不动,在空中就不会一直移动player.SetVelocity(0, rb.velocity.y);}public override void Update(){base.Update();player.SetVelocity(player.dashSpeed * player.dashDir, 0);if (stateTimer < 0){stateMachine.ChangeState(player.idleState);}}
}

PlayerIdleState(站立状态)

public class PlayerIdleState : PlayerGroundedState
{public PlayerIdleState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();// 将坐标设置为 0,0player.SetZeroVelocity();}public override void Exit(){base.Exit();}public override void Update(){base.Update();if(xInput != 0){stateMachine.ChangeState(player.moveState);}}
}

PlayerJumpState(跳跃状态)

public class PlayerJumpState : PlayerState
{public PlayerJumpState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();rb.velocity = new Vector2(rb.velocity.x, player.jumpForce);}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (rb.velocity.y < 0){stateMachine.ChangeState(player.airState);}}
}

PlayerMoveState(移动状态)

public class PlayerMoveState : PlayerGroundedState
{public PlayerMoveState(Player3 _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();player.SetVelocity(xInput * player.moveSpeed, rb.velocity.y);if (xInput == 0){stateMachine.ChangeState(player.idleState);}}
}

Player

玩家类,继承自MonoBehaviour,状态机和各类状态等的定义都在这里进行初始化赋值。

我们需要创建一些关键函数:Awark(),Start(),Update()

该方法初始化过程:

暂时无法在飞书文档外展示此内容

下面的类比较复杂,我设置一个简化版和详细版,了解大致流程简化版即可

简化版

public class Player3 : MonoBehaviour
{public Animator anim { get; private set; }public Rigidbody2D rb { get; private set; }#region Statespublic PlayerStateMachine stateMachine { get; private set; }public PlayerIdleState idleState { get; private set; }public PlayerMoveState moveState { get; private set; }public PlayerJumpState jumpState { get; private set; }public PlayerDashState dashState { get; private set; }public PlayerAirState airState { get; private set; }#endregionprivate void Awake(){stateMachine = new PlayerStateMachine();idleState = new PlayerIdleState(this, stateMachine, "Idle");moveState = new PlayerMoveState(this, stateMachine, "Move");dashState = new PlayerDashState(this, stateMachine, "Dash");jumpState = new PlayerJumpState(this, stateMachine, "Jump");airState = new PlayerAirState(this, stateMachine, "Jump");}private void Start(){rb = GetComponent<Rigidbody2D>();anim = GetComponentInChildren<Animator>();stateMachine.Initialize(idleState);}private void Update(){stateMachine.currentState.Update();CheckForDashInput();}
}

详细版

public class Player3 : MonoBehaviour
{public Animator anim { get; private set; }public Rigidbody2D rb { get; private set; }protected int facingDir = 1;protected bool facingRight = true;[Header("Move info")]public float moveSpeed = 12f;public float jumpForce;[Header("Dash info")][SerializeField] private float dashCooldown;private float dashUsageTimer;public float dashSpeed = 5f;public float dashDuration = 5f;public float dashDir { get; private set; }[Header("Collision Info")][SerializeField] protected Transform groundCheck;[SerializeField] protected float groundCheckDistance;[SerializeField] protected LayerMask whatIsGround;#region Statespublic PlayerStateMachine stateMachine { get; private set; }public PlayerIdleState idleState { get; private set; }public PlayerMoveState moveState { get; private set; }public PlayerJumpState jumpState { get; private set; }public PlayerDashState dashState { get; private set; }public PlayerAirState airState { get; private set; }#endregionprivate void Awake(){stateMachine = new PlayerStateMachine();idleState = new PlayerIdleState(this, stateMachine, "Idle");moveState = new PlayerMoveState(this, stateMachine, "Move");dashState = new PlayerDashState(this, stateMachine, "Dash");jumpState = new PlayerJumpState(this, stateMachine, "Jump");airState = new PlayerAirState(this, stateMachine, "Jump");}private void Start(){rb = GetComponent<Rigidbody2D>();anim = GetComponentInChildren<Animator>();stateMachine.Initialize(idleState);}private void Update(){stateMachine.currentState.Update();CheckForDashInput();}// => 可以理解为 简化返回表达式的符号,主要用于单行方法、属性或表达式的定义。public virtual bool IsGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);private void CheckForDashInput(){dashUsageTimer -= Time.deltaTime;if (Input.GetKeyDown(KeyCode.LeftShift) && dashUsageTimer < 0){dashUsageTimer = dashCooldown;dashDir = Input.GetAxisRaw("Horizontal");if (dashDir == 0)dashDir = facingDir;stateMachine.ChangeState(dashState);}}public void SetZeroVelocity(){rb.velocity = new Vector2(0, 0);}public void SetVelocity(float _xVelocity, float _yVelocity){rb.velocity = new Vector2(_xVelocity, _yVelocity);FlipController(_xVelocity);}public virtual void FlipController(float _x){if (_x > 0 && !facingRight){Flip();}else if (_x < 0 && facingRight){Flip();}}protected virtual void Flip(){facingDir = facingDir * -1;facingRight = !facingRight;transform.Rotate(0, 180, 0);}
}

小结

使用状态机进行重构我们可以看到,之后如果想新增或者修改状态,只需要去对应状态类中修改即可,不需要在很多地方维护对应代码,对于代码整体也更加清晰。

整体玩家状态机代码重构就是上面,接下来重构玩家动画部分

动画部分重构

重构前,我们是通过维护玩家当前的状态是否来判断是否进入和退出。使用状态机后,我们应该通过状态代表的参数来维护状态机。
在这里插入图片描述

重构后,我们可以看到状态机的方式我们不需要通过playerIdle来转换,每次状态机执行完都会进入 Exit 结点(改结点详情看拓展) ,这样我们不用维护状态之间是否有依赖,更方便后续的拓展

在这里插入图片描述

重构后效果

请添加图片描述

拓展

Unity 如何调用 Awake()

Awake() 方法在 Unity 生命周期中的角色
Awake() 是 Unity 中 MonoBehaviour 类的生命周期方法之一,它的主要功能是在对象被创建时初始化脚本和对象状态。它是 Unity 生命周期中非常重要的一个环节。


Awake() 的调用时机

  1. 在场景加载时
    当一个场景加载完成,所有启用的 GameObject 的组件(脚本)会在它们的 Awake() 方法中执行初始化。

  2. 在 GameObject 动态实例化时
    如果一个 GameObject 在运行时被动态创建(如通过 Instantiate() 方法),其附加的脚本也会在实例化时调用 Awake()

  3. 调用顺序

    • Awake() 的调用顺序不受脚本执行顺序的影响。Unity 会按照 GameObject 被加载的顺序来依次调用这些对象的 Awake() 方法。
    • 重要:如果有依赖其他对象的初始化,可以将逻辑放在 Start() 中,因为 Start() 会在所有 Awake() 调用完成之后执行。

Unity 生命周期的完整流程
以下是 Unity 中 MonoBehaviour 的常见生命周期方法及其顺序:

  1. 脚本的加载和初始化阶段

    • Awake()
      • 在所有脚本的生命周期中最先调用。
      • 用于初始化脚本的内部状态,以及为后续使用的变量赋初始值。
      • Awake() 被调用时,其他组件或 GameObject 可能尚未初始化完成,因此不适合依赖其他对象。
  2. 脚本的启用阶段

    • OnEnable()
      • 在对象被启用时调用。
      • 如果需要在对象启用时执行额外操作,可以在这里添加逻辑。
  3. 场景运行时初始化阶段

    • Start()
      • 在所有对象的 Awake() 方法执行完成后调用。
      • Start() 是初始化逻辑的推荐位置,特别是在需要依赖其他对象的情况下。
  4. 运行时更新阶段

    • Update():每帧调用一次,用于更新逻辑。
    • FixedUpdate():每固定时间间隔调用一次,用于物理计算。
    • LateUpdate():在每帧的所有 Update() 执行完成后调用,用于执行后续逻辑(例如摄像机跟随)。
  5. 销毁阶段

    • OnDisable()
    • OnDestroy()

Awake() 的作用和特点

1. 作用
  • 初始化脚本实例
    用于初始化脚本中的变量和状态,例如分配引用、加载资源、设置默认值等。

  • 加载必要的资源
    比如加载外部的材质、音频或配置文件。

  • 设置依赖项
    如果某些对象或组件需要在脚本激活时使用,可以在 Awake() 中获取或初始化它们。

  1. Start() 的区别
  • Awake()Start() 更早调用
  • Awake() 用于确保脚本自身的初始化,而 Start() 适合处理与其他对象或组件的交互
特性Awake()Start()
调用时机对象加载时立即调用所有对象的 Awake() 执行后
依赖其他对象状态不建议依赖其他对象可安全地依赖其他对象
手动调用不推荐(Unity 会自动调用)可以在特定情况下手动调用

示例:Awake() 与生命周期的关系

using UnityEngine;public class Example : MonoBehaviour
{void Awake(){Debug.Log("Awake: 初始化脚本变量和资源");}void OnEnable(){Debug.Log("OnEnable: 脚本或对象被激活");}void Start(){Debug.Log("Start: 在对象所有的初始化完成后调用");}void Update(){Debug.Log("Update: 每帧调用");}void OnDisable(){Debug.Log("OnDisable: 脚本或对象被禁用");}void OnDestroy(){Debug.Log("OnDestroy: 对象被销毁");}
}

在场景运行时,执行顺序为:

  1. Awake():初始化。
  2. OnEnable():对象启用时的逻辑。
  3. Start():所有对象的 Awake() 调用完成后。
  4. Update():每帧更新。
  5. OnDisable()OnDestroy():对象被禁用或销毁时。

总结

  • Awake() 的核心作用是初始化脚本变量和状态,在 Unity 生命周期中最早被调用。
  • 适合场景:用来初始化脚本或 GameObject 的自身逻辑,而不依赖其他对象。
  • 与其他方法的关系
    • Awake() 是生命周期的起点。
    • 如果依赖其他对象的初始化,建议将逻辑放到 Start()

希望这些内容能帮助你理解 Unity 的生命周期和 Awake() 方法的作用!如果你有更多问题,随时提问!

Unity 调用 Awake 的简单原理

Unity 调用 Awake() 的原理其实可以简单地理解为以下几个步骤。虽然 Unity 的底层实现细节是封闭的,但我们可以根据其生命周期行为和一些公开信息总结出其大致逻辑。


1. Unity 生命周期的核心
Unity 的生命周期方法(如 Awake()Start())是由 Unity 引擎在运行时按照特定顺序自动调用的。这些方法不需要开发者手动注册或显式调用。以下是基本的执行流程:

  1. 场景加载

    • Unity 会加载场景中的所有 GameObject。
    • 如果某个 GameObject 上挂载了继承自 MonoBehaviour 的脚本,它会参与生命周期流程。
  2. 脚本扫描和方法检测

    • Unity 会通过反射机制检测脚本中是否实现了特定的生命周期方法(如 Awake())。
    • 如果检测到某个生命周期方法,则将其注册到 Unity 的内部执行流程中。
  3. 方法调用

    • 在特定的生命周期阶段(例如场景加载后),Unity 引擎会按顺序调用注册的生命周期方法。

2. 调用 Awake() 的简化原理
以下是 Unity 如何调用 Awake() 的基本逻辑:

步骤 1:场景加载

  • 当场景加载时,Unity 会逐一加载场景中的所有 GameObject 和它们的组件。

步骤 2:反射检测

  • Unity 扫描每个继承自 MonoBehaviour 的脚本,检查是否定义了 Awake() 方法。
    • Unity 使用 反射 来动态检测方法是否存在。
    • 如果 Awake() 存在,Unity 会将该方法存储为一个待调用的任务。

步骤 3:初始化并调用 Awake()

  • 在所有 GameObject 被加载到内存后,Unity 会依次调用这些 GameObject 脚本的 Awake() 方法。
  • 调用顺序:
    • Awake() 是生命周期的第一个阶段。
    • 只要脚本和对象处于启用状态,Awake() 会在对象加载时立即被调用。

代码实现的简化逻辑
以下是 Unity 调用 Awake() 的简单逻辑,用伪代码表示:

// Unity 内部的场景加载流程
void LoadScene(Scene scene)
{// 1. 加载场景中的所有 GameObjectforeach (GameObject go in scene.gameObjects){// 2. 获取 GameObject 上的所有 MonoBehaviour 脚本foreach (MonoBehaviour script in go.GetComponents<MonoBehaviour>()){// 3. 检查是否实现了 Awake() 方法if (script.HasMethod("Awake")){// 4. 调用 Awake()script.Awake();}}}
}

3. Awake() 的调用特点

  • 只调用一次

    • Awake() 只会在脚本实例初始化时调用一次,无论该脚本的 GameObject 是否被启用或禁用。
  • 不依赖脚本执行顺序

    • Awake() 的调用顺序是按照 GameObject 加载的顺序。
    • 脚本执行顺序设置对 Awake() 无影响。
  • Start() 之前调用

    • Awake() 是生命周期的第一步,Start() 在所有 Awake() 调用完成后才会被执行。
  • 适合自我初始化

    • Awake() 的设计初衷是用于初始化自身的变量和状态,而不适合依赖其他 GameObject,因为此时其他对象可能尚未完成初始化。

4. 示例代码
以下代码演示了 Unity 如何在场景加载时调用 Awake()Start()

using UnityEngine;public class Example : MonoBehaviour
{void Awake(){Debug.Log("Awake called: 初始化变量");}void Start(){Debug.Log("Start called: 依赖其他对象初始化");}
}

假设场景中有两个 GameObject 分别挂载了该脚本,运行时输出可能是:

Awake called: 初始化变量 (GameObject 1)
Awake called: 初始化变量 (GameObject 2)
Start called: 依赖其他对象初始化 (GameObject 1)
Start called: 依赖其他对象初始化 (GameObject 2)

5. 为什么使用 Awake()
Awake() 的核心作用是初始化自身的状态。例如:

  1. 分配变量或资源。
  2. 加载外部数据。
  3. 设置默认值。

6. 与其他生命周期方法的关系

方法名调用时机适合的操作
Awake()GameObject 加载到场景时初始化自身变量,不依赖其他对象
OnEnable()GameObject 或脚本启用时运行需要在启用时触发的逻辑
Start()所有 Awake() 调用完成后,场景运行时初始化需要依赖其他对象的逻辑
Update()每帧调用持续更新逻辑,例如动画、输入检测

总结
Unity 调用 Awake() 的原理可以概括如下:

  1. 加载场景
    • Unity 会加载所有 GameObject 和其组件。
  2. 检测方法
    • Unity 使用反射检测脚本是否实现了 Awake() 方法。
  3. 方法调用
    • Unity 自动调用实现了 Awake() 的脚本,不需要开发者手动调用。

通过这个流程,Unity 实现了生命周期的动态管理,使得开发者只需专注于脚本逻辑的实现,而不用关心具体的调用机制。

Unity状态机的Exit结点

在Unity的Animator状态机(Animator State Machine)中,Exit结点用于表示从当前状态机退出到其父状态机的状态。以下是关于Exit结点的详细说明:


  1. Exit结点是什么?
    Exit结点是Unity Animator中的一个特殊的状态机结点,它表示一个状态机结束的出口点。通常用于嵌套的子状态机(Sub-State Machine)中,告诉父状态机当前子状态机的行为已经完成,可以切换到父状态机中的其他状态。

  1. 使用场景
  • 嵌套状态机(Sub-State Machines)
    当你将一个复杂的动画逻辑封装到一个子状态机中时,Exit结点表示该子状态机完成其逻辑后应该退出,回到父状态机进行下一步。
  • 动画流程控制
    如果子状态机处理完某些特定动画(如攻击动作、过渡动画等),可以通过Exit结点返回父状态机,从而进行主流程的继续。

  1. 如何设置Exit结点
    在Unity中,以下是设置Exit结点的步骤:
  2. 创建子状态机
    在Animator中,右键选择Create Sub-State Machine,创建一个嵌套的子状态机。
  3. 添加状态和过渡
    在子状态机中添加具体的动画状态(如攻击、跳跃等)。
  4. 使用Exit结点
    • 在子状态机中,右键选择Make Transition,并将过渡指向Exit结点。
    • Exit结点是子状态机的默认出口,不需要手动创建。
  5. 在父状态机中配置逻辑
    在父状态机中,可以设置子状态机到其他状态(或反过来)的过渡逻辑。

4. Exit的行为

  • 当动画流转到Exit结点时,子状态机会退出,控制权回到父状态机。
  • 可以通过Animator Controller中的条件(如布尔值、触发器等)控制子状态机何时退出。
  • 在父状态机中,子状态机到Exit的过渡会被认为完成,可以接着切换到其他状态。

  1. 注意事项
  • 不能直接控制Exit结点
    Exit是一个逻辑性的特殊结点,它不能像普通状态一样附加动画或行为。
  • 父状态机的后续逻辑
    确保在父状态机中正确设置过渡条件,否则子状态机退出后可能进入意料之外的状态。

  1. 示例场景
    假设有一个游戏角色的动画逻辑:
  • 父状态机
    包括“待机”、“跑步”、“攻击子状态机”。
  • 子状态机(攻击子状态机):
    包括“攻击准备”、“攻击动作”、“攻击结束”。

当“攻击动作”完成后,子状态机会通过Exit结点返回父状态机,角色的动画状态可能回到“待机”或其他状态。


http://www.ppmy.cn/devtools/149600.html

相关文章

NO.3 《机器学习期末复习篇》以题(问答题)促习(人学习),满满干huo,大胆学大胆补!

目录 &#x1f50d; 1. 对于非齐次线性模型 &#xff0c;试将其表示为齐次线性模型形式。 ​编辑 &#x1f50d; 2. 某汽车公司一年内各月份的广告投入与月销量数据如表3-28所示&#xff0c;试根据表中数据构造线性回归模型&#xff0c;并使用该模型预测月广告投入为20万元时…

初学stm32 --- ADC单通道采集

目录 ADC寄存器介绍&#xff08;F1&#xff09; ADC控制寄存器 1(ADC_CR1) ADC控制寄存器 2(ADC_CR2) ADC采样时间寄存器1(ADC_SMPR1) ADC采样时间寄存器2(ADC_SMPR2) ADC规则序列寄存器 1(ADC_SQR1) ADC规则序列寄存器 2(ADC_SQR2) ADC规则序列寄存器 3(ADC_SQR3) AD…

iPad编程新体验:如何用IDE Code App实现远程在线开发告别电脑束缚

文章目录 前言1. 在iPad下载Code APP2.安装cpolar内网穿透2.1 cpolar 安装2.2 创建TCP隧道 3. iPad远程vscode4. 配置固定TCP端口地址4.1 保留固定TCP地址4.2 配置固定的TCP端口地址4.3 使用固定TCP地址远程vscode 前言 对于开发者来说&#xff0c;iPad 的强大性能让其在越来越…

Element UI与Element Plus:深度剖析

文章目录 前言一、概述二、技术特性三、设计理念四、使用体验五、迁移指南结语 前言 随着前端开发技术的快速发展&#xff0c;Vue.js 生态系统中的组件库也在不断进化。Element UI 和 Element Plus 是两个深受开发者喜爱的 Vue 组件库&#xff0c;它们分别构建于 Vue 2.x 和 V…

13_Redis Stream消息队列

1.Stream消息队列介绍 1.1 基本概念 Redis的Pub/Sub发布订阅模式虽然能够实现消息队列的功能,但存在一个显著局限性:它不支持消息持久化。因此,在网络连接中断或Redis服务发生故障时,消息会丢失。简而言之,Redis Pub/Sub能够有效地传递实时消息,但却无法保存历史消息记…

Clickhouse基础(一)

操作命令&#xff1a; sudo clickhouse start sudo clickhouse restart sudo clickhouse status进入clickhouse clickhouse-client -mCREATE TABLE db_13.t_assist (modelId UInt64,taskId UInt64,testNo String,tdId UInt64,eventDay String,eventDaytime UInt64,eventBatch …

Linux 高级路由 —— 筑梦之路

Linux 高级路由详解 本文将基于您提供的 Linux 高级路由极简教程 文章&#xff0c;深入探讨 Linux 高级路由的概念、配置方法以及应用场景。 一、什么是 Linux 高级路由&#xff1f; Linux 高级路由是指利用 Linux 内核提供的强大网络功能&#xff0c;实现超越传统路由表和默…

MYSQL重置密码

目录 1. 停止 MySQL 服务 2. 以跳过权限检查的方式启动 MySQL 3. 连接到 MySQL 4. 更新 root 用户密码 5. 重启 MySQL 服务 6. 使用新密码登录 注意事项 一些错误情况及解决方法 1.运行 net stop mysql 时提示“服务名无效” 2. “服务”管理器中无MYSQL 3.datadir 数…