目录

角色类:SCharacter

设置摄像机

弹簧臂和摄像机设置

设置人物移动和动作绑定

移动与朝向:MoveForward/MoveRight、Turn/LoopUp

为人物绑定设置子弹类:SpawnProjectile适配器类

设置攻击动画以及攻击特效

普通攻击

黑洞攻击

闪现攻击

❗为角色添加属性组件:USAttributeComponent

为角色添加交互组件接口:USInteractionComponent* InteractionComp;

子弹类:SProejctileBase

胶囊碰撞体:USphereComponent* SphereComp;

子弹移动组件:UProjectileMovementComponent* MoveComp;

粒子系统组件(平A效果):UParticleSystemComponent* EffectComp;

粒子系统组件(爆炸效果):UParticleSystem* ImpactVFX;

子弹爆炸音效组件:USoundCue* ImpactSound;

?子弹播放声音组件:UAudioComponent* AudioComp;

子弹击中画面震动效果:TSubclassOf ImpactShake;

⭐碰撞事件&爆炸(虚函数):void Explode();(BlueprintNativeEvent)

普通攻击:SMagicProjectile

设置并重写OnActorOverlap逻辑:

设置子弹伤害:DamageAmount

❗闪现:SDashProjectile

重写BeginPlay:设置子弹存活时间

重写爆炸逻辑Explode_Implementation

实现闪现传送效果:TeleportInstigator

设置定时器:FTimerHandle

黑洞攻击(蓝图):Proj_BlackHole

径向力Force Strength

可影响物体Object Types to Affect

吸进黑洞后要杀掉物体:

❗接口类:USGameplayInterface

USInteractionComponent

世界中的物体

爆炸油桶:ASExplosiveBarrel

设置StaticMesh,径向力

初始化Actor组件(添加碰撞事件)

宝箱(实现交互接口):ASItemChest

血包(实现交互接口):ASPowerupActor

血包有很多种,因此接口实现逻辑由子类实现:

吃掉后隐藏血包(计时器):

血包子类:ASPowerup_HealthPotion

UI:Widget

十字准星

创建Widget并设置:

游戏开始时添加到屏幕上

人物血条、Credits、游戏进行时间:Main_HUD

血条

Credits:

游戏进行时间:

AI

UE中的AI系统

行为树:

添加导航网格体:NavMeshBoundsVolume​编辑

游戏AI角色:SAICharacter

游戏AI控制器:SAIController

设置行为树

为SAICharacter设置控制器

游戏AI逻辑-范围内攻击:USBTService_CheckAttackRange(UBTService)

范围查询逻辑

在行为树添加此Service

游戏AI范围内攻击任务结点:USBTTask_RangedAttack

AI朝向玩家:Set default focus

环境查询系统:

创建EQS

在玩家周围半径内设置EQS生成移动目的地

AI死亡


角色类:SCharacter

设置摄像机

弹簧臂和摄像机设置

 UPROPERTY(VisibleAnywhere)USpringArmComponent* SpringArmComp;UPROPERTY(VisibleAnywhere)UCameraComponent* CameraComp;

在构造函数中创建这两个类,并设置依赖关系:

弹簧臂附着在RootComponent,摄像机附着在弹簧臂上

 SpringArmComp = CreateDefaultSubobject<USpringArmComponent>("SpringArmComp");SpringArmComp->bUsePawnControlRotation = true;SpringArmComp->SetupAttachment(RootComponent);CameraComp = CreateDefaultSubobject<UCameraComponent>("CameraComp");CameraComp->SetupAttachment(SpringArmComp);GetCharacterMovement()->bOrientRotationToMovement = true;bUseControllerRotationYaw = false;

注意 bUseControllerRotationYaw =false;其效果为:

bUseControllerRotationYaw=true;

bUseControllerRotationYaw=false;

应该设置为false,

同时注意GetCharacterMovement()->bOrientRotationToMovement = true;

GetCharacterMovement()->bOrientRotationToMovement = false;如下

GetCharacterMovement()->bOrientRotationToMovement = true;如下

类似黑暗之魂这种第三人称的移动朝向方式。

设置人物移动和动作绑定

