六一的部落格


关关难过关关过,前路漫漫亦灿灿。




概览

  • 为HeroTPP添加Socket, 在背后挂载未使用武器
  • 绑定键位, 按下Tab或滚动鼠标时切换武器
  • 游戏角色死亡后, 销毁武器
  • 处理Rifle开火定时器

为骨骼网格体添加Socket

虚幻编辑器

  1. 打开Content/ExternalContent/Animation/Characters/HeroTPP/HeroTPP

  2. 选择骨骼b_Spine1, 添加Socket

  3. 在细节面板设置Socket

    -
    Socket Name ArmorySocket
    Relative Location (-9, -26, -23)
    Relative Rotation (-10, 70, -29)


  4. 为Socket添加预览资产, 选择Launcher

    武器未使用时背在背上



绑定切换武器键位

项目设置 > Engine > Input > Action Mappings

-
函数描述 NextWeapon
键位 Tab / MouseWheelUp / MouseWheelDown

不论滚轮方向, 切换到下一个武器



搭建框架

切换逻辑在STUWeaponComponent中


添加空的回调函数

  • ShootThemUp: Components/STUWeaponComponent.h

    public
    1void NextWeapon();
  • ShootThemUp: Components/STUWeaponComponent.cpp
    1void USTUWeaponComponent::NextWeapon()
    2{ }

绑定回调函数和函数描述

ShootThemUp: Player/STUBaseCharacter.cpp

1// SetupPlayerInputComponent
2
3PlayerInputComponent->BindAction("NextWeapon", IE_Pressed, WeaponComponent, &USTUWeaponComponent::NextWeapon);

调整当前逻辑


修改Socket名

现在附加武器的Socket有两个, 原先的Socket为装载武器Socket

  • ShootThemUp: Components/STUWeaponComponent.h

    WeaponAttachPointName -> WeaponEquipSocketName
  • ShootThemUp: Components/STUWeaponComponent.cpp
    1// SpawnWeapon
    2WeaponAttachPointName -> WeaponEquipSocketName          
    

添加接口: 将武器挂载到指定Socket

切换武器时使用


定义

  • ShootThemUp: Components/STUWeaponComponent.h

    private
    1void AttachWeaponToSocket(ASTUBaseWeapon *Weapon, USceneComponent *SceneComponent, const FName &SocketName);
  • ShootThemUp: Components/STUWeaponComponent.cpp
    1void USTUWeaponComponent::AttachWeaponToSocket(ASTUBaseWeapon *Weapon, USceneComponent *SceneComponent, const FName &SocketName)
    2{
    3    if (!Weapon || !SceneComponent) return;
    4    FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, false);
    5    Weapon->AttachToComponent(SceneComponent, AttachmentRules, SocketName);               
    6}

调用

ShootThemUp: Components/STUWeaponComponent.cpp

1// SpawnWeapon
2AttachWeaponToSocket(CurrentWeapon, Character->GetMesh(), WeaponEquipSocketName);           

武器数组

武器组件管理武器数组


将武器类属性改为武器类属性数组

protected

ShootThemUp: Components/STUWeaponComponent.h

1UPROPERTY(EditDefaultsOnly)
2TArray<TSubclassOf<ASTUBaseWeapon>> WeaponClasses;

添加武器数组

private

ShootThemUp: Components/STUWeaponComponent.h

1UPROPERTY()
2TArray<ASTUBaseWeapon*> Weapons;

添加数据成员: 保存挂载点名称

protected

ShootThemUp: Components/STUWeaponComponent.h

1UPROPERTY(EditDefaultsOnly)
2FName WeaponArmorySocketName = "ArmorySocket";

动态生成武器时, 生成Rifle和Launcher, 先全部挂载到背上


