编程基础:表驱动
能帮到你的话,就给个赞吧 😘
文章目录
- 查找:从元素集中查找到数据
- 顺序查找:O(n):遍历查找到符合条件的元素地址
- 定址查找:O(1):给定地址查找到元素
- 关键:以条件作为地址
- rate = rateTable[age][health]:给定age和health两个地址
- 表驱动
- 定义条件结构体:使用枚举量化条件
- 定义条件检测方法
- 定义动画表:以条件为索引
查找:从元素集中查找到数据
顺序查找:O(n):遍历查找到符合条件的元素地址
定址查找:O(1):给定地址查找到元素
关键:以条件作为地址
rate = rateTable[age][health]:给定age和health两个地址
表驱动
定义条件结构体:使用枚举量化条件
#pragma once#include "CoreMinimal.h"
#include "UObject/Struct.h"
#include "HorseTypes.generated.h"/** 下马方向枚举 - 定义角色下马时的左右方向选择 */
UENUM(BlueprintType)
enum class EDismountDirection : uint8
{Left, // 左侧下马 - 角色从马匹左侧下马Right, // 右侧下马 - 角色从马匹右侧下马Automatic // 自动选择 - 系统根据障碍物等条件自动选择最佳方向
};/** 地面类型枚举 - 描述角色下马时所处的地形类型 */
UENUM(BlueprintType)
enum class EGroundType : uint8
{Flat, // 平地 - 坡度较小的平坦地面Slope, // 斜坡 - 有一定坡度的倾斜地面Stairs, // 台阶 - 类似楼梯的阶梯状地形Uneven // 不平坦地形 - 崎岖不平的复杂地形
};/** 角色移动状态枚举 - 表示马匹在下车时的运动状态 */
UENUM(BlueprintType)
enum class EMovementState : uint8
{Standing, // 静止 - 马匹处于完全静止状态Walking, // 步行 - 马匹以较慢速度行走Running, // 奔跑 - 马匹以中等速度奔跑Galloping // 飞驰 - 马匹以最高速度疾驰
};/** 下马情境枚举 - 描述下马时的场景上下文 */
UENUM(BlueprintType)
enum class EDismountContext : uint8
{Normal, // 正常下马 - 非战斗状态下的常规下马Combat, // 战斗状态 - 战斗中紧急下马NearObject, // 靠近物体 - 靠近墙壁、树木等物体时下马Mounted // 从马背上 - 直接从骑行状态下马
};/** * 下马条件结构体* 整合所有影响下马动画选择的关键条件参数* 作为动画查询系统的核心数据结构,实现条件到动画的精准映射*/
USTRUCT(BlueprintType)
struct FHorseDismountConditions
{GENERATED_BODY() // 虚幻引擎反射系统宏,生成必要的元数据和代码/** 下马方向 - 决定角色从左侧、右侧还是自动选择方向下马 */UPROPERTY(BlueprintReadWrite, Category = "Conditions")EDismountDirection Direction;/** 地面类型 - 描述当前地形属于平地、斜坡、台阶还是不平坦地形 */UPROPERTY(BlueprintReadWrite, Category = "Conditions")EGroundType Ground;/** 移动状态 - 记录马匹在下马前的运动状态(静止、步行、奔跑等) */UPROPERTY(BlueprintReadWrite, Category = "Conditions")EMovementState Movement;/** 下马情境 - 标记下马时的场景上下文(正常、战斗、靠近物体等) */UPROPERTY(BlueprintReadWrite, Category = "Conditions")EDismountContext Context;/** 是否有障碍物 - 标记下马方向是否存在障碍物(如墙壁、树木) */UPROPERTY(BlueprintReadWrite, Category = "Conditions")bool bHasObstacle;/** 坡度角度 - 量化地面倾斜程度(单位:度),用于更精细的动画选择 */UPROPERTY(BlueprintReadWrite, Category = "Conditions")float SlopeAngle;/** * 构造函数 - 初始化所有条件参数为默认值* 确保结构体在创建时处于合理的初始状态*/FHorseDismountConditions(): Direction(EDismountDirection::Automatic) // 默认自动选择下马方向, Ground(EGroundType::Flat) // 默认平地地形, Movement(EMovementState::Standing) // 默认静止状态, Context(EDismountContext::Normal) // 默认正常下马情境, bHasObstacle(false) // 默认无障碍物, SlopeAngle(0.0f) // 默认坡度为0度(水平){}
};/** * 下马动画映射结构体* 建立"下马条件组合"与"对应动画资源"之间的映射关系* 用于数据驱动的动画选择系统*/
USTRUCT(BlueprintType)
struct FAnimationMapping
{GENERATED_BODY()/** 触发该动画的条件组合 - 当检测到的条件与此匹配时使用该动画 */UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")FHorseDismountConditions Conditions;/** 动画蒙太奇资源 - 包含具体的下马动画片段 */UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")UAnimationMontage* AnimationMontage;/** 动画片段名称 - 指定蒙太奇中需要播放的具体片段 */UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")FName AnimationSection;/** 动画过渡时间 - 从当前动画切换到该下马动画的平滑过渡时间(单位:秒) */UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation")float TransitionTime;
};
定义条件检测方法
FHorseDismountConditions UHorseComponent::DetectDismountConditions()
{FHorseDismountConditions Conditions;// 检测地面类型Conditions.Ground = DetectGroundType();// 检测移动状态Conditions.Movement = DetectMovementState();// 检测下马方向Conditions.Direction = DetermineDismountDirection();// 检测是否有障碍物FVector StartLocation = OwningCharacter->GetActorLocation();FVector EndLocation = StartLocation + OwningCharacter->GetActorForwardVector() * ObstacleCheckDistance;FHitResult HitResult;Conditions.bHasObstacle = UKismetSystemLibrary::LineTraceSingle(GetWorld(),StartLocation,EndLocation,ETraceTypeQuery::TraceTypeQuery1,false,TArray<AActor*>(),EDrawDebugTrace::None,HitResult,true);// 检测下马场景(简化版)Conditions.Context = Conditions.bHasObstacle ? EDismountContext::NearObject : EDismountContext::Normal;return Conditions;
}EGroundType UHorseComponent::DetectGroundType()
{if (!OwningCharacter) return EGroundType::Flat;FVector StartLocation = OwningCharacter->GetActorLocation();FVector EndLocation = StartLocation + FVector(0, 0, -GroundCheckDistance);FHitResult HitResult;bool bHit = UKismetSystemLibrary::LineTraceSingle(GetWorld(),StartLocation,EndLocation,ETraceTypeQuery::TraceTypeQuery1,false,TArray<AActor*>(),EDrawDebugTrace::None,HitResult,true);if (!bHit) return EGroundType::Flat;// 计算坡度float Angle = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(HitResult.Normal, FVector::UpVector)));((FHorseDismountConditions*)nullptr)->SlopeAngle = Angle;if (Angle < 5.0f)return EGroundType::Flat;else if (Angle < 25.0f)return EGroundType::Slope;else if (Angle < 45.0f)return EGroundType::Stairs;elsereturn EGroundType::Uneven;
}EMovementState UHorseComponent::DetectMovementState()
{if (!OwningCharacter) return EMovementState::Standing;float Speed = OwningCharacter->GetVelocity().Size();if (Speed < 50.0f)return EMovementState::Standing;else if (Speed < 200.0f)return EMovementState::Walking;else if (Speed < 400.0f)return EMovementState::Running;elsereturn EMovementState::Galloping;
}EDismountDirection UHorseComponent::DetermineDismountDirection(const FHorseDismountConditions& Conditions)
{// 安全检查:确认角色(OwningCharacter)和马匹(HorseActor)的有效性,避免空指针错误if (!OwningCharacter || !HorseActor) return EDismountDirection::Left;// 检测点计算:计算左右两侧检测点(距离角色100单位的左右位置)FVector RightSide = OwningCharacter->GetActorLocation() + OwningCharacter->GetActorRightVector() * 100.0f;FVector LeftSide = OwningCharacter->GetActorLocation() - OwningCharacter->GetActorRightVector() * 100.0f;//障碍物检测:从左右检测点分别向下发射 150 单位的射线(FVector(0,0,-150)),通过射线是否命中物体判断两侧是否有障碍物(如墙壁、岩石、地形突起等)。FHitResult RightHit, LeftHit; // 声明碰撞结果变量// 检测右侧是否有障碍物(向下发射150单位射线,检测地面或物体)bool bRightBlocked = UKismetSystemLibrary::LineTraceSingle(GetWorld(), RightSide, RightSide + FVector(0, 0, -150), ETraceTypeQuery::TraceTypeQuery1, false, TArray<AActor*>(), EDrawDebugTrace::None, RightHit, true);// 检测左侧是否有障碍物(同上逻辑)bool bLeftBlocked = UKismetSystemLibrary::LineTraceSingle(GetWorld(), LeftSide, LeftSide + FVector(0, 0, -150), ETraceTypeQuery::TraceTypeQuery1, false, TArray<AActor*>(), EDrawDebugTrace::None, LeftHit, true);// 方向决策:优先选择无障碍物的一侧if (bRightBlocked && !bLeftBlocked)return EDismountDirection::Left; // 右侧有障碍,选左侧if (bLeftBlocked && !bRightBlocked)return EDismountDirection::Right; // 左侧有障碍,选右侧return EDismountDirection::Left; // 两侧都无障碍(或都有障碍)时,默认选择左侧(符合多数游戏操作习惯)
}
定义动画表:以条件为索引
// 为FHorseDismountConditions实现哈希函数,使其可作为TMap的键
template<>
struct THash<FHorseDismountConditions>
{FORCEINLINE static uint32 GetHash(const FHorseDismountConditions& Key){uint32 Hash = 0;Hash = HashCombine(Hash, GetTypeHash(Key.DismountDir));Hash = HashCombine(Hash, GetTypeHash(Key.Ground));Hash = HashCombine(Hash, GetTypeHash(Key.Movement));Hash = HashCombine(Hash, GetTypeHash(FMath::RoundToInt(Key.SlopeAngle)));Hash = HashCombine(Hash, GetTypeHash(Key.bIsInCombat));return Hash;}
};// 实现相等运算符,用于TMap的键比较
FORCEINLINE bool operator==(const FHorseDismountConditions& A, const FHorseDismountConditions& B)
{return A.DismountDir == B.DismountDir &&A.Ground == B.Ground &&A.Movement == B.Movement &&A.bIsInCombat == B.bIsInCombat &&FMath::IsNearlyEqual(A.SlopeAngle, B.SlopeAngle, 1.0f);
}// 直接定义TMap类型别名,方便使用
using FHorseDismountAnimationMap = TMap<FHorseDismountConditions, UAnimationMontage*>;//动画表
FHorseDismountAnimationMap dismountAnimationTable;FHorseDismountConditions Conditions;UAnimationMontage*FoundMontage = dismountAnimationTable.Find(Conditions);