《UE5_C++多人TPS完整教程》学习笔记42 ——《P43 瞄准(Aiming)》


本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P43 瞄准(Aiming)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author) Stephen Ulibarri 发布在 Udemy 上的课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
在这里插入图片描述


文章目录

  • P43 瞄准(Aiming)
  • 43.1 创建瞄准动作事件
  • 43.2 创建站立、蹲伏瞄准动画蓝图
  • 43.3 瞄准相关的变量复制及 RPC 函数
  • 43.4 Summary


P43 瞄准(Aiming)

本节课我们将学习瞄准,这涉及瞄准动画姿势的改变。瞄准与枪战功能组件有关,我们将在枪战功能组件中实现瞄准,以适配多人游戏。
在这里插入图片描述


43.1 创建瞄准动作事件

  1. 在虚幻引擎打开 “项目设置”(Project Settings),在 “引擎”(Engine)下找到 “输入”(Input),添加 “动作映射”(Action Mappings)“Aim”,当我们单击 “鼠标右键”(Right Mouse Button) 时能使得人物角色进行瞄准。
    在这里插入图片描述

  2. 我们想持续按住 “鼠标右键” 时进行瞄准,松开 “鼠标右键” 时停止瞄准。因此,在 “BlasterCharacter.h” 中声明动作映射 “Aim” 的回调函数 “AimButtonPressed()” 和 “AimButtonReleased()”,接着在 “BlasterCharacter.cpp” 的 “SetupPlayerInputComponent()” 函数中绑定回调函数 “AimButtonPressed()” 和 “AimButtonReleased()”。

    /*** BlasterCharacter.h ***/...UCLASS()
    class BLASTER_API ABlasterCharacter : public ACharacter
    {GENERATED_BODY()...protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;// 与轴映射相对应的回调函数void MoveForward(float Value);	// 人物角色前进或后退void MoveRight(float Value);	// 人物角色左移或右移void Turn(float Value);			// 人物角色视角左转或右转void LookUp(float Value);		// 人物角色俯视或仰视// 与动作映射相对应的回调函数void EquipButtonPressed();		// 人物角色装备武器void CrouchButtonPressed();		// 人物角色蹲伏/* P43 瞄准(Aiming)*/void AimButtonPressed();		// 人物角色开始瞄准void AimButtonReleased();		// 人物角色停止瞄准/* P43 瞄准(Aiming)*/...};
    
    /*** BlasterCharacter.cpp ***/...// Called to bind functionality to input
    void ABlasterCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {Super::SetupPlayerInputComponent(PlayerInputComponent);// 绑定动作映射PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);PlayerInputComponent->BindAction("Equip", IE_Pressed, this, &ABlasterCharacter::EquipButtonPressed);PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &ABlasterCharacter::CrouchButtonPressed);/* P43 瞄准(Aiming)*/PlayerInputComponent->BindAction("Aim", IE_Pressed, this, &ABlasterCharacter::AimButtonPressed);PlayerInputComponent->BindAction("Aim", IE_Released, this, &ABlasterCharacter::AimButtonReleased);/* P43 瞄准(Aiming)*/// 绑定轴映射PlayerInputComponent->BindAxis("MoveForward", this, &ABlasterCharacter::MoveForward);PlayerInputComponent->BindAxis("MoveRight", this, &ABlasterCharacter::MoveRight);PlayerInputComponent->BindAxis("Turn", this, &ABlasterCharacter::Turn);PlayerInputComponent->BindAxis("LookUp", this, &ABlasterCharacter::LookUp);
    }.../* P43 瞄准(Aiming)*/
    // 按住鼠标右键开始瞄准
    void ABlasterCharacter::AimButtonPressed()
    {}// 松开鼠标右键停止瞄准
    void ABlasterCharacter::AimButtonReleased()
    {}
    /* P43 瞄准(Aiming)*/
    
  3. 我们添加一些变量或函数来记录我们是否在瞄准。在 “CombatComponent.h” 中定义一个布尔变量 “bAiming”,接着在 “BlasterCharacter.h” 中创建一个返回值为布尔变量的函数 “IsAiming()” 并在 “BlasterCharacter.cpp” 中仿照函数 “IsWeaponEquipped()” 完成 “IsAiming()” 定义。在 “BlasterAnimInstance.h” 中定义一个布尔变量 “bIsAiming”,用以记录人物角色是否在瞄准;随后,在 “BlasterAnimInstance.cpp” 的 “NativeUpdateAnimation()” 函数中通过调用 “BlasterCharacter” 的 “IsAiming()” 函数更新 “bAiming” 的值。

    /*** CombatComponent.h ***/...UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class BLASTER_API UCombatComponent : public UActorComponent
    {GENERATED_BODY()...private:class ABlasterCharacter* Character;	// 声明人物角色类,避免反复 casting 到 ABlasterCharacterUPROPERTY(Replicated)class AWeapon* EquippedWeapon;		// 保存当前装备的武器/* P43 瞄准(Aiming)*/bool bAiming;						// 是否在瞄准/* P43 瞄准(Aiming)*/
    };
    
    /*** BlasterCharacter.h ***/UCLASS()
    class BLASTER_API ABlasterCharacter : public ACharacter
    {GENERATED_BODY()...public:	...bool IsWeaponEquipped();	// 判断是否装备了武器/* P43 瞄准(Aiming)*/bool IsAiming();			// 判断是否在瞄准/* P43 瞄准(Aiming)*/
    };
    /*** BlasterCharacter.cpp ***/...bool ABlasterCharacter::IsWeaponEquipped()
    {return (Combat && Combat->EquippedWeapon);	// 返回值为是否装备了武器
    }/* P43 瞄准(Aiming)*/
    bool ABlasterCharacter::IsAiming()				
    {return (Combat && Combat->bAiming);	// 返回值为是否装备了武器
    }
    /* P43 瞄准(Aiming)*/
    
    /*** BlasterAnimInstance.h ***/...UCLASS()
    class BLASTER_API UBlasterAnimInstance : public UAnimInstance
    {GENERATED_BODY()...private:...UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;bool bWeaponEquipped;						// 是否装备了武器UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;bool bIsCrouched;							// 是否在蹲伏/* P43 瞄准(Aiming)*/UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))		// 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;bool bAiming;/* P43 瞄准(Aiming)*/
    };
    
    /*** BlasterAnimInstance.cpp ***/...void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaTime)		// 原生(Native)类更新函数 NativeUpdateAnimation() 覆写,用于在每一帧调用以更新动画
    {...bWeaponEquipped = BlasterCharacter->IsWeaponEquipped();				// 调用 BlasterCharacter 的 IsWeaponEquipped() 函数判断人物角色是否装备了武器bIsCrouched = BlasterCharacter->bIsCrouched;						// 访问 BlasterCharacter 的 bIsCrouched 变量值来判断人物角色是否在蹲伏/* P43 瞄准(Aiming)*/bAiming = BlasterCharacter->IsAiming();								// 调用 BlasterCharacter 的 IsAiming() 函数来判断人物角色是否在瞄准/* P43 瞄准(Aiming)*/
    }...	
    
  4. 回到 “BlasterCharacter.cpp” 中对动作映射 “Aim” 的回调函数 “AimButtonPressed()” 和 “AimButtonReleased()” 进行简单的定义,随后进行编译。

    /*** BlasterCharacter.cpp ***/.../* P43 瞄准(Aiming)*/
    // 按住鼠标右键进行瞄准
    void ABlasterCharacter::AimButtonPressed()
    {if (Combat) {Combat->bAiming = true;}
    }// 松开鼠标右键取消瞄准
    void ABlasterCharacter::AimButtonReleased()
    {if (Combat) {Combat->bAiming = false;}
    }
    /* P43 瞄准(Aiming)*/...
    