void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{Super::SetupPlayerInputComponent(PlayerInputComponent);PlayerInputComponent->BindAxis("MoveForward", this, &ASCharacter::MoveForward);PlayerInputComponent->BindAxis("MoveRight", this, &ASCharacter::MoveRight);PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);PlayerInputComponent->BindAction("PrimaryAttack", IE_Pressed, this, &ASCharacter::PrimaryAttack);// Used generic name 'SecondaryAttack' for bindingPlayerInputComponent->BindAction("SecondaryAttack", IE_Pressed, this, &ASCharacter::BlackHoleAttack);PlayerInputComponent->BindAction("Dash", IE_Pressed, this, &ASCharacter::Dash);PlayerInputComponent->BindAction("PrimaryInteract", IE_Pressed, this, &ASCharacter::PrimaryInteract);PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
}

移动与朝向:MoveForward/MoveRight、Turn/LoopUp

void ASCharacter::MoveForward(float Value)
{FRotator ControlRot = GetControlRotation();ControlRot.Pitch = 0.0f;ControlRot.Roll = 0.0f;AddMovementInput(ControlRot.Vector(), Value);
}void ASCharacter::MoveRight(float Value)
{FRotator ControlRot = GetControlRotation();ControlRot.Pitch = 0.0f;ControlRot.Roll = 0.0f;// X = Forward (Red)// Y = Right (Green)// Z = Up (Blue)FVector RightVector = FRotationMatrix(ControlRot).GetScaledAxis(EAxis::Y);AddMovementInput(RightVector, Value);
}

 其中Turn和LookUp绑定的函数是由APawn中已经写好的内容。

&APawn::AddControllerYawInput
&APawn::AddControllerPitchInput

这里附上Yaw、Roll、Pitch的内容,结合代码理解:

UE是左手坐标系:拇指X,食指Y,中指Z(中指向上摆放)
X:Roll翻滚角
Y:Pitch俯仰角
Z:Yaw偏航角

动图:

1.俯仰:(Pitch)

2.翻滚:(Roll)

3.偏航:(Yaw)

所以,MoveForward和MoveRight只与Yaw有关,控制偏航角度;Turn也和Yaw有关,控制偏航角度;而LoopUp与Pitch有关,控制上下移动视角。

为人物绑定设置子弹类:SpawnProjectile适配器类

  • 生成位置:手部Socket(HandLocation)
  • Actor生成:设置碰撞,instigator为角色自身
  • 碰撞预设:
  • 子弹轨迹:起点为手部,终点为十字准星方向
void ASCharacter::SpawnProjectile(TSubclassOf<AActor> ClassToSpawn)
{if (ensureAlways(ClassToSpawn)){FVector HandLocation = GetMesh()->GetSocketLocation(HandSocketName);FActorSpawnParameters SpawnParams;SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;SpawnParams.Instigator = this;FCollisionShape Shape;Shape.SetSphere(20.0f);// Ignore PlayerFCollisionQueryParams Params;Params.AddIgnoredActor(this);FCollisionObjectQueryParams ObjParams;ObjParams.AddObjectTypesToQuery(ECC_WorldDynamic);ObjParams.AddObjectTypesToQuery(ECC_WorldStatic);ObjParams.AddObjectTypesToQuery(ECC_Pawn);FVector TraceStart = CameraComp->GetComponentLocation();// endpoint far into the look-at distance (not too far, still adjust somewhat towards crosshair on a miss)FVector TraceEnd = CameraComp->GetComponentLocation() + (GetControlRotation().Vector() * 5000);FHitResult Hit;// returns true if we got to a blocking hitif (GetWorld()->SweepSingleByObjectType(Hit, TraceStart, TraceEnd, FQuat::Identity, ObjParams, Shape, Params)){// Overwrite trace end with impact point in worldTraceEnd = Hit.ImpactPoint;}// find new direction/rotation from Hand pointing to impact point in world.FRotator ProjRotation = FRotationMatrix::MakeFromX(TraceEnd - HandLocation).Rotator();FTransform SpawnTM = FTransform(ProjRotation, HandLocation);GetWorld()->SpawnActor<AActor>(ClassToSpawn, SpawnTM, SpawnParams);}
}

设置攻击动画以及攻击特效

void ASCharacter::StartAttackEffects()
{PlayAnimMontage(AttackAnim);UGameplayStatics::SpawnEmitterAttached(CastingEffect, GetMesh(), HandSocketName, FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::SnapToTarget);
}

普通攻击

void ASCharacter::PrimaryAttack()
{StartAttackEffects();GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack, this, &ASCharacter::PrimaryAttack_TimeElapsed, AttackAnimDelay);
}void ASCharacter::PrimaryAttack_TimeElapsed()
{SpawnProjectile(ProjectileClass);
}

