Unity学习笔记(六)使用状态机重构角色移动、跳跃、冲刺
前言
本文为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()
的调用时机
-
在场景加载时:
当一个场景加载完成,所有启用的 GameObject 的组件(脚本)会在它们的Awake()
方法中执行初始化。 -
在 GameObject 动态实例化时:
如果一个 GameObject 在运行时被动态创建(如通过Instantiate()
方法),其附加的脚本也会在实例化时调用Awake()
。 -
调用顺序:
Awake()
的调用顺序不受脚本执行顺序的影响。Unity 会按照 GameObject 被加载的顺序来依次调用这些对象的Awake()
方法。- 重要:如果有依赖其他对象的初始化,可以将逻辑放在
Start()
中,因为Start()
会在所有Awake()
调用完成之后执行。
Unity 生命周期的完整流程
以下是 Unity 中 MonoBehaviour 的常见生命周期方法及其顺序:
-
脚本的加载和初始化阶段:
Awake()
- 在所有脚本的生命周期中最先调用。
- 用于初始化脚本的内部状态,以及为后续使用的变量赋初始值。
- 在
Awake()
被调用时,其他组件或 GameObject 可能尚未初始化完成,因此不适合依赖其他对象。
-
脚本的启用阶段:
OnEnable()
- 在对象被启用时调用。
- 如果需要在对象启用时执行额外操作,可以在这里添加逻辑。
-
场景运行时初始化阶段:
Start()
- 在所有对象的
Awake()
方法执行完成后调用。 Start()
是初始化逻辑的推荐位置,特别是在需要依赖其他对象的情况下。
- 在所有对象的
-
运行时更新阶段:
Update()
:每帧调用一次,用于更新逻辑。FixedUpdate()
:每固定时间间隔调用一次,用于物理计算。LateUpdate()
:在每帧的所有Update()
执行完成后调用,用于执行后续逻辑(例如摄像机跟随)。
-
销毁阶段:
OnDisable()
OnDestroy()
Awake()
的作用和特点
1. 作用
-
初始化脚本实例:
用于初始化脚本中的变量和状态,例如分配引用、加载资源、设置默认值等。 -
加载必要的资源:
比如加载外部的材质、音频或配置文件。 -
设置依赖项:
如果某些对象或组件需要在脚本激活时使用,可以在Awake()
中获取或初始化它们。
- 与
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: 对象被销毁");}
}
在场景运行时,执行顺序为:
Awake()
:初始化。OnEnable()
:对象启用时的逻辑。Start()
:所有对象的Awake()
调用完成后。Update()
:每帧更新。OnDisable()
和OnDestroy()
:对象被禁用或销毁时。
总结
Awake()
的核心作用是初始化脚本变量和状态,在 Unity 生命周期中最早被调用。- 适合场景:用来初始化脚本或 GameObject 的自身逻辑,而不依赖其他对象。
- 与其他方法的关系:
Awake()
是生命周期的起点。- 如果依赖其他对象的初始化,建议将逻辑放到
Start()
。
希望这些内容能帮助你理解 Unity 的生命周期和 Awake()
方法的作用!如果你有更多问题,随时提问!
Unity 调用 Awake 的简单原理
Unity 调用 Awake()
的原理其实可以简单地理解为以下几个步骤。虽然 Unity 的底层实现细节是封闭的,但我们可以根据其生命周期行为和一些公开信息总结出其大致逻辑。
1. Unity 生命周期的核心
Unity 的生命周期方法(如 Awake()
、Start()
)是由 Unity 引擎在运行时按照特定顺序自动调用的。这些方法不需要开发者手动注册或显式调用。以下是基本的执行流程:
-
场景加载:
- Unity 会加载场景中的所有 GameObject。
- 如果某个 GameObject 上挂载了继承自
MonoBehaviour
的脚本,它会参与生命周期流程。
-
脚本扫描和方法检测:
- Unity 会通过反射机制检测脚本中是否实现了特定的生命周期方法(如
Awake()
)。 - 如果检测到某个生命周期方法,则将其注册到 Unity 的内部执行流程中。
- Unity 会通过反射机制检测脚本中是否实现了特定的生命周期方法(如
-
方法调用:
- 在特定的生命周期阶段(例如场景加载后),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()
的核心作用是初始化自身的状态。例如:
- 分配变量或资源。
- 加载外部数据。
- 设置默认值。
6. 与其他生命周期方法的关系
方法名 | 调用时机 | 适合的操作 |
---|---|---|
Awake() | GameObject 加载到场景时 | 初始化自身变量,不依赖其他对象 |
OnEnable() | GameObject 或脚本启用时 | 运行需要在启用时触发的逻辑 |
Start() | 所有 Awake() 调用完成后,场景运行时 | 初始化需要依赖其他对象的逻辑 |
Update() | 每帧调用 | 持续更新逻辑,例如动画、输入检测 |
总结
Unity 调用 Awake()
的原理可以概括如下:
- 加载场景:
- Unity 会加载所有 GameObject 和其组件。
- 检测方法:
- Unity 使用反射检测脚本是否实现了
Awake()
方法。
- Unity 使用反射检测脚本是否实现了
- 方法调用:
- Unity 自动调用实现了
Awake()
的脚本,不需要开发者手动调用。
- Unity 自动调用实现了
通过这个流程,Unity 实现了生命周期的动态管理,使得开发者只需专注于脚本逻辑的实现,而不用关心具体的调用机制。
Unity状态机的Exit结点
在Unity的Animator状态机(Animator State Machine)中,Exit
结点用于表示从当前状态机退出到其父状态机的状态。以下是关于Exit
结点的详细说明:
- Exit结点是什么?
Exit
结点是Unity Animator中的一个特殊的状态机结点,它表示一个状态机结束的出口点。通常用于嵌套的子状态机(Sub-State Machine)中,告诉父状态机当前子状态机的行为已经完成,可以切换到父状态机中的其他状态。
- 使用场景
- 嵌套状态机(Sub-State Machines):
当你将一个复杂的动画逻辑封装到一个子状态机中时,Exit
结点表示该子状态机完成其逻辑后应该退出,回到父状态机进行下一步。 - 动画流程控制:
如果子状态机处理完某些特定动画(如攻击动作、过渡动画等),可以通过Exit
结点返回父状态机,从而进行主流程的继续。
- 如何设置
Exit
结点
在Unity中,以下是设置Exit
结点的步骤: - 创建子状态机:
在Animator中,右键选择Create Sub-State Machine
,创建一个嵌套的子状态机。 - 添加状态和过渡:
在子状态机中添加具体的动画状态(如攻击、跳跃等)。 - 使用
Exit
结点:- 在子状态机中,右键选择
Make Transition
,并将过渡指向Exit
结点。 Exit
结点是子状态机的默认出口,不需要手动创建。
- 在子状态机中,右键选择
- 在父状态机中配置逻辑:
在父状态机中,可以设置子状态机到其他状态(或反过来)的过渡逻辑。
4. Exit
的行为
- 当动画流转到
Exit
结点时,子状态机会退出,控制权回到父状态机。 - 可以通过
Animator Controller
中的条件(如布尔值、触发器等)控制子状态机何时退出。 - 在父状态机中,子状态机到
Exit
的过渡会被认为完成,可以接着切换到其他状态。
- 注意事项
- 不能直接控制
Exit
结点:
Exit
是一个逻辑性的特殊结点,它不能像普通状态一样附加动画或行为。 - 父状态机的后续逻辑:
确保在父状态机中正确设置过渡条件,否则子状态机退出后可能进入意料之外的状态。
- 示例场景
假设有一个游戏角色的动画逻辑:
- 父状态机:
包括“待机”、“跑步”、“攻击子状态机”。 - 子状态机(攻击子状态机):
包括“攻击准备”、“攻击动作”、“攻击结束”。
当“攻击动作”完成后,子状态机会通过Exit
结点返回父状态机,角色的动画状态可能回到“待机”或其他状态。
相关文章:
Unity学习笔记(六)使用状态机重构角色移动、跳跃、冲刺
前言 本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记 整体状态框架(简化) Player 是操作对象的类: 继承了 MonoBehaviour 用于定义游戏对象的行为,每个挂载在 Unity 游戏对象上的脚本都需要继承自 MonoBehaviour&#x…...
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
参考文章: 安装protoc、protoc-gen-go、protoc-gen-go-grpc-CSDN博客 一、简单介绍 本文开发环境,均为 windows 环境,mac 环境其实也类似 ~ ① 编译proto文件,相关插件 简单介绍: protoc 是编译器,用于将…...
策略模式(strategy)
一.策略模式是什么 策略模式是一种行为型对象模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。这样,算法可以独立于使用它的客户端而变化。 策略者模式的核心思想是将一系列的算法封装到一系列的策略类里…...
Centos源码安装MariaDB 基于GTID主从部署(一遍过)
MariaDB安装 安装依赖 yum install cmake ncurses ncurses-devel bison 下载源码 // 下载源码 wget https://downloads.mariadb.org/interstitial/mariadb-10.6.20/source/mariadb-10.6.20.tar.gz // 解压源码 tar xzvf mariadb-10.5.9.tar.gz 编译安装 cmake -DCMAKE_INSTA…...
如何在 VSCode 中配置 C++ 开发环境:详细教程
如何在 VSCode 中配置 C 开发环境:详细教程 在软件开发的过程中,选择一个合适的开发环境是非常重要的。Visual Studio Code(VSCode)作为一款轻量级的代码编辑器,凭借其强大的扩展性和灵活性,受到许多开发者…...
信息安全、网络安全和数据安全的区别和联系
1. 前言 有次有朋友问我 信息安全、网络安全和数据安全,这三个词平时写文档时怎么用? 我想很多人都说不清。这次我查阅了资料,尽量讲清楚这三者之间的区别和联系。 2. 信息安全 2.1 定义 信息安全是指为数据处理系统建立和采用的技术和管…...
路由组件与一般组件的区别
路由组件与一般组件的区别 1. 基本概念 1.1 路由组件 路由组件是指通过路由规则映射的组件,通常放在 pages 或 views 文件夹中。 1.2 一般组件 一般组件是指通过 import 导入后直接使用的组件,通常放在 components 文件夹中。 2. 主要区别 2.1 存…...
【微服务】4、服务保护
微服务架构与组件介绍 单体架构拆分:黑马商城早期为单体架构,后拆分为微服务架构。跨服务调用与组件使用 服务拆分后存在跨服务远程调用,如下单需查询商品信息,使用openfeign组件解决。服务间调用关系复杂,需维护服务…...
6_TypeScript 函数 --[深入浅出 TypeScript 测试]
在 TypeScript 中,函数是编程的核心组成部分之一。TypeScript 不仅继承了 JavaScript 的所有函数特性,还添加了静态类型检查和其他一些增强功能,使得函数更加安全和易于理解。以下是关于 TypeScript 函数的一些关键点和两个具体的示例&#x…...
Apifox=Postman+Swagger+Jmeter+Mock
A. 开发人员接口管理使用(Swagger 工具管理接口) B. 后端开发人员通过Postman 工具,一边开发一边测试 C. 前端开发人员需要Mock 工具提供前端调用 D. 测试人员通过(Postman、Jmeter)等工具进行接口测试 为了后台开发、前端开发、测试工程师等不同角色更加便捷管理…...
升级 Spring Boot 3 配置讲解 —— Spring Boot 3 核心源码专讲
学会这款 🔥全新设计的 Java 脚手架 ,从此面试不再怕! Spring Boot 3 是 Spring 生态中的重要里程碑,它不仅全面支持 Java 17,还引入了许多新特性,如对 GraalVM 原生镜像的支持、改进的性能优化以及更灵活的…...
接口开发完后,个人对于接下来接口优化的一些思考
优化点 入参的合法性和长度范围,必填项的检查验证 因为没有入参,所以不需要考虑。 批量思想解决N1问题 // 假设要查询100个订单及其对应的用户信息 List<Order> orders orderMapper.selectList(new QueryWrapper<>().last("limit …...
jenkins 使用 ssh-agent向windows进行部署
背景: jenkins在linux的docker环境内,应用服务部署在windows。需要使用jenkins实现自动化部署。 实现方式: jenkins上构建pipeline任务,脚本如下: 遇到问题: 1、问题:jenkins 调用部署bat脚…...
音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现
一、引言 通过FFmpeg命令可以获取到PS文件/PS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息: ./ffmpeg -i XXX.ps 本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 二、视频压缩编码格式 (…...
如果Adobe 退出中国后怎么办
最近听说Adobe要退出中国了?那咱们的设计师们可得好好想想怎么搞到正版软件了。别急,今天教大家一个超酷的福利——Edu邮箱! Edu邮箱是什么?有什么好处? Edu邮箱就是学校给学生和老师们发的邮箱,一般结尾是.edu。有了…...
欧几里得距离在权重矩阵中的物理意义
欧几里得距离在权重矩阵中的物理意义 目录 欧几里得距离在权重矩阵中的物理意义**衡量神经元差异程度**:**反映模型变化程度**:**聚类和分组的依据**:自然语言处理中的模型更新:**神经网络聚类分组**:欧几里得距离在权重矩阵中的物理意义衡量神经元差异程度: 在神经网络中…...
玩转大语言模型——ollama导入huggingface下载的模型
ollama导入huggingface模型 前言gguf模型查找相关模型下载模型 导入Ollama配置参数文件导入模型查看导入情况 safetensfors模型下载模型下载llama.cpp配置环境并转换 前言 ollama在大语言模型的应用中十分的方便,但是也存在一定的问题,比如不能使用自己…...
Linux-----进程通讯(管道Pipe)
目录 进程不共享内存 匿名管道 通过匿名管道实现通讯 有名管道 库函数mkfifo() 案例 进程不共享内存 不同进程之间内存是不共享的。是相互独立的。 #include <stdio.h> #include <stdlib.h> #include <errno.h>int num 0;int main(int argc, char con…...
【C++11】列表初始化、右值引用和移动语义、引用折叠、完美转发
C11 一.C的发展历史二.列表初始化1.C98的{}2.C11的{}3.C11中的std::initializer_list 三.右值引用和移动语义1.左值和右值2.左值引用和右值引用3.引用延长生命周期4.左值和右值的参数匹配5.右值引用和移动语义使用场景1.左值引用使用场景2.移动构造和移动赋值3.右值引用和移动语…...
Openssl1.1.1s rpm包构建与升级
rpmbuild入门知识 openssh/ssl二进制升级 文章目录 前言一、资源准备1.下载openssh、openssl二进制包2.安装rpmbuild工具3.拷贝源码包到SOURCES目录下4.系统开启telnet,防止意外导致shh无法连接5.编译工具安装6.补充说明 二、制作 OpenSSL RPM 包1.编写 SPEC 文件2.…...
递归思想的深度理解——汉诺塔问题和青蛙跳台阶问题
递归的深度理解——汉诺塔问题and青蛙跳台阶问题 青蛙跳台阶问题汉诺塔问题 青蛙跳台阶问题 问题:一只青蛙可以一次跳一级台阶,也可以一次跳两级台阶,如果青蛙要跳n级台阶,共有多少种跳法? 解答:我们可以先…...
从数据到诊断:朴素贝叶斯算法助力肿瘤预测之路
1.案例概述 肿瘤性质的判断影响着患者的治疗方式和痊愈速度。传统的做法是医生根据数十个指标来判断肿瘤的性质,预测效果依赖于医生的个人经验而且效率较低,而通过机器学习有望能快速预测肿瘤的性质。 2.数据集 本次肿瘤预测使用的数据集共有569组样本…...
Element-UI:如何实现表格组件el-table多选场景下根据数据对某一行进行禁止被选中?
如何实现表格组件el-table多选场景下根据数据对某一行进行禁止被选中? 在使用 Element UI 的 Table 组件时,如果你想要禁用某一行的选中(特别是在多选模式下),可以通过自定义行的 selectable 属性来实现。selectable …...
Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py
此脚本的主要功能是可视化点云数据文件(.pcd 文件),并通过键盘交互选择演示数据的起始帧和结束帧,生成片段标记文件 (clip_marks.json) 主要流程包括: 用户指定数据目录:检查目录是否存在并处理标记文件 -…...
JWT理解
前言 随着互联网的快速发展,身份验证和授权成为了许多应用的重要需求。JWT(JSON Web Token)作为一种轻量级的身份验证和授权机制,得到了广泛的应用。本文将为您详细介绍JWT的原理、结构和优点,帮助您更好地理解和应用…...
一种融合联邦学习和大模型特点的全新系统架构
一种融合联邦学习和大模型特点的全新系统架构 以下是一种融合联邦学习和大模型特点的全新系统架构设计: 分层分布式架构 底层 - 数据采集与预处理层:由大量的边缘设备和终端节点组成,如智能手机、物联网传感器等。这些设备负责采集本地数据,并在本地进行初步的数据预处理,…...
html表格table导出excel,主从表格式,带样式.自动分列
html的table导出成excel, vue模板 项目使用xlsx-js-style 源代码从https://github.com/gitbrent/xlsx-js-style/releases/tag/v1.2.0 下载 用里面的dist目录下的文件即可. 复制到vue项目的public目录下的XLSX目录下. 在index.hml中引入js脚本, 为啥要在这里引入? 是因为这里…...
U8G2库使用案例(stm32)
目录 一、小球在 OLED 屏幕平面内运动并碰撞反弹的效果 二、 简单的波形生成和显示程序: 三、三维三角形旋转展示 四、正方形平面内顺时针旋转 五、带有旋转点的空心圆圈应用 六、字幕滚动效果 七、下雪动画效果 八、进度条动画效果 自己移植的U8g2库,OLED库…...
067B-基于R语言平台Biomod2模型的物种分布建模与数据可视化-高阶课程【2025】
课程培训包含:发票全套软件脚本学习数据视频文件导师答疑 本教程旨在通过系统的培训学习,学员可以掌握Biomod2模型最新版本的使用方法,最新版包含12个模型(ANN, CTA, FDA, GAM, GBM, GLM, MARS, MAXENT, MAXNET, RF, SRE, XGBOOST…...
【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜
AI的两次寒冬:从感知机困局到深度学习前夜 引用(中英双语) 中文: “第一次AI寒冬,是因为感知机局限性被揭示,让人们失去了对算法可行性的信心。” “第二次AI寒冬,则是因为专家系统的局限性和硬…...
141.《mac m系列芯片安装mongodb详细教程》
文章目录 下载从官网下载安装包 下载后双击解压出文件夹安装文件名修改为 mongodb配置data存放位置和日志log的存放位置启动方式一方式二方式二:输入mongo报错以及解决办法 本人电脑 m2 pro,属于 arm 架构 下载 官网地址: mongodb官网 怎么查看自己电脑应该下载哪个版本,输入…...
【Linux】sed编辑器
一、基本介绍 sed编辑器也叫流编辑器(stream editor),它是根据事先设计好得一组规则编辑数据流。 交互式文本编辑器(如Vim)中,可以用键盘命令交互式地插入、删除或替换文本数据。 sed编辑器是根据命令处理…...
unity3d-搞个场景漫游如何实现Alpha
要处理两个问题: 如何设置地面人不掉下去 方法一、 游戏物体加刚体,将游戏物体和地面加collider。如果是地形,可以使用 Terrain Collider;如果是简单的平面,可以添加 Box Collider 或者 Mesh Collider(如果…...
概率基本概念 --- 离散型随机变量实例
条件概率&独立事件 随机变量 - 离散型随机变量 - 非离散型随机变量 连续型随机变量奇异性型随机变量 概率表示 概率分布函数概率密度函数概率质量函数全概率公式贝叶斯公式 概率计算 数学期望方差协方差 计算实例 假设有两个离散型随机变量X和Y,它们代…...
oscp备考 oscp系列——Kioptix Level 1靶场 古老的 Apache Vuln
目录 前言 1. 主机发现 2. 端口扫描 3. 指纹识别 4. 目录扫描 5. 漏洞搜索和利用 前言 oscp备考,oscp系列——Kioptix Level 1靶场 Kioptix Level 1难度为简单靶场,主要考察 nmap的使用已经是否会看输出,以及是否会通过应用查找对应漏…...
【简博士统计学习方法】3. 统计学习方法的三要素
3. 统计学习方法的三要素 3.1 监督学习的三要素 3.1.1 模型 假设空间(Hypothesis Space):所有可能的条件概率分布或决策函数,用 F \mathcal{F} F表示。 若定义为决策函数的集合: F { f ∣ Y f ( X ) } \mathcal{F…...
UnionTech OS Server 20 网页无法访问yum源地址
统信yum地址 https://euler-packages.chinauos.com/server-euler/fuyu/1060/everything/sw_64/Packages/ 浏览器访问401报错无权限,查看linux uos环境下yum配置的用户名和密码 cat /etc/yum/vars/auth_* 然后自己组装生成Basic Authorization def generate_basic_…...
WPF区域导航+导航参数使用+路由守卫+导航日志
背景:使用ContentControl控件实现区域导航是有Mvvm框架的WPF都能使用的,不限于Prism 主要是将ContenControl控件的Content内容在ViewModel中切换成不同的用户控件 下面是MainViewModel: private object body;public object Body {get { retu…...
jvm基础
jvm的基本结构 类加载器(ClassLoader):加载class文件到内存中进行使用。 运行时数据区(Runtime Data Area):这是JVM在运行Java程序期间管理的内存区域,包括方法区(Meta…...
kaggle竞赛:纽约出租车行程时间NYC Taxi Trip Duration
1.引言 作为一名(坦白说有点懒的)图像处理方向的研究生,说实话最近新开一个坑,可能是因为要寒假了比较无聊,这次带来的系列是kaggle数据处理竞赛的经典例题:纽约出租车行程时间问题。希望大家多多支持&…...
Python提取目标Json键值:包含子嵌套列表和字典
目标:取json中所有的Name、Age字典 思路:递归处理字典中直接包含子字典的情况, import jsondef find_targ_dicts(data,key1,key2):result {}if isinstance(data, dict):if key1 in data and key2 in data: # 第一层字典中包含key1和key2re…...
<div>{{ $t(“collectionPlan“) }}</div> 中的$t是什么
$t是Vue I18n插件提供的一种方法,用于根据当前应用的语言环境来获取相应的翻译文本。 以下是一个简单的示例,展示如何在Vue I18n中定义消息: const i18n new VueI18n({locale: en, // 设置默认语言messages: {en: {collectionPlan: Collec…...
医学图像分析工具01:FreeSurfer || Recon -all 全流程MRI皮质表面重建
FreeSurfer是什么 FreeSurfer 是一个功能强大的神经影像学分析软件包,广泛用于处理和可视化大脑的横断面和纵向研究数据。该软件由马萨诸塞州总医院的Martinos生物医学成像中心的计算神经影像实验室开发,旨在为神经科学研究人员提供一个高效、精确的数据…...
win32汇编环境,在对话框中画五边形与六边形
;运行效果 ;win32汇编环境,在对话框中画五边形与六边形 ;展示五边形与六边形的画法 ;将代码复制进radasm软件里,直接编译可运行.重要部分加备注。 ;下面为asm文件 ;>>>>>>>>>>>>>>>>>>>>>>>>>&g…...
小白学Pytorch
小白学Pytorch 发现一个比较好的教程,对于自己来说比较合适,适合从零开始的教程。 1、搭建一个简单的网络 https://www.cnblogs.com/PythonLearner/p/13587092.html 搭建网络这步说的比较清楚: 我们使用nn包中的Sequential搭建网络&#…...
[A-25]ARMv8/v9-GIC的系统架构(中断的硬件基础)
ver0.1 前言 我们在观看很多的影视剧过程中,尤其是军旅体裁类型的布景中,经常会看见高级干部的办公桌上都会有几部电话机。这样的电话可不能小看,重要的事情尤其是突发和紧急的情况都要通过这几部电话第一时间通知给决策者。这几部电话,必须举报几个特点:及时性好、稳定…...
毕业项目推荐:基于yolov8/yolov5的行人检测识别系统(python+卷积神经网络)
文章目录 概要一、整体资源介绍技术要点功能展示:功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出(xls格式)功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…...
学习threejs,导入AWD格式的模型
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.AWDLoader AWD模型加…...
C# 事件
目录 1、事件模型的5个组成部分2、使用内置委托类型声明事件2.1 EventHandler2.1.1 ?2.1.2 this2.1.3 使用匿名函数和lamda表达式2.1.3.1 匿名函数2.1.3.2 lamda表达式 2.1.4 异常处理 2.2 EventHandler<TEventArgs> 3、使用自定义委托类型声明事件3.1 事件的…...
WebRtc02: WebRtc架构、目录结构、运行机制
整体架构 WebRtc主要分为三层: CAPI层:外层调用Session管理核心层:包括视频引擎、音频引擎、网络传输 可由使用者重写视频引擎:编解码器、视频缓存、视频增强音频引擎:编解码器、音频缓存、回音消除、降噪传输&#x…...