43.2 创建站立、蹲伏瞄准动画蓝图

  1. 在虚幻引擎中打开动画蓝图 “BlasterAnimBP”,然后在 “AnimGraph” 面板中打开状态机 “Equipped” 的 “Idle” 状态编辑界面,在右侧内容浏览器中将动画资产 “Idle_Ironsights” 拖拽至面板中生成 “序列播放器 Idle_Rifle_Ironsights”(Play Idle_Rifle_Ironsights) 蓝图节点;接着,新增节点 “按布尔混合姿势”(Blend Poses by bool)以及 “获取 Aiming”(Get Aiming),并将生成的蓝图变量 “Aiming” 连接到 “按布尔混合姿势” 节点的 “Active Value” 引脚;然后,将 “序列播放器 Idle_Rifle_Ironsights” 节点的输出引脚连接到 “按布尔混合姿势” 节点的 “真 姿势”(True Pose)引脚,将原先的 “序列播放器 Idle_Rifle_Hip” 节点的输出引脚连接到 “按布尔混合姿势” 节点的 “False 姿势”(False Pose)引脚,最后将 “按布尔混合姿势” 的输出引脚连接到 “输出动画姿势”(Output Animation Pose) 节点的 “Result” 输入引脚上。这段蓝图表示如果 “Aiming” 的值为 “true” 则输出人物角色站立瞄准姿势,否则输出站立待机姿势。
    在这里插入图片描述

  2. 在 “AnimGraph” 面板中打开状态机 “Equipped” 的 “CrouchIdle” 状态编辑界面,在右侧内容浏览器中将动画资产 “Crouch_Idle_Rifle_Ironsights” 拖拽至面板中生成 “序列播放器 Crouch_Idle_Rifle_Ironsights”(Play Crouch_Idle_Rifle_Ironsights) 蓝图节点,然后仿照 步骤 1 绘制如下图所示的蓝图。这段蓝图表示如果 “Aiming” 的值为 “true” 则输出人物角色蹲伏瞄准姿势,否则输出蹲伏待机姿势。
    在这里插入图片描述

  3. 编译、保存后进行测试。我们操控一个客户端上的人物角色进行瞄准时,在本地可以看到站立和蹲伏的瞄准动画姿势,但是在另一个客户端和服务器上均看不到本地的人物角色在瞄准,这说明我们需要对瞄准动作事件中的变量进行复制,创建相应的 RPC 函数,以实现瞄准动画姿势的网络同步。
    在这里插入图片描述
    在这里插入图片描述