黑洞攻击

void ASCharacter::BlackHoleAttack()
{StartAttackEffects();GetWorldTimerManager().SetTimer(TimerHandle_BlackholeAttack, this, &ASCharacter::BlackholeAttack_TimeElapsed, AttackAnimDelay);
}void ASCharacter::BlackholeAttack_TimeElapsed()
{SpawnProjectile(BlackHoleProjectileClass);
}

闪现攻击

void ASCharacter::Dash()
{StartAttackEffects();GetWorldTimerManager().SetTimer(TimerHandle_Dash, this, &ASCharacter::Dash_TimeElapsed, AttackAnimDelay);
}void ASCharacter::Dash_TimeElapsed()
{SpawnProjectile(DashProjectileClass);
}

❗为角色添加属性组件:USAttributeComponent

为角色添加交互组件接口:USInteractionComponent* InteractionComp;

子弹类:SProejctileBase

ASProjectileBase::ASProjectileBase()
{SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");SphereComp->SetCollisionProfileName("Projectile");SphereComp->OnComponentHit.AddDynamic(this, &ASProjectileBase::OnActorHit);RootComponent = SphereComp;EffectComp = CreateDefaultSubobject<UParticleSystemComponent>("EffectComp");EffectComp->SetupAttachment(RootComponent);AudioComp = CreateDefaultSubobject<UAudioComponent>("AudioComp");AudioComp->SetupAttachment(RootComponent);MoveComp = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMoveComp");MoveComp->bRotationFollowsVelocity = true;MoveComp->bInitialVelocityInLocalSpace = true;MoveComp->ProjectileGravityScale = 0.0f;MoveComp->InitialSpeed = 8000;ImpactShakeInnerRadius = 0.0f;ImpactShakeOuterRadius = 1500.0f;}

胶囊碰撞体:USphereComponent* SphereComp;

  • 设置名称
  • 设置碰撞预设
  • 设置碰撞触发事件
  • 将RootComponent设为碰撞体

子弹移动组件:UProjectileMovementComponent* MoveComp;

  • 初始化相关移动属性,比如速度

粒子系统组件(平A效果):UParticleSystemComponent* EffectComp;

在cpp中构造函数进行初始化,只需要设置名称与挂载RootComponent。

因为子弹效果必然要跟随着子弹,故需要附着在RootComponent上。

粒子系统组件(爆炸效果):UParticleSystem* ImpactVFX;

在编辑中设置。

因为爆炸效果只需要在碰撞点触发,故不需要附着在RootComponent上。

子弹爆炸音效组件:USoundCue* ImpactSound;

?子弹播放声音组件:UAudioComponent* AudioComp;

声音较小,在黑洞攻击时有体现

子弹击中画面震动效果:TSubclassOf<UCameraShakeBase> ImpactShake;

 UPROPERTY(EditDefaultsOnly, Category = "Effects|Shake")TSubclassOf<UCameraShakeBase> ImpactShake;UPROPERTY(EditDefaultsOnly, Category = "Effects|Shake")float ImpactShakeInnerRadius;UPROPERTY(EditDefaultsOnly, Category = "Effects|Shake")float ImpactShakeOuterRadius;

⭐碰撞事件&爆炸(虚函数):void Explode();(BlueprintNativeEvent)

 // BlueprintNativeEvent = C++ base implementation, can be expanded in Blueprints// BlueprintCallable to allow child classes to trigger explosions// Not required for assignment, useful for expanding in Blueprint later onUFUNCTION(BlueprintCallable, BlueprintNativeEvent)void Explode();