SpawnWeapons定义

  • ShootThemUp: Components/STUWeaponComponent.h

    SpawnWeapon -> SpawnWeapons
  • ShootThemUp: Components/STUWeaponComponent.cpp
     1void USTUWeaponComponent::SpawnWeapons()
     2{
     3    if (!GetWorld()) return;
     4
     5    ACharacter *Character = Cast<ACharacter>(GetOwner());
     6    if (!Character) return;
     7
     8    for (auto WeaponClass : WeaponClasses)
     9    {
    10        auto Weapon = GetWorld()->SpawnActor<ASTUBaseWeapon>(WeaponClass);
    11        if (!Weapon) return;
    12        Weapons.Add(Weapon);
    13        Weapon->SetOwner(Character);
    14        AttachWeaponToSocket(Weapon, Character->GetMesh(), WeaponArmorySocketName);
    15    }
    16}

在BeginPlay中调用

ShootThemUp: Components/STUWeaponComponent.cpp


装载武器


添加数据成员: 保存当前武器标号

private

和CurrentWeapon相对应

ShootThemUp: Components/STUWeaponComponent.h

1int32 CurrentWeaponIndex = 0;

EquipWeapon定义

  • ShootThemUp: Components/STUWeaponComponent.h

    private
    1void EquipWeapon(int32 WeaponIndex);
  • ShootThemUp: Components/STUWeaponComponent.cpp

    设置CurrentWeapon指针, 将武器挂载到WeaponEquipSocketName
     1void USTUWeaponComponent::EquipWeapon(int32 WeaponIndex)
     2{
     3    ACharacter *Character = Cast<ACharacter>(GetOwner());
     4    if (!Character) return;
     5
     6    if (CurrentWeapon)
     7    {
     8        AttachWeaponToSocket(CurrentWeapon, Character->GetMesh(), WeaponArmorySocketName);               
     9    }
    10
    11    CurrentWeapon = Weapons[WeaponIndex];
    12    AttachWeaponToSocket(CurrentWeapon, Character->GetMesh(), WeaponEquipSocketName);
    13}

动态生成武器后, 需装载武器

ShootThemUp: Components/STUWeaponComponent.cpp

1// BeginPlay
2
3CurrentWeaponIndex = 0;
4EquipWeapon(CurrentWeaponIndex);

实现切换武器回调函数

ShootThemUp: Components/STUWeaponComponent.cpp

1void USTUWeaponComponent::NextWeapon()
2{
3    CurrentWeaponIndex = (1 + CurrentWeaponIndex) % Weapons.Num();
4    EquipWeapon(CurrentWeaponIndex);
5}

查看

  1. 设置游戏角色武器类属性数组


  2. 武器切换和使用正常



游戏角色死亡时, 销毁武器

  • 对游戏角色调用Destroy, 会对游戏角色调用EndPlay
  • 先调用组件的EndPlay, 再对游戏角色调用EndPlay
  • 覆写EndPlay, 销毁武器Actor

ShootThemUp: Components/STUWeaponComponent.h

protected

1virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

ShootThemUp: Components/STUWeaponComponent.cpp

 1void USTUWeaponComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
 2{
 3    CurrentWeapon = nullptr;
 4    for (auto Weapon : Weapons)
 5    {
 6        // 卸载Actor的网格体, 与AttachToComponent相反
 7        Weapon->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform); // 此处是静态结构体, 不是枚举成员
 8
 9        // 销毁Actor
10        Weapon->Destroy();
11    }
12    Weapons.Empty(); // 清空数组
13
14    // 也可以关闭武器的物理碰撞, 将武器保留在关卡, 几秒钟后销毁武器
15    Super::EndPlay(EndPlayReason);
16}

处理Rifle开火定时器


切换武器时, 关闭定时器

ShootThemUp: Components/STUWeaponComponent.cpp

1// EquipWeapon
2
3 if (CurrentWeapon)
4 {
5     // 其他处理
6     CurrentWeapon->FireStop();
7 }

游戏角色死亡后, 关闭定时器

ShootThemUp: Player/STUBaseCharacter.cpp