43.3 瞄准相关的变量复制及 RPC 函数

  1. 在 “CombatComponent.h” 中将 “bAiming” 重新定义为可复制的变量,在 “CombatComponent.cpp” 的 “GetLifetimeReplicatedProps” 函数中添加 “DOREPLIFETIME()” 宏调用,以在派生的 “UCombatComponent” 实例的生命周期内复制 “bAming”。

    /*** CombatComponent.h ***/...UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class BLASTER_API UCombatComponent : public UActorComponent
    {GENERATED_BODY()...private:class ABlasterCharacter* Character;	// 声明人物角色类,避免反复 casting 到 ABlasterCharacterUPROPERTY(Replicated)class AWeapon* EquippedWeapon;		// 保存当前装备的武器/* P43 瞄准(Aiming)*/UPROPERTY(Replicated)				// 可复制变量bool bAiming;						// 是否在瞄准/* P43 瞄准(Aiming)*/
    };
    
    /*** CombatComponent.cpp ***/...// 重写复制属性函数
    void UCombatComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
    {// 调用SuperSuper::GetLifetimeReplicatedProps(OutLifetimeProps);// 添加要为派生的类 UCombatComponent 复制的属性,需要添加头文件 "Net/UnrealNetwork.h"// DOREPLIFETIME 宏用于指定 UCombatComponent 哪些属性需要被复制,以及复制的条件。DOREPLIFETIME(UCombatComponent, EquippedWeapon);/* P43 瞄准(Aiming)*/DOREPLIFETIME(UCombatComponent, bAiming);/* P43 瞄准(Aiming)*/
    }
    
  2. 编译后进行测试,当我们操控服务器上的人物角色进行站立和蹲伏的瞄准时,所有的客户端都可以看到服务器上的人物角色在进行瞄准;但当我们在客户端上进行瞄准时,只有本地的客户端自己可见,另一个客户端和服务器上均不可见。这再次说明变量复制是单向的,只能从服务器到客户端,因此需要实现从客户端到服务器 RPC 函数。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 在 “CombatComponent.h” 中声明一个可以设置 “bAiming” 的值的函数 “SetAiming()”,接着使用带有 “Server” 和 “Reliable” 关键字的 “UFUNCTION()” 宏声明服务器可靠 RPC 函数 “ServerSetAiming()”。接着,在 “CombatComponent.cpp” 中完成 “SetAiming()” 和 “ServerSetAiming()” 的定义,并在 “ServerSetAiming()” 函数名后添加 “_Implementation” 使之成为 “实施函数”。随后,修改 “BlasterCharacter.cpp” 中动作映射 “Aim” 的回调函数 “AimButtonPressed()” 和 “AimButtonReleased()” 的定义,以调用 “SetAiming()” 函数。

    /*** CombatComponent.h ***/...UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
    class BLASTER_API UCombatComponent : public UActorComponent
    {GENERATED_BODY()...protected:// Called when the game startsvirtual void BeginPlay() override;/* P43 瞄准(Aiming)*/void SetAiming(bool bIsAiming);UFUNCTION(Server, Reliable)void ServerSetAiming(bool bIsAiming);/* P43 瞄准(Aiming)*/private:class ABlasterCharacter* Character;	// 声明人物角色类,避免反复 casting 到 ABlasterCharacterUPROPERTY(Replicated)class AWeapon* EquippedWeapon;		// 保存当前装备的武器/* P43 瞄准(Aiming)*/UPROPERTY(Replicated)				// 可复制变量bool bAiming;						// 是否在瞄准/* P43 瞄准(Aiming)*/
    };
    
    /*** CombatComponent.cpp ***/.../* P43 瞄准(Aiming)*/
    // 设置 bAiming
    void UCombatComponent::SetAiming(bool bIsAiming)
    {bAiming = bIsAiming;// 由虚幻引擎官方文档 “从服务器调用的 RPC” 的表格 “Server” 这一列和 “从客户端调用的 RPC” 的表格第一行 “Server” 列可知,// 以 Server 关键字声明的 RPC 函数无论在客户端还是服务器上调用都是在服务器上执行,因此无需对当前机器是客户端还是服务器端进行判断。//if (!Character->HasAuthority()) {//	ServerSetAiming(bIsAiming);//}ServerSetAiming(bIsAiming);
    }// 服务器可靠 RPC 函数,bAiming 会被服务器复制到所有客户端
    void UCombatComponent::ServerSetAiming_Implementation(bool bIsAiming)
    {bAiming = bIsAiming;	// 由于函数在服务器上执行且 bAiming 可复制,服务器会将 bAiming 值的更新复制到所有客户端
    }
    /* P43 瞄准(Aiming)*/
    
    /*** BlasterCharacter.cpp ***/
    /* P43 瞄准(Aiming)*/
    // 按住鼠标右键进行瞄准
    void ABlasterCharacter::AimButtonPressed()
    {if (Combat) {/*Combat->bAiming = true;*/Combat->SetAiming(true);}
    }// 松开鼠标右键取消瞄准
    void ABlasterCharacter::AimButtonReleased()
    {if (Combat) {/*Combat->bAiming = false;*/Combat->SetAiming(false);}
    }
    /* P43 瞄准(Aiming)*/
    

    要求和注意事项
    您必须满足一些要求才能充分发挥 RPC 的作用:

    • 它们必须从 Actor 上调用。
    • Actor 必须被复制。
    • 如果 RPC 是从服务器调用并在客户端上执行,则只有实际拥有这个 Actor 的客户端才会执行函数。
    • 如果 RPC 是从客户端调用并在服务器上执行,客户端就必须拥有调用 RPC 的 Actor。

    多播 RPC 则是个例外:

    • 如果它们是从服务器调用,服务器将在本地和所有已连接的客户端上执行它们。
    • 如果它们是从客户端调用,则只在本地而非服务器上执行。

    现在,我们有了一个简单的多播事件限制机制:在特定 Actor 的网络更新期内,多播函数将不会复制两次以上。按长期计划,我们会对此进行改善,同时更好的支持跨通道流量管理与限制。

    下面的表格根据执行调用的 actor 的所有权(最左边的一列),总结了特定类型的 RPC 将在哪里执行。

    从服务器调用的 RPC

    Actor 所有权未复制NetMulticastServerClient
    Client-owned actor在服务器上运行在服务器和所有客户端上运行在服务器上运行在 actor 的所属客户端上运行
    Server-owned actor在服务器上运行在服务器和所有客户端上运行在服务器上运行在服务器上运行
    Unowned actor在服务器上运行在服务器和所有客户端上运行在服务器上运行在服务器上运行

    从客户端调用的 RPC

    Actor 所有权未复制NetMulticastServerClient
    Owned by invoking client在执行调用的客户端上运行在执行调用的客户端上运行在服务器上运行在执行调用的客户端上运行
    Owned by a different client在执行调用的客户端上运行在执行调用的客户端上运行丢弃在执行调用的客户端上运行
    Server-owned actor在执行调用的客户端上运行在执行调用的客户端上运行丢弃在执行调用的客户端上运行
    Unowned actor在执行调用的客户端上运行在执行调用的客户端上运行丢弃在执行调用的客户端上运行

    —— 虚幻引擎官方文档《虚幻引擎中的 RPC》

  4. 编译后进行测试,当我们操控一个客户端上的人物角色进行瞄准时,在本地客户端可以看到站立和蹲伏的瞄准动画姿势,在另一个客户端和服务器上也能都能看到。
    在这里插入图片描述
    在这里插入图片描述