在cpp中实现逻辑:

  • 在爆炸处播放粒子特效ImpactVFX
  • 在爆炸处播放爆炸音效ImpactSound
  • 在爆炸处触发画面震动效果ImpactShake
void ASProjectileBase::OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{Explode();
}
// _Implementation from it being marked as BlueprintNativeEvent
void ASProjectileBase::Explode_Implementation()
{// Check to make sure we aren't already being 'destroyed'// Adding ensure to see if we encounter this situation at allif (ensure(!IsPendingKill())){UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());UGameplayStatics::PlaySoundAtLocation(this, ImpactSound, GetActorLocation());UGameplayStatics::PlayWorldCameraShake(this, ImpactShake, GetActorLocation(), ImpactShakeInnerRadius, ImpactShakeOuterRadius);Destroy();}
}

普通攻击:SMagicProjectile

依赖于USAtrributeComponent,人物属性组件,因为要影响人物的血量

设置并重写OnActorOverlap逻辑:

构造函数初始化并设置碰撞体触发事件:

ASMagicProjectile::ASMagicProjectile()
{SphereComp->SetSphereRadius(20.0f);SphereComp->OnComponentBeginOverlap.AddDynamic(this, &ASMagicProjectile::OnActorOverlap);DamageAmount = 20.0f;
}

重写事件逻辑:

  • 更改属性组件AttributeComp(HP)数值
  • 不能伤害自身:OtherActor != GetInstigator()