1// OnDeath
2WeaponComponent->FireStop();

切换武器



概览

  • 为HeroTPP添加Socket, 在背后挂载未使用武器
  • 绑定键位, 按下Tab或滚动鼠标时切换武器
  • 游戏角色死亡后, 销毁武器
  • 处理Rifle开火定时器

为骨骼网格体添加Socket

虚幻编辑器

  1. 打开Content/ExternalContent/Animation/Characters/HeroTPP/HeroTPP

  2. 选择骨骼b_Spine1, 添加Socket

  3. 在细节面板设置Socket

    -
    Socket Name ArmorySocket
    Relative Location (-9, -26, -23)
    Relative Rotation (-10, 70, -29)


  4. 为Socket添加预览资产, 选择Launcher

    武器未使用时背在背上



绑定切换武器键位

项目设置 > Engine > Input > Action Mappings

-
函数描述 NextWeapon
键位 Tab / MouseWheelUp / MouseWheelDown

不论滚轮方向, 切换到下一个武器



搭建框架

切换逻辑在STUWeaponComponent中


添加空的回调函数

  • ShootThemUp: Components/STUWeaponComponent.h

    public
    1void NextWeapon();
  • ShootThemUp: Components/STUWeaponComponent.cpp
    1void USTUWeaponComponent::NextWeapon()
    2{ }

绑定回调函数和函数描述

ShootThemUp: Player/STUBaseCharacter.cpp

1// SetupPlayerInputComponent
2
3PlayerInputComponent->BindAction("NextWeapon", IE_Pressed, WeaponComponent, &USTUWeaponComponent::NextWeapon);

调整当前逻辑


修改Socket名

现在附加武器的Socket有两个, 原先的Socket为装载武器Socket

  • ShootThemUp: Components/STUWeaponComponent.h

    WeaponAttachPointName -> WeaponEquipSocketName
  • ShootThemUp: Components/STUWeaponComponent.cpp
    1// SpawnWeapon
    2WeaponAttachPointName -> WeaponEquipSocketName          
    

添加接口: 将武器挂载到指定Socket

切换武器时使用


定义

  • ShootThemUp: Components/STUWeaponComponent.h

    private
    1void AttachWeaponToSocket(ASTUBaseWeapon *Weapon, USceneComponent *SceneComponent, const FName &SocketName);
  • ShootThemUp: Components/STUWeaponComponent.cpp
    1void USTUWeaponComponent::AttachWeaponToSocket(ASTUBaseWeapon *Weapon, USceneComponent *SceneComponent, const FName &SocketName)
    2{
    3    if (!Weapon || !SceneComponent) return;
    4    FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, false);
    5    Weapon->AttachToComponent(SceneComponent, AttachmentRules, SocketName);               
    6}

调用

ShootThemUp: Components/STUWeaponComponent.cpp

1// SpawnWeapon
2AttachWeaponToSocket(CurrentWeapon, Character->GetMesh(), WeaponEquipSocketName);           

武器数组

武器组件管理武器数组


将武器类属性改为武器类属性数组

protected

ShootThemUp: Components/STUWeaponComponent.h

1UPROPERTY(EditDefaultsOnly)
2TArray<TSubclassOf<ASTUBaseWeapon>> WeaponClasses;

添加武器数组

private

ShootThemUp: Components/STUWeaponComponent.h

1UPROPERTY()
2TArray<ASTUBaseWeapon*> Weapons;

添加数据成员: 保存挂载点名称

protected

ShootThemUp: Components/STUWeaponComponent.h

1UPROPERTY(EditDefaultsOnly)
2FName WeaponArmorySocketName = "ArmorySocket";

动态生成武器时, 生成Rifle和Launcher, 先全部挂载到背上