43.4 Summary

本节课我们实现了人物角色的瞄准功能及其网络同步机制。首先,在项目设置中添加 “Aim” 动作映射,绑定为鼠标右键,然后在人物角色类 “ABlasterCharacter” 中声明并实现了该动作映射下按住与释放鼠标右键的回调函数。接着,在枪战组件 “UCombatComponent”中创建了布尔变量 “bAiming” 表示人物角色的瞄准状态,并通过人物角色类 “ABlasterCharacter” 的 “IsAiming()” 方法供动画实例 “UBlasterAnimInstance” 进行访问,同时 “UBlasterAnimInstance” 中新增 “bIsAiming” 变量,在每帧更新时同步人物角色瞄准状态。然后,在动画蓝图中重构了持枪状态机的动画逻辑,即人物角色在站立与蹲伏状态下,根据“bIsAiming” 变量切换待机姿势与瞄准姿势。
在进行测试时,我们发现本地操控人物角色进行瞄准时可以看到对应的动画姿势,但其他机器上却无法看到,因而需要对瞄准相关的变量进行网络同步。首先,将枪战组件的 “bAiming” 注册为复制属性,这样服务器上人物角色的瞄准状态能复制、更新到所有客户端,当服务器上的人物角色进行瞄准时,所有客户端也能看到;随后,创建服务器可靠 的 RPC 函数 “ServerSetAiming()”,当客户端操控人物角色进行瞄准时通过“SetAiming()” 方法调用该 RPC 函数,并在服务器执行,保证将客户端上人物角色的瞄准状态更新到其他客户端。最终,测试结果验证了所有客户端都能正确显示本地及其他玩家的瞄准状态,实现多人游戏下瞄准状态及动画姿势的网络同步。
在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90330.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL Server 临时表、表变量与WITH语句的用法与区别