void ASMagicProjectile::OnActorOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor && OtherActor != GetInstigator()){USAttributeComponent* AttributeComp = Cast<USAttributeComponent>(OtherActor->GetComponentByClass(USAttributeComponent::StaticClass()));if (AttributeComp){// minus in front of DamageAmount to apply the change as damage, not healingAttributeComp->ApplyHealthChange(GetInstigator(), -DamageAmount);// Only explode when we hit something validExplode();}}
}

设置子弹伤害:DamageAmount

❗闪现:SDashProjectile

重写BeginPlay:设置子弹存活时间

void ASDashProjectile::BeginPlay()
{Super::BeginPlay();GetWorldTimerManager().SetTimer(TimerHandle_DelayedDetonate, this, &ASDashProjectile::Explode, DetonateDelay);
}

从Begin之后,DetonateDelay秒之后,调用一次ASDashProjectile。

虚幻引擎中的Gameplay定时器 | 虚幻引擎5.0文档 (unrealengine.com)

重写爆炸逻辑Explode_Implementation

  • 清除计时器
  • 爆炸依然播放粒子效果
  • 爆炸后取消粒子效果
  • 爆炸后子弹立即停止移动
  • 爆炸后子弹立即取消碰撞
  • 重新设置定时器,计时触发传送
void ASDashProjectile::Explode_Implementation()
{// Clear timer if the Explode was already called through another source like OnActorHitGetWorldTimerManager().ClearTimer(TimerHandle_DelayedDetonate);UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());EffectComp->DeactivateSystem();MoveComp->StopMovementImmediately();SetActorEnableCollision(false);FTimerHandle TimerHandle_DelayedTeleport;GetWorldTimerManager().SetTimer(TimerHandle_DelayedTeleport, this, &ASDashProjectile::TeleportInstigator, TeleportDelay);// Skip base implementation as it will destroy actor (we need to stay alive a bit longer to finish the 2nd timer)//Super::Explode_Implementation();
}

实现闪现传送效果:TeleportInstigator

APawn* Instigator为人物本身,调用actor的TeleportTo函数实现传送AActor::TeleportTo | Unreal Engine Documentation

void ASDashProjectile::TeleportInstigator()
{AActor* ActorToTeleport = GetInstigator();if (ensure(ActorToTeleport)){// Keep instigator rotation or it may end up jarringActorToTeleport->TeleportTo(GetActorLocation(), ActorToTeleport->GetActorRotation(), false, false);}
}

设置定时器:FTimerHandle

FTimerHandle TimerHandle_DelayedDetonate;

黑洞攻击(蓝图):Proj_BlackHole

由SProjectileBase继承而来,为蓝图。

添加了RadialForce径向力,并设置为负数,因为要实现一种黑洞的吸附能力

径向力Force Strength

可影响物体Object Types to Affect

要排除自身

吸进黑洞后要杀掉物体:

❗接口类:USGameplayInterface

USInteractionComponent

利用射线检测实现交互

void ASCharacter::PrimaryInteract()
{if (InteractionComp){InteractionComp->PrimaryInteract();}
}
void USInteractionComponent::PrimaryInteract()
{//碰撞预设FCollisionObjectQueryParams ObjectQueryParams;ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic);//获取自身AActor* MyOwner = GetOwner();//设置碰撞检测位置参数,从眼部开始FVector EyeLocation;FRotator EyeRotation;MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);FVector End = EyeLocation + (EyeRotation.Vector() * 1000);//FHitResult Hit;//bool bBlockingHit = GetWorld()->LineTraceSingleByObjectType(Hit, EyeLocation, End, ObjectQueryParams);TArray<FHitResult> Hits;float Radius = 30.f;//射线检测形状FCollisionShape Shape;Shape.SetSphere(Radius);bool bBlockingHit = GetWorld()->SweepMultiByObjectType(Hits, EyeLocation, End, FQuat::Identity, ObjectQueryParams, Shape);//射线颜色FColor LineColor = bBlockingHit ? FColor::Green : FColor::Red;for (FHitResult &Hit : Hits){AActor* HitActor = Hit.GetActor();if (HitActor){//执行击中物体所实现的接口if (HitActor->Implements<USGameplayInterface>()){APawn* MyPawn = Cast<APawn>(MyOwner);ISGameplayInterface::Execute_Interact(HitActor, MyPawn);break;}}DrawDebugSphere(GetWorld(), Hit.ImpactPoint, Radius, 32, LineColor, false, 2.0f);}DrawDebugLine(GetWorld(), EyeLocation, End, LineColor, false, 2.0f, 0, 2.0f);}

世界中的物体

爆炸油桶:ASExplosiveBarrel

设置StaticMesh,径向力

ASExplosiveBarrel::ASExplosiveBarrel()
{MeshComp = CreateDefaultSubobject<UStaticMeshComponent>("MeshComp");MeshComp->SetSimulatePhysics(true);RootComponent = MeshComp;ForceComp = CreateDefaultSubobject<URadialForceComponent>("ForceComp");ForceComp->SetupAttachment(MeshComp);// Leaving this on applies small constant force via component 'tick' (Optional)ForceComp->SetAutoActivate(false);ForceComp->Radius = 750.0f;ForceComp->ImpulseStrength = 2500.0f; // Alternative: 200000.0 if bImpulseVelChange = false// Optional, ignores 'Mass' of other objects (if false, the impulse strength will be much higher to push most objects depending on Mass)ForceComp->bImpulseVelChange = true;// Optional, default constructor of component already adds 4 object types to affect, excluding WorldDynamicForceComp->AddCollisionChannelToAffect(ECC_WorldDynamic);// Binding either in constructor or in PostInitializeComponents() below//MeshComp->OnComponentHit.AddDynamic(this, &ASExplosiveBarrel::OnActorHit);
}

初始化Actor组件(添加碰撞事件)

AActor::PostInitializeComponents | Unreal Engine Documentation

‎允许Actor在初始化所有组件后在C++端初始化自己,仅在游戏过程中调用‎

void ASExplosiveBarrel::PostInitializeComponents()
{// Don't forget to call parent functionSuper::PostInitializeComponents();MeshComp->OnComponentHit.AddDynamic(this, &ASExplosiveBarrel::OnActorHit);
}

其实有效果的就是第一句:

void ASExplosiveBarrel::OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{ForceComp->FireImpulse();UE_LOG(LogTemp, Log, TEXT("OnActorHit in Explosive Barrel"));// %s = string// %f = float// logs: "OtherActor: MyActor_1, at gametime: 124.4" UE_LOG(LogTemp, Warning, TEXT("OtherActor: %s, at game time: %f"), *GetNameSafe(OtherActor), GetWorld()->TimeSeconds);FString CombinedString = FString::Printf(TEXT("Hit at location: %s"), *Hit.ImpactPoint.ToString());DrawDebugString(GetWorld(), Hit.ImpactPoint, CombinedString, nullptr, FColor::Green, 2.0f, true);// Detailed info on logging in ue4// https://nerivec.github.io/old-ue4-wiki/pages/logs-printing-messages-to-yourself-during-runtime.html}

宝箱(实现交互接口):ASItemChest

 实现交互后,打开/关闭宝箱盖子 ,同时可以在蓝图中设置粒子效果

ASItemChest::ASItemChest()
{BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BaseMesh"));RootComponent = BaseMesh;LidMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("LidMesh"));LidMesh->SetupAttachment(BaseMesh);TargetPitch = 110;
}void ASItemChest::Interact_Implementation(APawn* InstigatorPawn)
{LidMesh->SetRelativeRotation(FRotator(TargetPitch, 0, 0));
}

在蓝图中的Class Settings里可以看到继承了接口:

实现交互开关宝箱、设置粒子效果

血包(实现交互接口):ASPowerupActor

老规矩,首先是简单的构造函数中的初始化

ASPowerupActor::ASPowerupActor()
{SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");SphereComp->SetCollisionProfileName("Powerup");RootComponent = SphereComp;RespawnTime = 10.0f;
}

血包有很多种,因此接口实现逻辑由子类实现:

void ASPowerupActor::Interact_Implementation(APawn* InstigatorPawn)
{// logic in derived classes...
}

吃掉后隐藏血包(计时器):

void ASPowerupActor::HideAndCooldownPowerup()
{SetPowerupState(false);GetWorldTimerManager().SetTimer(TimerHandle_RespawnTimer, this, &ASPowerupActor::ShowPowerup, RespawnTime);
}void ASPowerupActor::SetPowerupState(bool bNewIsActive)
{SetActorEnableCollision(bNewIsActive);// Set visibility on root and all childrenRootComponent->SetVisibility(bNewIsActive, true);
}

血包子类:ASPowerup_HealthPotion

void ASPowerup_HealthPotion::Interact_Implementation(APawn* InstigatorPawn)
{if (!ensure(InstigatorPawn)){return;}USAttributeComponent* AttributeComp = USAttributeComponent::GetAttributes(InstigatorPawn);// Check if not already at max healthif (ensure(AttributeComp) && !AttributeComp->IsFullHealth()){// Only activate if healed successfullyif (AttributeComp->ApplyHealthChange(this, AttributeComp->GetHealthMax())){HideAndCooldownPowerup();}}
}

UI:Widget

十字准星

创建Widget并设置:

游戏开始时添加到屏幕上

create widget:

人物血条、Credits、游戏进行时间:Main_HUD

血条

创建对应组件UI

为text绑定health事件

Credits:

游戏进行时间:

AI

UE中的AI系统

  • Navigation Mesh:AI寻路,可达区域
  • Behavior Tree:行为树,AI的大脑
  • Blackboard:AI数据存储
  • Environment Query System:空间查询
  • PawnSensing&AIPerception:感知
  • Debug相关

行为树:

设置黑板

添加导航网格体:NavMeshBoundsVolume

游戏AI角色:SAICharacter

游戏AI控制器:SAIController

设置行为树

class UBehaviorTree;/*** */
UCLASS()
class ACTIONROGUELIKE_API ASAIController : public AAIController
{GENERATED_BODY()protected:UPROPERTY(EditDefaultsOnly, Category = "AI")UBehaviorTree* BehaviorTree;virtual void BeginPlay() override;
};
void ASAIController::BeginPlay()
{Super::BeginPlay();if (ensureMsgf(BehaviorTree, TEXT("Behavior Tree is nullptr! Please assign BehaviorTree in your AI Controller."))){RunBehaviorTree(BehaviorTree);}//  APawn* MyPawn = UGameplayStatics::GetPlayerPawn(this, 0);
//  if (MyPawn)
//  {
//      GetBlackboardComponent()->SetValueAsVector("MoveToLocation", MyPawn->GetActorLocation());
//
//      GetBlackboardComponent()->SetValueAsObject("TargetActor", MyPawn);
//  }
}

 

为SAICharacter设置控制器

游戏AI逻辑-范围内攻击:USBTService_CheckAttackRange(UBTService)

范围查询逻辑

  • 检查AI与玩家的距离
  • LineOfSightTo:true if controller's pawn can see Other actor
  • 在黑板中添加对应key值,通过C++逻辑设置bool值:BlackBoardComp->SetValueAsBool(AttackRangeKey.SelectedKeyName, (bWithinRange && bHasLOS));

逻辑为:当且仅当:

  • AI距离玩家小于MaxAttackRange时
  • AI能够看见玩家时

为真。

void USBTService_CheckAttackRange::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);// Check distance between ai pawn and target actorUBlackboardComponent* BlackBoardComp = OwnerComp.GetBlackboardComponent();if (ensure(BlackBoardComp)){AActor* TargetActor = Cast<AActor>(BlackBoardComp->GetValueAsObject("TargetActor"));if (TargetActor){AAIController* MyController = OwnerComp.GetAIOwner();APawn* AIPawn = MyController->GetPawn();if (ensure(AIPawn)){float DistanceTo = FVector::Distance(TargetActor->GetActorLocation(), AIPawn->GetActorLocation());bool bWithinRange = DistanceTo < MaxAttackRange;bool bHasLOS = false;if (bWithinRange){bHasLOS = MyController->LineOfSightTo(TargetActor);}BlackBoardComp->SetValueAsBool(AttackRangeKey.SelectedKeyName, (bWithinRange && bHasLOS));}}}
}

在行为树添加此Service

CheckAttackRange后,会首先进入WithinAttackRange?,若条件为真,则AI执行攻击;若条件为假,则AI向玩家移动,直到到达攻击范围内;

游戏AI范围内攻击任务结点:USBTTask_RangedAttack

AI朝向玩家:Set default focus

环境查询系统:

创建EQS

图中蓝色球体为未选中移动目标,绿色球体为已被选择的目标,AI会对EQS所查询的目标进行移动

随机选取一个位置让AI移动

在玩家周围半径内设置EQS生成移动目的地

AI死亡

void ASAICharacter::OnHealthChanged(AActor* InstigatorActor, USAttributeComponent* OwningComp, float NewHealth, float Delta)
{if (Delta < 0.0f){if (InstigatorActor != this){SetTargetActor(InstigatorActor);}if (ActiveHealthBar == nullptr){ActiveHealthBar = CreateWidget<USWorldUserWidget>(GetWorld(), HealthBarWidgetClass);if (ActiveHealthBar){ActiveHealthBar->AttachedActor = this;ActiveHealthBar->AddToViewport();}}GetMesh()->SetScalarParameterValueOnMaterials(TimeToHitParamName, GetWorld()->TimeSeconds);if (NewHealth <= 0.0f){// stop BTAAIController* AIC = Cast<AAIController>(GetController());if (AIC){AIC->GetBrainComponent()->StopLogic("Killed");}// ragdollGetMesh()->SetAllBodiesSimulatePhysics(true);GetMesh()->SetCollisionProfileName("Ragdoll");// set lifespanSetLifeSpan(10.0f);}}
}

UE4 C++ ActionRoguelike开发记录相关推荐

  1. 使用KBEngine开发UE4服务端——开发详解

    使用KBEngine开发UE4服务端--开发详解 1. 简述 开始正式进入开发,网络上关于开发和修改文件的文章较少,这里会记录自己开发过程中需要更改的地方和一些流程. 2. 前期的配置说明 根据前文配 ...

  2. Anytime项目开发记录0

    Anytime,中文名:我很忙. 开发者:孤独的猫咪神. 这个项目会持续更新,直到我决定不再维护这个APP. 2014年3月10日:近日有事,暂时断更.希望可以会尽快完事. 2014年3月27日:很抱 ...

  3. CozyRSS开发记录3-标题栏再加强

    CozyRSS开发记录3-标题栏再加强 1.更精炼的标题栏 接下来,我们把窗口的边框和默认的标题栏给去掉,让Cozy看起来更像一个平板应用. 在主窗口的属性里,修改下列两个属性: 效果一目了然: 2. ...

  4. CozyRSS开发记录19-窗口标题栏交互

    CozyRSS开发记录19-窗口标题栏交互 1.谈谈对mvvm解耦的看法 在使用mvvm时,如何操作窗口,这是一个问题.这个问题的关键点是:mvvm是把view和viewmodel解耦了的,很多写法一 ...

  5. TMS320F28335项目开发记录9_28335之中断系统

    TMS320F28335项目开发记录9_28335之中断系统 2014年11月08日 12:00:12 阅读数:3104 28335中断系统 1.中断系统 在这里我们要十分清楚DSP的中断系统.C28 ...

  6. 转:修改Content Server管理员密码 - [Documentum 实施开发记录]

    修改Content Server管理员密码 - [Documentum 实施开发记录] 2010-02-25 Tag: 版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明 http:// ...

  7. CozyRSS开发记录8-解析一份RSS

    CozyRSS开发记录8-解析一份RSS 1.使用Rss20FeedFormatter解析RSS 使用Rss20FeedFormatter配合XmlReader来解析RSS非常的简单,几行搞定: 来试 ...

  8. 前端radio单选框默认选中_开发记录篇前端内容1

    有段时间没有更新文章了,因为是用的公司电脑,没有虚拟机,所以就没法演示hadoop相关的东西了,而且大数据篇的东西需要花费一些时间和精力去收集整理内容,那大数据篇就先暂停一下.最近这段时间的话我可能会 ...

  9. DTS开发记录(5)-- 挑战增量导出

    增量导出恐怕是DTS系统中最艰难的部分了,我们曾考虑过很多方案,最后都因为需要表大纲做一定的假设而不具备通用性而放弃.有很多效率较高的方案,由于为了追求通用性而无法实现,因为现实的情况比我们理想的要复 ...

最新文章

  1. matlab contour光滑,使用Matplotlib在Contour Plot中平滑数据
  2. GMTC 大前端时代前端监控的最佳实践
  3. bat修改文件内容_在win10系统中一键修改MapGIS67系统库背景色
  4. Jacobian vector products(转载+翻译+代码+解读)
  5. GP学习(四)—Running a geoprocessing tool using background geoprocessing
  6. 终于没刘海了!iPhone12 Pro 渲染图首曝,回归经典
  7. Odoo 14 版本优化更新的新功能体验
  8. css—left和margin-left的区别
  9. 一些常用的JavaScript总结
  10. Ps 初学者教程,如何使用色阶功能提高照片的对比度和亮度?
  11. Javascript面向对象编程与继承机制的设计思想(转)
  12. 区块链技术指南:术语
  13. 台达触摸屏MODBUS直接与台达变频器通讯程序
  14. ERP仓库管理系统主要功能
  15. python遍历二维秋天_只要一杯秋天的奶茶,就能学会Python数值分析(1)
  16. 2022-2027年(新版)中国石油化工行业发展建议及投资前景展望报告
  17. 管理多个java版本,OS X中使用jEnv管理多个Java版本
  18. 服务器机房消防系统,服务器机房消防系统和维护
  19. 希望今天遇见你(二)
  20. 12306登录password参数加密逻辑

热门文章

  1. 千元以内蓝牙耳机只能听响?2020五款高音质蓝牙耳机给你好看!
  2. lol韩服游戏内设置_LOL韩服更新账号教程 LOL韩服不能玩解决办法
  3. SAP Marketing Cloud 功能概述(二)
  4. 电网大屏报表的制作--全国用电量分析
  5. 深入浅出 gRPC 04:gRPC 服务调用原理
  6. 2021/11/1 前端开发之JavaScript+jQuery入门 第十三章表单效验
  7. JavaScript从零开始 学习记录(三)
  8. springboot+篮球场馆预约系统 毕业设计-附源码211706
  9. 一场游戏一场梦------------------大学随手写(11年05月-------11年06月04)
  10. HTML里关于空间转换3D和动画效果的实现