SpawnWeapons定义

  • ShootThemUp: Components/STUWeaponComponent.h

    SpawnWeapon -> SpawnWeapons
  • ShootThemUp: Components/STUWeaponComponent.cpp
     1void USTUWeaponComponent::SpawnWeapons()
     2{
     3    if (!GetWorld()) return;
     4
     5    ACharacter *Character = Cast<ACharacter>(GetOwner());
     6    if (!Character) return;
     7
     8    for (auto WeaponClass : WeaponClasses)
     9    {
    10        auto Weapon = GetWorld()->SpawnActor<ASTUBaseWeapon>(WeaponClass);
    11        if (!Weapon) return;
    12        Weapons.Add(Weapon);
    13        Weapon->SetOwner(Character);
    14        AttachWeaponToSocket(Weapon, Character->GetMesh(), WeaponArmorySocketName);
    15    }
    16}

在BeginPlay中调用

ShootThemUp: Components/STUWeaponComponent.cpp


装载武器


添加数据成员: 保存当前武器标号

private

和CurrentWeapon相对应

ShootThemUp: Components/STUWeaponComponent.h

1int32 CurrentWeaponIndex = 0;

EquipWeapon定义

  • ShootThemUp: Components/STUWeaponComponent.h

    private
    1void EquipWeapon(int32 WeaponIndex);
  • ShootThemUp: Components/STUWeaponComponent.cpp

    设置CurrentWeapon指针, 将武器挂载到WeaponEquipSocketName
     1void USTUWeaponComponent::EquipWeapon(int32 WeaponIndex)
     2{
     3    ACharacter *Character = Cast<ACharacter>(GetOwner());
     4    if (!Character) return;
     5
     6    if (CurrentWeapon)
     7    {
     8        AttachWeaponToSocket(CurrentWeapon, Character->GetMesh(), WeaponArmorySocketName);               
     9    }
    10
    11    CurrentWeapon = Weapons[WeaponIndex];
    12    AttachWeaponToSocket(CurrentWeapon, Character->GetMesh(), WeaponEquipSocketName);
    13}

动态生成武器后, 需装载武器

ShootThemUp: Components/STUWeaponComponent.cpp

1// BeginPlay
2
3CurrentWeaponIndex = 0;
4EquipWeapon(CurrentWeaponIndex);

实现切换武器回调函数

ShootThemUp: Components/STUWeaponComponent.cpp

1void USTUWeaponComponent::NextWeapon()
2{
3    CurrentWeaponIndex = (1 + CurrentWeaponIndex) % Weapons.Num();
4    EquipWeapon(CurrentWeaponIndex);
5}

查看

  1. 设置游戏角色武器类属性数组


  2. 武器切换和使用正常



游戏角色死亡时, 销毁武器

  • 对游戏角色调用Destroy, 会对游戏角色调用EndPlay
  • 先调用组件的EndPlay, 再对游戏角色调用EndPlay
  • 覆写EndPlay, 销毁武器Actor

ShootThemUp: Components/STUWeaponComponent.h

protected

1virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

ShootThemUp: Components/STUWeaponComponent.cpp

 1void USTUWeaponComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
 2{
 3    CurrentWeapon = nullptr;
 4    for (auto Weapon : Weapons)
 5    {
 6        // 卸载Actor的网格体, 与AttachToComponent相反
 7        Weapon->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform); // 此处是静态结构体, 不是枚举成员
 8
 9        // 销毁Actor
10        Weapon->Destroy();
11    }
12    Weapons.Empty(); // 清空数组
13
14    // 也可以关闭武器的物理碰撞, 将武器保留在关卡, 几秒钟后销毁武器
15    Super::EndPlay(EndPlayReason);
16}

处理Rifle开火定时器


切换武器时, 关闭定时器

ShootThemUp: Components/STUWeaponComponent.cpp

1// EquipWeapon
2
3 if (CurrentWeapon)
4 {
5     // 其他处理
6     CurrentWeapon->FireStop();
7 }

游戏角色死亡后, 关闭定时器

ShootThemUp: Player/STUBaseCharacter.cpp

1// OnDeath
2WeaponComponent->FireStop();