引言 在SQL Server数据处理中,临时表、表变量和WITH语句(CTE)是关键的中间结果集管理工具。临时表适合大数据量操作,表变量优化小数据量场景,而CTE则简化复杂查询逻辑。三者选择需综合考量数据量级、事务需求及代码可读性。本文将深入解析其工作机制,通过实测对比指导场…

【Android】组件及布局介绍

一&#xff1a;代码分析 1&#xff1a;Android界面开发方式 &#xff08;1&#xff09;JavaView&#xff08;传统视图系统&#xff09; 这是 Android 早期的开发方式&#xff0c;用 Java 或 Kotlin 代码配合 XML 布局文件 来构建界面。&#xff08;简单了解即可&#xff09; 分…

Android 音视频 IPC序列化工具-Flattenable

Android Binder与AIDL与Service使用案例及分析-CSDN博客 讲讲这个类,被用在Android音视频中,跨进程序列化反序列化用。与Binder驱动有很强的联系。位于: feameworks/native/utils/Flattenable.h Flattenable, 译为令人满意的。可能是作者十分满意自己的这些作品吧,起了这…

文献学习|全面绘制和建模水稻调控组景观揭示了复杂性状背后的调控架构。

摘要&#xff1a; 解析调控复杂性状的机制对于推进作物改良至关重要。在此&#xff0c;我们提出了一个全面的水稻&#xff08;Oryza sativa&#xff09;调控组图谱&#xff0c;涵盖了来自三个代表性品种的23种不同组织的染色质可及性。我们的研究揭示了117,176个独特的开放染色…

Linux的压缩与解压缩

一、使用tar命令进行打包与解包 1.0、tar命令简介和常用选项 tar命令是Linux中经常使用的归档工具&#xff0c;它的主要功能是【对文件或者目录进行打包归档】&#xff0c;归档为一个文件&#xff0c;但是并不进行压缩&#xff1b;tar命令的归档操作效果如下&#xff1a; tar命…

OpenCV+OCR实现弧形文字识别

以下是基于OpenCV与OCR实现弧形文字识别的完整技术方案&#xff0c;结合了图像预处理、几何变换与OCR引擎调用等关键步骤&#xff0c;并提供优化技巧&#xff1a;&#x1f50d; 一、技术原理弧形文字识别的核心在于​​将弯曲文本转换为水平直线​​&#xff0c;便于OCR引擎处理…

【保姆级目标检测教程】Ubuntu 20.04 部署 YOLOv13 全流程(附训练/推理代码)

前言 YOLOv13 是 YOLO 系列的全新一代实时目标检测框架&#xff0c;在保持极高推理速度的同时显著提升了检测精度&#xff0c;广泛适用于嵌入式部署、工业质检、智能安防等多种场景。该版本提供了 Nano、Small、Large、X-Large 四种模型规格&#xff0c;用户可以根据计算资源和…

【大模型】到底什么是Function Calling和MCP,以及和ReAct推理的关系是什么?

文章目录背景&#xff1a;什么是Agent&#xff1f;背景&#xff1a;为什么需要Function Calling或者MCP&#xff1f;Function Calling和MCP在用户请求中的整体流程Function Calling&#xff08;函数/工具调用&#xff09;MCP (Model Context Protocol)ReAct (Reasoning and Act…

CANDENCE 17.4 进行元器件缓存更新

在我从立创商城导入CANDENCE元器件后&#xff0c;在ORCAD放置元器件时出现了下面的错误解决办法&#xff1a;1、在左边找到 Design Cache文件夹&#xff0c;在文件夹上鼠标右击选择 Cleanup Cache2、再放置该元器件&#xff0c;不管这个&#xff0c;点击确定3、这时候成功放上…

深入理解Kafka幂等性:原理、边界与最佳实践

一、什么是真正的消息幂等性&#xff1f; 消息系统的幂等性经常被误解&#xff0c;我们需要明确其精确含义和能力边界&#xff1a; 1. 正确定义 Kafka幂等性保证的是&#xff1a;在消息传输过程中&#xff0c;无论因网络重试、生产者重启等故障导致的消息重复发送&#xff0c;B…

【RTSP从零实践】8、多播传输H264码流的RTSP服务器——最简单的实现例子(附带源码)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【Linux】基础开发工具(3)

1. 版本控制器Git1.1 Git的简史Git 的历史可以追溯到 2005 年1。当时 Linux 内核项目的开发团队一直使用 BitKeeper 进行版本管理&#xff0c;但由于一位 Linux 开发成员写了一个连接 BitKeeper 仓库的外挂&#xff0c;BitMover 公司决定中止 Linux 免费使用 BitKeeper 的授权1…

synchronized 的使用和特性

synchronized 锁对象 普通方法 synchronized 锁普通方法时&#xff0c;其锁的对象是调用该方法的实例 public synchronized void method() { // 方法体 } 静态方法 静态方法的锁对象是所属的 class&#xff0c;全局只有一个。 public static synchronized void staticMetho…

Gin Web 层集成 Viper 配置文件和 Zap 日志文件指南(下)

在微服务架构中&#xff0c;Gin 常被用作 Web 层框架&#xff0c;而 Viper 用于管理配置文件&#xff0c;Zap 则提供高性能的日志记录功能。下面将详细介绍如何在 Gin Web 层集成 Viper 配置文件和 Zap 日志文件。 1. 项目概述 假设我们有一个基于 Go 语言的微服务项目&#…

IoTDB:专为物联网场景设计的高性能时序数据库

什么是IoTDB&#xff1f;IoTDB&#xff08;Internet of Things Database&#xff09;是一款开源的时序数据库管理系统&#xff0c;专为物联网&#xff08;IoT&#xff09;场景设计&#xff0c;由清华大学软件学院团队自研&#xff0c;天谋科技团队负责维护。它针对物联网数据的…

[netty5: MessageAggregator HttpObjectAggregator]-源码解析

在阅读这篇文章前&#xff0c;推荐先阅读 [netty5: ByteToMessageCodec & MessageToByteEncoder & ByteToMessageDecoder]-源码分析[netty5: HttpObject]-源码解析 100-continue 100-continue 是 HTTP/1.1 协议中的一种机制&#xff0c;用于客户端在发送大体积请求体…

前端学习1--行内元素 vs 块级元素(基础概念+案例实操)

一、内外边距学习&#xff1a;&#xff08;1&#xff09;简单理解&#xff1a;padding为内边距。padding不会影响元素的位置&#xff0c;只会调整元素的内容&#xff08;文字&#xff09;与边框之间的间距。margin为外边距。margin会影响元素在流式布局中的位置&#xff0c;改变…

Express + mysql2 + jwt 实现简单的登录鉴权

目前项目中使用Express 实现简单API功能&#xff0c;需要提供一套登录鉴权方案。这边是API侧实现 相关路由的登录鉴权。大体思路&#xff1a;就是&#xff0c;登录接口中通过jwt加密 token返回前端&#xff0c;前端其他接口把加密好的放入请求头Authorization中。中间件通过请求…

ReAct (Reason and Act) OR 强化学习(Reinforcement Learning, RL)

这个问题触及了现代AI智能体&#xff08;Agent&#xff09;构建的两种核心思想。 简单来说&#xff0c;ReAct 是一种“调用专家”的模式&#xff0c;而强化学习 (RL) 是一种“从零试错”的模式。 为了让你更清晰地理解&#xff0c;我们从一个生动的比喻开始&#xff0c;然后进行…

iTwinjs 4.10-4.11 更新

撤销更改 目前&#xff0c;撤销一个有缺陷的变更集的唯一方法是从 iModel Hub 中移除它&#xff0c;这可能会导致许多副作用&#xff08;无法撤销&#xff09;。一个更好的方法是在时间线中撤销变更集&#xff0c;并将其作为新的变更集引入。尽管这种方法仍然具有侵入性&#…