UEC++ day8

傷害系統

給敵人創建血條

  • 首先添加一個UI界面用來顯示敵人血條
  • 設置背景圖像為黑色半透明
    在這里插入圖片描述
  • 填充顏色
    在這里插入圖片描述
  • 給敵人類添加兩種狀態表示血量與最大血量,添加一個UWidegtComponet組件與UProgressBar組件
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Enemy Stats")float Health;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Enemy Stats")float MaxHealth;UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Enemy Stats")class UWidgetComponent* HealthBarWidgetComponent;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Enemy Stats")class UProgressBar* HealthBar;MaxHealth = 100.f;
Health = MaxHealth;

設置WidgetComponent組件

  • 頭文件:
    • #include “Components/WidgetComponent.h”:使用WidgetComponent就要使用
    • #include “Blueprint/UserWidget.h”:獲取到User內部對象時需要使用
    • #include “Components/ProgressBar.h”:獲取調用ProgressBar需要使用
  • 創建UWidgetComponent組件
	HealthBarWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("HealthBarWidgetComponent"));HealthBarWidgetComponent->SetupAttachment(GetRootComponent());HealthBarWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);HealthBarWidgetComponent->SetDrawSize(FVector2D(125.f, 10.f));HealthBarWidgetComponent->SetWorldLocation(FVector(0.f, 0.f, 50.f));
  • 獲取到HealBar小組件
// Called when the game starts or when spawned
void ABaseEnemy::BeginPlay()
{Super::BeginPlay();ChaseVolume->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnChaseVolumeOverlapBegin);ChaseVolume->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnChaseVolumeOverlapEnd);AttackVolume->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnAttackVolumeOverlapBegin);AttackVolume->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnAttackVolumeOverlapEnd);//獲取到HealBar小組件HealthBar = Cast<UProgressBar>(HealthBarWidgetComponent->GetUserWidgetObject()->GetWidgetFromName("HealthBar"));HealthBar->SetPercent(Health / MaxHealth);//拿到ControllerAIController = Cast<AAIController>(GetController());
}
  • 運行結果
    在這里插入圖片描述
    在這里插入圖片描述

血條進入敵人追逐區顯示

  • 一開始敵人血條是不顯示的,只有主角進入了敵人追逐區才開始顯示血條,出了追逐區也不顯示
	//獲取到HealBar小組件HealthBar = Cast<UProgressBar>(HealthBarWidgetComponent->GetUserWidgetObject()->GetWidgetFromName("HealthBar"));HealthBar->SetPercent(Health / MaxHealth);HealthBar->SetVisibility(ESlateVisibility::Hidden);//一開始不顯示血條void ABaseEnemy::OnChaseVolumeOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){//主角進入追逐范圍顯示血條HealthBar->SetVisibility(ESlateVisibility::Visible);MoveToTarget(Player);}}
}void ABaseEnemy::OnChaseVolumeOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (AIController){//主角出追逐范圍不顯示血條HealthBar->SetVisibility(ESlateVisibility::Hidden);//停止移動AIController->StopMovement();}}}
}

觸發傷害思路

  • 我們可以在武器上加一個碰撞器,當這個碰撞器碰撞到敵人的時候就開啟傷害,如果沒有就躲避的傷害
  • 給每把劍的骨骼添加一個Socket
    在這里插入圖片描述

觸發傷害需求

  • 因為要使用UE中內置的直接傷害附加的功能,類似爆炸物那一節,所以我們要在武器類中新建一個盒子碰撞觸發器,新建一個傷害值,新建一個傷害類型,新建一個傷害發起者
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon|Attack")class UBoxComponent* AttackCollision;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Attack")float Damage;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Attack")TSubclassOf<UDamageType> DamageTyClass;UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon|Attack")class AController* WeaponOwner;
  • 新建傷害開始觸發事件與結束事件,兩個動態切換碰撞函數,我只需要在揮劍的那個瞬間去切換需要在動畫的notify中去切換的所以需要添加反射
	UFUNCTION()void OnAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);UFUNCTION()void OnAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);//動態切換碰撞UFUNCTION(BlueprintCallable)void ActiveAttackCollision();UFUNCTION(BlueprintCallable)void DeactiveAttackCollision();
  • 初始化,創建BoxComponent需要頭文件:#include “Components/BoxComponent.h”
	AttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("AttackCollision"));AttackCollision->SetupAttachment(DisplayMesh, "WeaponSocket");//將這個碰撞點附加到武器插槽上DeactiveAttackCollision();//關閉碰撞Damage = 25.f;//綁定
void AWeaponItem::BeginPlay()
{Super::BeginPlay();AttackCollision->OnComponentBeginOverlap.AddDynamic(this, &AWeaponItem::OnAttackCollisionOverlapBegin);AttackCollision->OnComponentEndOverlap.AddDynamic(this, &AWeaponItem::OnAttackCollisionOverlapEnd);
}void AWeaponItem::OnAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
}void AWeaponItem::OnAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}
//設置碰撞類型
void AWeaponItem::ActiveAttackCollision()
{AttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);AttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);AttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);AttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);}void AWeaponItem::DeactiveAttackCollision()
{AttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
WeaponItem.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "WeaponItem.h"
#include "Components/SkeletalMeshComponent.h"
#include "Characters/Player/MainPlayer.h"
#include "Engine/SkeletalMeshSocket.h"
#include "Kismet/GameplayStatics.h"
#include "Sound/SoundCue.h"
#include "Particles/ParticleSystemComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/BoxComponent.h"
AWeaponItem::AWeaponItem()
{//銷毀if (DisplayMesh){DisplayMesh->DestroyComponent();//UE_LOG(LogTemp, Warning, TEXT("delete succeed"));}else{//UE_LOG(LogTemp, Warning, TEXT("fail to delete"));}//因為TEXT具有唯一性,我們不知道什么時候銷毀原UStaticMeshComponent* DisplayMesh;所以這里TEXT進行一下區分DisplayMesh=CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("DisplaySkeletalMesh"));DisplayMesh->SetupAttachment(GetRootComponent());ActiveDisplayMeshCollision();//設置碰撞AttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("AttackCollision"));AttackCollision->SetupAttachment(DisplayMesh, "WeaponSocket");//將這個碰撞點附加到武器插槽上DeactiveAttackCollision();static ConstructorHelpers::FObjectFinder<USoundCue> SoundCueAsset(TEXT("SoundCue'/Game/Assets/Audios/Blade_Cue.Blade_Cue'"));if (SoundCueAsset.Succeeded()){OnEquipSound = SoundCueAsset.Object;}//拾取武器后粒子效果默認關閉bOnEquipParticle = false;//默認狀態武器是可拾取的WeaponState = EWeaponState::EWS_CanPickUp;Damage = 25.f;
}void AWeaponItem::BeginPlay()
{Super::BeginPlay();AttackCollision->OnComponentBeginOverlap.AddDynamic(this, &AWeaponItem::OnAttackCollisionOverlapBegin);AttackCollision->OnComponentEndOverlap.AddDynamic(this, &AWeaponItem::OnAttackCollisionOverlapEnd);
}void AWeaponItem::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{Super::OnOverlapBegin(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);if (OtherActor && WeaponState == EWeaponState::EWS_CanPickUp){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){//告訴角色正在重疊的武器是當前武器Player->OverlapWeapon = this;}}
}void AWeaponItem::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{Super::OnOverlapEnd(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex);if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);//判斷一開始是否拾起的武器是當前武器if (Player && Player->OverlapWeapon == this){//告訴角色離開了武器觸發器Player->OverlapWeapon = nullptr;}}
}void AWeaponItem::Equip(AMainPlayer* Player)
{if (Player && !Player->GetMovementComponent()->IsFalling()){//已裝備武器WeaponState = EWeaponState::EWS_Equip;DeactiveDisplayMeshCollision();//關閉碰撞//獲取Player的Socketconst USkeletalMeshSocket* RightHandSocker = Player->GetMesh()->GetSocketByName(TEXT("RightHandSocket"));if (RightHandSocker){//讓武器附屬到Socket上RightHandSocker->AttachActor(this, Player->GetMesh());Player->bIsWeapon = true;Player->EquipWeapon = this;Player->OverlapWeapon = nullptr;bRotate = false;//武器旋轉關閉if (OnEquipSound){//播放音樂UGameplayStatics::PlaySound2D(this, OnEquipSound);}//if (!bOnEquipParticle)//{//	//關閉粒子組件//	ParticleEffectsComponent->Deactivate();//	//}}}
}void AWeaponItem::UnEuip(AMainPlayer* Player)
{if (Player && !Player->GetMovementComponent()->IsFalling() && !Player->bIsAttacking){WeaponState = EWeaponState::EWS_CanPickUp;ActiveDisplayMeshCollision();//開啟碰撞Player->bIsWeapon = false;Player->EquipWeapon = nullptr;if (Player->OverlapWeapon == nullptr){Player->OverlapWeapon = this;}//分離當前WeaponItem SocketDetachFromActor(FDetachmentTransformRules::KeepWorldTransform);SetActorRotation(FRotator(0.f));SetActorScale3D(FVector(1.f));bRotate = true;	}
}void AWeaponItem::ActiveDisplayMeshCollision()
{DisplayMesh->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);DisplayMesh->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);DisplayMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);DisplayMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Block);}void AWeaponItem::DeactiveDisplayMeshCollision()
{DisplayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}void AWeaponItem::OnAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
}void AWeaponItem::OnAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}void AWeaponItem::ActiveAttackCollision()
{AttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);AttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);AttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);AttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);}void AWeaponItem::DeactiveAttackCollision()
{AttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

在蒙太奇中添加攻擊通知激活傷害碰撞

  • 在主角的蒙太奇中添加四個通知,跟當時添加攻擊結束通知差不多
    在這里插入圖片描述
  • 然后在動畫藍圖中進行調用函數
    在這里插入圖片描述
  • 將三把劍的盒子碰撞檢測大小調整一下
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述

給敵人添加觸發器

  • 基本與上面給主角添加傷害需求的思路一致
  • 先給敵人添加兩個骨骼
    在這里插入圖片描述

觸發傷害需求

  • 再來就是編輯敵人類的變量需求與基本邏輯了,兩個觸發盒子因為是兩只腿的攻擊,新建傷害值,傷害類型
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Attack")class UBoxComponent* LeftAttackCollision;UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Attack")UBoxComponent* RightAttackCollision;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack")float Damage;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack")TSubclassOf<UDamageType> DamageTyClass;
  • 不用說,現在是四個傷害觸發事件的新建,四個動態切換碰撞的函數
	UFUNCTION()void OnLeftAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);UFUNCTION()void OnLeftAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);UFUNCTION()void OnRightAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);UFUNCTION()void OnRightAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);//動態切換碰撞UFUNCTION(BlueprintCallable)void ActiveLeftAttackCollision();UFUNCTION(BlueprintCallable)void DeactiveLeftAttackCollision();UFUNCTION(BlueprintCallable)void ActiveRightAttackCollision();UFUNCTION(BlueprintCallable)void DeactiveRightAttackCollision();
  • 現在就可以開始編寫,初始化了
	LeftAttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("LeftAttackCollision"));LeftAttackCollision->SetupAttachment(GetMesh(), "LeftAttackSocket");DeactiveLeftAttackCollision();RightAttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("RightAttackCollision"));RightAttackCollision->SetupAttachment(GetMesh(), "RightAttackSocket");DeactiveRightAttackCollision();//賦值	
Damage = 10.f;//綁定LeftAttackCollision->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnLeftAttackCollisionOverlapBegin);LeftAttackCollision->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnLeftAttackCollisionOverlapEnd);RightAttackCollision->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnRightAttackCollisionOverlapBegin);RightAttackCollision->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnRightAttackCollisionOverlapEnd);void ABaseEnemy::ActiveLeftAttackCollision()
{LeftAttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);LeftAttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);LeftAttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);LeftAttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}void ABaseEnemy::DeactiveLeftAttackCollision()
{LeftAttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}void ABaseEnemy::ActiveRightAttackCollision()
{RightAttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);RightAttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);RightAttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);RightAttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}void ABaseEnemy::DeactiveRightAttackCollision()
{RightAttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
BaseEnemy.cpp
// Fill out your copyright notice in the Description page of Project Settings.#include "BaseEnemy.h"
#include "Components/SphereComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/CapsuleComponent.h"
#include "AIController.h"
#include "Characters/Player/MainPlayer.h"
#include "Animation/AnimInstance.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Components/WidgetComponent.h"
#include "Blueprint/UserWidget.h"
#include "Components/ProgressBar.h"
#include "Components/BoxComponent.h"
// Sets default values
ABaseEnemy::ABaseEnemy()
{// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;ChaseVolume = CreateDefaultSubobject<USphereComponent>(TEXT("ChaseVolume"));ChaseVolume->SetupAttachment(GetRootComponent());ChaseVolume->InitSphereRadius(800.f);ChaseVolume->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);ChaseVolume->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);ChaseVolume->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);AttackVolume = CreateDefaultSubobject<USphereComponent>(TEXT("AttackVolume"));AttackVolume->SetupAttachment(GetRootComponent());AttackVolume->InitSphereRadius(100.f);AttackVolume->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);AttackVolume->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);AttackVolume->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);HealthBarWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("HealthBarWidgetComponent"));HealthBarWidgetComponent->SetupAttachment(GetRootComponent());HealthBarWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);HealthBarWidgetComponent->SetDrawSize(FVector2D(125.f, 10.f));HealthBarWidgetComponent->SetWorldLocation(FVector(0.f, 0.f, 50.f));LeftAttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("LeftAttackCollision"));LeftAttackCollision->SetupAttachment(GetMesh(), "LeftAttackSocket");DeactiveLeftAttackCollision();RightAttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("RightAttackCollision"));RightAttackCollision->SetupAttachment(GetMesh(), "RightAttackSocket");DeactiveRightAttackCollision();//避免攝像機被敵人給阻擋GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);//設置持有屬性AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;//初始化默認移動狀態EnemyMovementStatus = EEnemyMovementStatus::EEMS_Idle;InterpSpeed = 15.f;bInterpToPlayer = false;MaxHealth = 100.f;Health = MaxHealth;Damage = 10.f;
}// Called when the game starts or when spawned
void ABaseEnemy::BeginPlay()
{Super::BeginPlay();ChaseVolume->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnChaseVolumeOverlapBegin);ChaseVolume->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnChaseVolumeOverlapEnd);AttackVolume->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnAttackVolumeOverlapBegin);AttackVolume->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnAttackVolumeOverlapEnd);LeftAttackCollision->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnLeftAttackCollisionOverlapBegin);LeftAttackCollision->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnLeftAttackCollisionOverlapEnd);RightAttackCollision->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnRightAttackCollisionOverlapBegin);RightAttackCollision->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnRightAttackCollisionOverlapEnd);//獲取到HealBar小組件HealthBar = Cast<UProgressBar>(HealthBarWidgetComponent->GetUserWidgetObject()->GetWidgetFromName("HealthBar"));HealthBar->SetPercent(Health / MaxHealth);HealthBar->SetVisibility(ESlateVisibility::Hidden);//一開始不顯示血條//拿到ControllerAIController = Cast<AAIController>(GetController());
}// Called every frame
void ABaseEnemy::Tick(float DeltaTime)
{Super::Tick(DeltaTime);if (bInterpToPlayer){FRotator LookYaw(0.f, UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), UGameplayStatics::GetPlayerPawn(this, 0)->GetActorLocation()).Yaw, 0.f);FRotator InterpRotation = FMath::RInterpTo(GetActorRotation(), LookYaw, DeltaTime, InterpSpeed);SetActorRotation(InterpRotation);}
}// Called to bind functionality to input
void ABaseEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{Super::SetupPlayerInputComponent(PlayerInputComponent);}void ABaseEnemy::OnChaseVolumeOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){//主角進入追逐范圍顯示血條HealthBar->SetVisibility(ESlateVisibility::Visible);MoveToTarget(Player);}}
}void ABaseEnemy::OnChaseVolumeOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (AIController){//主角出追逐范圍不顯示血條HealthBar->SetVisibility(ESlateVisibility::Hidden);//停止移動AIController->StopMovement();}}}
}void ABaseEnemy::OnAttackVolumeOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){Player->UpdataAttackTarget();bAttackVolumeOverlap = true;AttackBegin();}}
}void ABaseEnemy::OnAttackVolumeOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){bAttackVolumeOverlap = false;if (EnemyMovementStatus!=EEnemyMovementStatus::EEMS_Attacking){MoveToTarget(Player);}}}
}void ABaseEnemy::MoveToTarget(AMainPlayer* Player)
{EnemyMovementStatus = EEnemyMovementStatus::EEMS_MoveToTarget;if (AIController){FAIMoveRequest MoveRequest;MoveRequest.SetGoalActor(Player);//設置移動請求目標MoveRequest.SetAcceptanceRadius(10.f);	//設置移動半徑FNavPathSharedPtr NavPath;//會返回路徑AIController->MoveTo(MoveRequest, &NavPath);}
}void ABaseEnemy::AttackBegin()
{//攻擊中關閉移動if (AIController){AIController->StopMovement();}if (EnemyMovementStatus != EEnemyMovementStatus::EEMS_Attacking){EnemyMovementStatus = EEnemyMovementStatus::EEMS_Attacking;bInterpToPlayer = true;UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();if (AnimInstance && AttackMontage){float PlayRate = FMath::RandRange(0.9f, 1.1f);FString SectionName = FString::FromInt(FMath::RandRange(1, 3));AnimInstance->Montage_Play(AttackMontage, PlayRate);AnimInstance->Montage_JumpToSection(FName(*SectionName), AttackMontage);}}
}void ABaseEnemy::AttackEnd()
{EnemyMovementStatus = EEnemyMovementStatus::EEMS_Idle;bInterpToPlayer = false;if (bAttackVolumeOverlap){AttackBegin();}
}void ABaseEnemy::OnLeftAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
}void ABaseEnemy::OnLeftAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}void ABaseEnemy::OnRightAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
}void ABaseEnemy::OnRightAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}void ABaseEnemy::ActiveLeftAttackCollision()
{LeftAttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);LeftAttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);LeftAttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);LeftAttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}void ABaseEnemy::DeactiveLeftAttackCollision()
{LeftAttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}void ABaseEnemy::ActiveRightAttackCollision()
{RightAttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);RightAttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);RightAttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);RightAttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}void ABaseEnemy::DeactiveRightAttackCollision()
{RightAttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

在蒙太奇中添加攻擊通知激活傷害碰撞

  • 首先修改攻擊觸發器的大小
    在這里插入圖片描述
  • 在敵人的蒙太奇中添加通知,跟上面主角添加通知差不多
    在這里插入圖片描述
  • 編輯動畫藍圖即可
    在這里插入圖片描述

設置擊中敵人時的特效、音效與傷害

  • 給MainPlayer.h與BaseEnemy.h中添加一個粒子系統與聲音組件
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hit Effect")class UParticleSystem* HitPaticles;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hit Effect")class USoundCue* HitSound;
  • 在WeaponItem.cpp的攻擊開始重疊事件中去添加聲音特效與傷害,這個就和之前裝備武器與爆炸物傳遞傷害邏輯差不多
  • 正好復習一下,獲取插槽的時候必須加const,因為獲取插槽的函數返回的是一個const值
void AWeaponItem::OnAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor){ABaseEnemy* BaseEnemy = Cast<ABaseEnemy>(OtherActor);if (BaseEnemy){if (BaseEnemy->HitPaticles){//獲取WeaponSocket插槽const USkeletalMeshSocket* WeaponScoket = ((USkeletalMeshComponent*)DisplayMesh)->GetSocketByName("WeaponSocket");if (WeaponScoket){FVector SocketLocation = WeaponScoket->GetSocketLocation((USkeletalMeshComponent*)DisplayMesh);UGameplayStatics::SpawnEmitterAtLocation(this, BaseEnemy->HitPaticles, SocketLocation, FRotator(0.f), true);}if (BaseEnemy->HitSound){UGameplayStatics::PlaySound2D(this, BaseEnemy->HitSound);}if (DamageTyClass){UGameplayStatics::ApplyDamage(BaseEnemy, Damage, WeaponOwner, this, DamageTyClass);}}}}
}
  • 將攻擊類型添加到每把劍上,人物添加上粒子與音效

在這里插入圖片描述
在這里插入圖片描述

設置擊中玩家時的特效、音效與傷害

  • 基本與上面一樣,在BaseEnemy.cpp的左右攻擊事件中添加粒子音效與傷害
void ABaseEnemy::OnLeftAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (Player->HitPaticles){const USkeletalMeshSocket* AttackScoket = GetMesh()->GetSocketByName("LeftAttackSocket");if (AttackScoket){FVector SocketLocation = AttackScoket->GetSocketLocation(GetMesh());UGameplayStatics::SpawnEmitterAtLocation(this, Player->HitPaticles, SocketLocation, FRotator(0.f), true);}if (Player->HitSound){UGameplayStatics::PlaySound2D(this, Player->HitSound);}if (DamageTyClass){UGameplayStatics::ApplyDamage(Player, Damage, AIController, this, DamageTyClass);}}}}
}void ABaseEnemy::OnLeftAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}void ABaseEnemy::OnRightAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (Player->HitPaticles){const USkeletalMeshSocket* AttackScoket = GetMesh()->GetSocketByName("RightAttackSocket");if (AttackScoket){FVector SocketLocation = AttackScoket->GetSocketLocation(GetMesh());UGameplayStatics::SpawnEmitterAtLocation(this, Player->HitPaticles, SocketLocation, FRotator(0.f), true);}if (Player->HitSound){UGameplayStatics::PlaySound2D(this, Player->HitSound);}if (DamageTyClass){UGameplayStatics::ApplyDamage(Player, Damage, AIController, this, DamageTyClass);}}}}
}
  • 將攻擊類型添加到敵人上,添加上粒子與音效
    在這里插入圖片描述

敵人受傷死亡分析

  • 敵人與主角類中添加死亡函數
  • 在敵人類中重寫TakeDamage方法接收傷害,注意的是因為MainPlayer的血量UI是寫在UI綁定事件藍圖里面在,而敵人血量UI是在C++中編碼跟隨,所以最后返回寫完邏輯返回血量前,要更新一下血條
	//重寫TakeDamage方法float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;void Die();
  • 傷害邏輯
float ABaseEnemy::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{if (Health - Damage <= 0.f){Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth);Die();}else{Health -= Damage;}HealthBar->SetPercent(Health / MaxHealth);//更新UI血條return Health;
}

按布爾類型播放死亡動畫

  • 思路:我們還是可以使用notify來通知調用死亡動畫,首先在MainPlayer與BaseEnemy中新建一個用于信息通知的函數
UFUNCTION(BlueprintCallable)
void DeathEnd();
  • 然后編輯MainPlayer死亡函數的邏輯,首先主角狀態設置為死亡,判斷是否持劍,持劍就關閉武器所有碰撞
void AMainPlayer::Die()
{SetMovementStatus(EPlayerMovementStatus::EPMS_Dead);if (EquipWeapon){EquipWeapon->DeactiveAttackCollision();EquipWeapon->DeactiveDisplayMeshCollision();}
}
  • 編輯BaseEnemy死亡函數邏輯,首先設置狀態為死亡 ,關閉所有的碰撞,因為敵人已死亡更新主角攻擊目標
void ABaseEnemy::Die()
{EnemyMovementStatus = EEnemyMovementStatus::EEMS_Dead;DeactiveLeftAttackCollision();DeactiveRightAttackCollision();ChaseVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);AttackVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);//敵人死亡,主角要更新攻擊目標Cast<AMainPlayer>(UGameplayStatics::GetPlayerPawn(this, 0))->UpdataAttackTarget();
}
  • 編輯他們的動畫藍圖,添加Blend Poses by bool播放死亡動畫,Enemy狀態為死亡時播放死亡動畫,否則就正常播放其他動畫,記得將死亡的循環播放動畫關閉
    在這里插入圖片描述
    在這里插入圖片描述

限制玩家死亡后的一切活動狀態

  • 思路:我們在MainPlayer與BaseEnemy中新建一個專門用來判斷玩家是否存活狀態的函數,如果玩家不存活就停止一切活動
FORCEINLINE bool IsAlive() { return MovementStatus != EPlayerMovementStatus::EPMS_Dead; }
  • 敵人的狀態中還得檢測一下玩家是否死亡
FORCEINLINE bool IsAlive() { return EnemyMovementStatus != EEnemyMovementStatus::EEMS_Dead; }
bool HasValidTarget();//------------------------------------------------------------------------------------------------
bool ABaseEnemy::HasValidTarget()
{return Cast<AMainPlayer>(UGameplayStatics::GetPlayerPawn(this, 0))->MovementStatus != EPlayerMovementStatus::EPMS_Dead;
}

MainPlayer.cpp

  • 然后在MainPlayer中添加判斷禁止死亡后的一切活動,也就是都加上IsAlive判斷
// Fill out your copyright notice in the Description page of Project Settings.#include "MainPlayer.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GamePlay/WeaponItem.h"
#include "Animation/AnimInstance.h"
#include "Characters/Enemy/BaseEnemy.h"
#include "Kismet/KismetMathLibrary.h"
// Sets default values
AMainPlayer::AMainPlayer()
{// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));SpringArm->SetupAttachment(GetRootComponent());//設置SPringArm無碰撞臂長SpringArm->TargetArmLength = 600.f;SpringArm->bUsePawnControlRotation = true;//硬編碼SpringArm繼承controlller旋轉為真FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));FollowCamera->SetupAttachment(SpringArm, NAME_None);FollowCamera->bUsePawnControlRotation = false;//硬編碼FollowCamera繼承controlller旋轉為假//設置膠囊體的默認寬高GetCapsuleComponent()->SetCapsuleSize(35.f, 100.f);//對Character的Pawn進行硬編碼bUseControllerRotationPitch = false;bUseControllerRotationYaw = false;bUseControllerRotationRoll = false;//硬編碼orient Rotation to Movement,給個默認轉向速率GetCharacterMovement()->bOrientRotationToMovement = true;GetCharacterMovement()->RotationRate = FRotator(0.f, 500.f, 0.f);//設置跳躍初始值與在空中的墜落時橫向運動控制量GetCharacterMovement()->JumpZVelocity = 400.f;GetCharacterMovement()->AirControl = 0.15f;//給鍵盤控制轉向的速率變量賦初值BaseTurnRate = 21.f;BaseLookUpRate = 21.f;//初始化角色狀態MaxHealth = 100.f;Health = MaxHealth;MaxStamina = 200.f;Stamina = MaxStamina;StaminaConsumeRate = 20.f;ExhaustedStamina = 0.167f;Coins = 0;RunningSpeed = 600.f;SprintSpeed = 900.f;MovementStatus = EPlayerMovementStatus::EPMS_Normal;StaminaStatus = EPlayerStaminaStatus::EPSS_Normal;//默認沒有按下shiftbLeftShiftDown = false;InterpSpeed = 15.f;bInterpToEnemy = false;
}// Called when the game starts or when spawned
void AMainPlayer::BeginPlay()
{Super::BeginPlay();
}// Called every frame
void AMainPlayer::Tick(float DeltaTime)
{Super::Tick(DeltaTime);//不存活就不執行其他活動了if (!IsAlive()){return;}switch (StaminaStatus){case EPlayerStaminaStatus::EPSS_Normal://當Shift按下if (bLeftShiftDown){if (Stamina - StaminaConsumeRate * DeltaTime <= MaxStamina * ExhaustedStamina){StaminaStatus = EPlayerStaminaStatus::EPSS_Exhausted;}//無論是不是精疲力盡狀態都要減去當前幀沖刺消耗的耐力Stamina -= StaminaConsumeRate * DeltaTime;SetMovementStatus(EPlayerMovementStatus::EPMS_Sprinting);}else{//當Shift沒有按下,恢復耐力Stamina = FMath::Clamp(Stamina + StaminaConsumeRate * DeltaTime, 0.f, MaxStamina);SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);}break;case EPlayerStaminaStatus::EPSS_Exhausted:if (bLeftShiftDown){//如果耐力已經為0if (Stamina - StaminaConsumeRate * DeltaTime <= 0.f){//么我們需要內部編碼把shift抬起,此時StaminaStatus狀態轉換為ExhaustedRecovering狀態,然后設置移動狀態為NormalLeftShiftUp();StaminaStatus = EPlayerStaminaStatus::EPSS_ExhaustedRecovering;	SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);}else{Stamina -= StaminaConsumeRate * DeltaTime;}}else{StaminaStatus = EPlayerStaminaStatus::EPSS_ExhaustedRecovering;Stamina = FMath::Clamp(Stamina + StaminaConsumeRate * DeltaTime, 0.f, MaxStamina);SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);}break;case EPlayerStaminaStatus::EPSS_ExhaustedRecovering://當恢復大于疲勞區時,StaminaStatus狀態為Normalif (Stamina + StaminaConsumeRate * DeltaTime >= MaxStamina * ExhaustedStamina){StaminaStatus = EPlayerStaminaStatus::EPSS_Normal;}//這狀態值肯定是加定了Stamina += StaminaConsumeRate * DeltaTime;//抬起shiftLeftShiftUp();SetMovementStatus(EPlayerMovementStatus::EPMS_Normal);break;default:break;}//進行轉向插值if (bInterpToEnemy && AttackTarget){//只需要AttackTarget的Yaw轉向FRotator LookAtYaw(0.f, UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), AttackTarget->GetActorLocation()).Yaw, 0.f);FRotator InterpRotation = FMath::RInterpTo(GetActorRotation(), LookAtYaw, DeltaTime, InterpSpeed);SetActorRotation(InterpRotation);}
}// Called to bind functionality to input
void AMainPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{Super::SetupPlayerInputComponent(PlayerInputComponent);//檢查PlayerInputComponent指針,check函數只能在這使用check(PlayerInputComponent);//綁定跳躍軸映射事件PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AMainPlayer::Jump);//按下空格PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);//抬起空格PlayerInputComponent->BindAction("Sprint", IE_Pressed, this, &AMainPlayer::LeftShiftDown);//按下shiftPlayerInputComponent->BindAction("Sprint", IE_Released, this, &AMainPlayer::LeftShiftUp);//抬起shift//拾取劍PlayerInputComponent->BindAction("Interact", IE_Pressed, this, &AMainPlayer::InteractKeyDown);//按下F//攻擊PlayerInputComponent->BindAction("Attack", IE_Pressed, this, &AMainPlayer::AttackKeyDown);PlayerInputComponent->BindAction("Attack", IE_Released, this, &AMainPlayer::AttackKeyUp);//綁定移動軸映射事件PlayerInputComponent->BindAxis("MoveForward", this, &AMainPlayer::MoveForward);PlayerInputComponent->BindAxis("MoveRight", this, &AMainPlayer::MoveRight);//綁定Controller控制器去管理視角旋轉PlayerInputComponent->BindAxis("Turn", this, &AMainPlayer::Turn);PlayerInputComponent->BindAxis("LookUp", this, &AMainPlayer::LookUp);//綁定鍵盤鼠標軸映射事件PlayerInputComponent->BindAxis("TurnRate", this, &AMainPlayer::TurnRate);PlayerInputComponent->BindAxis("LookUpRate", this, &AMainPlayer::LookUpRate);}void AMainPlayer::Jump()
{//繼承父類的方法if (IsAlive()){Super::Jump();}}void AMainPlayer::MoveForward(float value)
{if (Controller != nullptr && value != 0.f && !(bIsAttacking) && IsAlive()){//獲取到Control旋轉FRotator Rotation = Controller->GetControlRotation();//轉向只關注水平Yaw方向,因此置0防止影響FRotator YowRotation = FRotator(0.0f, Rotation.Yaw, 0.0f);//獲取相機(鼠標控制器的朝向),并且朝這個軸的方向移動FVector Direction = FRotationMatrix(YowRotation).GetUnitAxis(EAxis::X);AddMovementInput(Direction, value);}}void AMainPlayer::MoveRight(float value)
{if (Controller != nullptr && value != 0.f && !(bIsAttacking) && IsAlive()){//獲取到Controller旋轉FRotator Rotation = Controller->GetControlRotation();//轉向只關注水平Yaw方向,因此置0防止影響FRotator YowRotation = FRotator(0.0f, Rotation.Yaw, 0.0f);//獲取相機(鼠標控制器的朝向),并且朝這個軸的方向移動FVector Direction = FRotationMatrix(YowRotation).GetUnitAxis(EAxis::Y);AddMovementInput(Direction, value);}
}void AMainPlayer::Turn(float Value)
{if (Value != 0.f && IsAlive()){AddControllerYawInput(Value);}}void AMainPlayer::LookUp(float Value)
{//UE_LOG(LogTemp, Warning, TEXT("%f"), GetControlRotation().Pitch);if (IsAlive()){//控制視角if (GetControlRotation().Pitch < 270.f && GetControlRotation().Pitch >180.f && Value > 0.f){return;}else if (GetControlRotation().Pitch < 180.f && GetControlRotation().Pitch >45.f && Value < 0.f){return;}AddControllerPitchInput(Value);}
}void AMainPlayer::TurnRate(float Rate)
{//要乘以一個DeltaTime這樣就可以避免高幀底幀差值問題float Value = Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds();if (Value != 0.f && IsAlive()){AddControllerYawInput(Value);}
}void AMainPlayer::LookUpRate(float Rate)
{//要乘以一個DeltaTime這樣就可以避免高幀底幀差值問題if (IsAlive()){float Value = Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds();//控制視角if (GetControlRotation().Pitch < 270.f && GetControlRotation().Pitch >180.f && Value > 0.f){return;}else if (GetControlRotation().Pitch < 180.f && GetControlRotation().Pitch >45.f && Value < 0.f){return;}AddControllerPitchInput(Value);}}void AMainPlayer::AddHealth(float value)
{Health = FMath::Clamp(Health + value, 0.f, MaxHealth);
}void AMainPlayer::AddStamina(float value)
{Stamina = FMath::Clamp(Stamina + value, 0.f, MaxStamina);
}void AMainPlayer::AddCoin(float value)
{Coins += value;
}float AMainPlayer::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{if (Health - Damage <= 0.f){Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth);//TODO Die();}else{Health -= Damage;}return Health;
}void AMainPlayer::SetMovementStatus(EPlayerMovementStatus Status)
{if (IsAlive()){MovementStatus = Status;//切換狀態的時候改變移動速度switch (MovementStatus){case EPlayerMovementStatus::EPMS_Sprinting:GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;break;default:GetCharacterMovement()->MaxWalkSpeed = RunningSpeed;break;}}
}void AMainPlayer::InteractKeyDown()
{if (OverlapWeapon && IsAlive()){if (EquipWeapon){//交換武器EquipWeapon->UnEuip(this);OverlapWeapon->Equip(this);}else{//裝備武器OverlapWeapon->Equip(this);}}else{if (EquipWeapon){//卸載武器EquipWeapon->UnEuip(this);}}
}void AMainPlayer::AttackKeyDown()
{if (IsAlive()){bAttackKeyDown = true;if (bIsWeapon){AttackBegin();}}}void AMainPlayer::AttackBegin()
{if (!bIsAttacking && IsAlive()){bIsAttacking = true;bInterpToEnemy = true;//拿到動畫UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();if (AnimInstance && AttackMontage){float PlayRate = FMath::RandRange(1.25f, 1.75f);FString SectionName = FString::FromInt(FMath::RandRange(1, 2));//指定片段播放AnimInstance->Montage_Play(AttackMontage, PlayRate);AnimInstance->Montage_JumpToSection(FName(*SectionName), AttackMontage);}}
}void AMainPlayer::AttackEnd()
{bIsAttacking = false;bInterpToEnemy = false;//形成閉環if (bAttackKeyDown && IsAlive()){AttackKeyDown();}
}void AMainPlayer::UpdataAttackTarget()
{TArray<AActor*> OVerlappingActors;GetOverlappingActors(OVerlappingActors,EnemyFilter);//判斷列表里面是否為空,為空就無攻擊目標if (OVerlappingActors.Num() == 0){AttackTarget = nullptr;return;}ABaseEnemy* ClosestDistance = nullptr;float MinDistance = 1000.f;FVector Loation = GetActorLocation();for (auto Actor : OVerlappingActors){ABaseEnemy* Enemy = Cast<ABaseEnemy>(Actor);if (Enemy && Enemy->EnemyMovementStatus != EEnemyMovementStatus::EEMS_Dead){float DistanceToActor = (Enemy->GetActorLocation() - Loation).Size();//記錄當前位置Enemy距離MainPlayer位置if (DistanceToActor < MinDistance){MinDistance = DistanceToActor;ClosestDistance = Enemy;}}}AttackTarget = ClosestDistance;
}void AMainPlayer::Die()
{SetMovementStatus(EPlayerMovementStatus::EPMS_Dead);if (EquipWeapon){EquipWeapon->DeactiveAttackCollision();EquipWeapon->DeactiveDisplayMeshCollision();}
}void AMainPlayer::DeathEnd()
{
}

限制敵人死亡后的一切活動狀態

  • 基本跟限制玩家差不多,只不過有些位置得多一個玩家是否存活的判斷

BaseEnemy.cpp

// Fill out your copyright notice in the Description page of Project Settings.#include "BaseEnemy.h"
#include "Components/SphereComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/CapsuleComponent.h"
#include "AIController.h"
#include "Characters/Player/MainPlayer.h"
#include "Animation/AnimInstance.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Components/WidgetComponent.h"
#include "Blueprint/UserWidget.h"
#include "Components/ProgressBar.h"
#include "Components/BoxComponent.h"
#include "Sound/SoundCue.h"
#include "Engine/SkeletalMeshSocket.h"
#include "Characters/Player/MainPlayer.h"
// Sets default values
ABaseEnemy::ABaseEnemy()
{// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;ChaseVolume = CreateDefaultSubobject<USphereComponent>(TEXT("ChaseVolume"));ChaseVolume->SetupAttachment(GetRootComponent());ChaseVolume->InitSphereRadius(800.f);ChaseVolume->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);ChaseVolume->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);ChaseVolume->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);AttackVolume = CreateDefaultSubobject<USphereComponent>(TEXT("AttackVolume"));AttackVolume->SetupAttachment(GetRootComponent());AttackVolume->InitSphereRadius(100.f);AttackVolume->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);AttackVolume->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);AttackVolume->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);HealthBarWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("HealthBarWidgetComponent"));HealthBarWidgetComponent->SetupAttachment(GetRootComponent());HealthBarWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);HealthBarWidgetComponent->SetDrawSize(FVector2D(125.f, 10.f));HealthBarWidgetComponent->SetWorldLocation(FVector(0.f, 0.f, 50.f));LeftAttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("LeftAttackCollision"));LeftAttackCollision->SetupAttachment(GetMesh(), "LeftAttackSocket");DeactiveLeftAttackCollision();RightAttackCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("RightAttackCollision"));RightAttackCollision->SetupAttachment(GetMesh(), "RightAttackSocket");DeactiveRightAttackCollision();//避免攝像機被敵人給阻擋GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);//設置持有屬性AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;//初始化默認移動狀態EnemyMovementStatus = EEnemyMovementStatus::EEMS_Idle;InterpSpeed = 15.f;bInterpToPlayer = false;MaxHealth = 100.f;Health = MaxHealth;Damage = 5.f;
}// Called when the game starts or when spawned
void ABaseEnemy::BeginPlay()
{Super::BeginPlay();ChaseVolume->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnChaseVolumeOverlapBegin);ChaseVolume->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnChaseVolumeOverlapEnd);AttackVolume->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnAttackVolumeOverlapBegin);AttackVolume->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnAttackVolumeOverlapEnd);LeftAttackCollision->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnLeftAttackCollisionOverlapBegin);LeftAttackCollision->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnLeftAttackCollisionOverlapEnd);RightAttackCollision->OnComponentBeginOverlap.AddDynamic(this, &ABaseEnemy::OnRightAttackCollisionOverlapBegin);RightAttackCollision->OnComponentEndOverlap.AddDynamic(this, &ABaseEnemy::OnRightAttackCollisionOverlapEnd);//獲取到HealBar小組件HealthBar = Cast<UProgressBar>(HealthBarWidgetComponent->GetUserWidgetObject()->GetWidgetFromName("HealthBar"));HealthBar->SetPercent(Health / MaxHealth);HealthBar->SetVisibility(ESlateVisibility::Hidden);//一開始不顯示血條//拿到ControllerAIController = Cast<AAIController>(GetController());
}// Called every frame
void ABaseEnemy::Tick(float DeltaTime)
{Super::Tick(DeltaTime);if (bInterpToPlayer && HasValidTarget() && IsAlive()){FRotator LookYaw(0.f, UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), UGameplayStatics::GetPlayerPawn(this, 0)->GetActorLocation()).Yaw, 0.f);FRotator InterpRotation = FMath::RInterpTo(GetActorRotation(), LookYaw, DeltaTime, InterpSpeed);SetActorRotation(InterpRotation);}
}// Called to bind functionality to input
void ABaseEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{Super::SetupPlayerInputComponent(PlayerInputComponent);}void ABaseEnemy::OnChaseVolumeOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor && IsAlive()){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){//主角進入追逐范圍顯示血條HealthBar->SetVisibility(ESlateVisibility::Visible);MoveToTarget(Player);}}
}void ABaseEnemy::OnChaseVolumeOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{if (OtherActor && IsAlive()){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (AIController){//主角出追逐范圍不顯示血條HealthBar->SetVisibility(ESlateVisibility::Hidden);//停止移動AIController->StopMovement();}}}
}void ABaseEnemy::OnAttackVolumeOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor && IsAlive()){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){Player->UpdataAttackTarget();bAttackVolumeOverlap = true;AttackBegin();}}
}void ABaseEnemy::OnAttackVolumeOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{if (OtherActor && IsAlive()){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){bAttackVolumeOverlap = false;if (EnemyMovementStatus!=EEnemyMovementStatus::EEMS_Attacking){MoveToTarget(Player);}}}
}void ABaseEnemy::MoveToTarget(AMainPlayer* Player)
{if (IsAlive()){EnemyMovementStatus = EEnemyMovementStatus::EEMS_MoveToTarget;if (AIController){FAIMoveRequest MoveRequest;MoveRequest.SetGoalActor(Player);//設置移動請求目標MoveRequest.SetAcceptanceRadius(10.f);	//設置移動半徑FNavPathSharedPtr NavPath;//會返回路徑AIController->MoveTo(MoveRequest, &NavPath);}}
}void ABaseEnemy::AttackBegin()
{if (HasValidTarget() && IsAlive()){//攻擊中關閉移動if (AIController){AIController->StopMovement();}if (EnemyMovementStatus != EEnemyMovementStatus::EEMS_Attacking){EnemyMovementStatus = EEnemyMovementStatus::EEMS_Attacking;bInterpToPlayer = true;UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();if (AnimInstance && AttackMontage){float PlayRate = FMath::RandRange(0.9f, 1.1f);FString SectionName = FString::FromInt(FMath::RandRange(1, 3));AnimInstance->Montage_Play(AttackMontage, PlayRate);AnimInstance->Montage_JumpToSection(FName(*SectionName), AttackMontage);}}}
}void ABaseEnemy::AttackEnd()
{bInterpToPlayer = false;if (HasValidTarget() && IsAlive()){EnemyMovementStatus = EEnemyMovementStatus::EEMS_Idle;if (bAttackVolumeOverlap && HasValidTarget() && IsAlive()){AttackBegin();}}
}void ABaseEnemy::OnLeftAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor && IsAlive()){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (Player->HitPaticles){const USkeletalMeshSocket* AttackScoket = GetMesh()->GetSocketByName("LeftAttackSocket");if (AttackScoket){FVector SocketLocation = AttackScoket->GetSocketLocation(GetMesh());UGameplayStatics::SpawnEmitterAtLocation(this, Player->HitPaticles, SocketLocation, FRotator(0.f), true);}if (Player->HitSound){UGameplayStatics::PlaySound2D(this, Player->HitSound);}if (DamageTyClass){UGameplayStatics::ApplyDamage(Player, Damage, AIController, this, DamageTyClass);}}}}
}void ABaseEnemy::OnLeftAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}void ABaseEnemy::OnRightAttackCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{if (OtherActor && IsAlive()){AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);if (Player){if (Player->HitPaticles){const USkeletalMeshSocket* AttackScoket = GetMesh()->GetSocketByName("RightAttackSocket");if (AttackScoket){FVector SocketLocation = AttackScoket->GetSocketLocation(GetMesh());UGameplayStatics::SpawnEmitterAtLocation(this, Player->HitPaticles, SocketLocation, FRotator(0.f), true);}if (Player->HitSound){UGameplayStatics::PlaySound2D(this, Player->HitSound);}if (DamageTyClass){UGameplayStatics::ApplyDamage(Player, Damage/2, AIController, this, DamageTyClass);}}}}
}void ABaseEnemy::OnRightAttackCollisionOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}void ABaseEnemy::ActiveLeftAttackCollision()
{LeftAttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);LeftAttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);LeftAttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);LeftAttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}void ABaseEnemy::DeactiveLeftAttackCollision()
{LeftAttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}void ABaseEnemy::ActiveRightAttackCollision()
{RightAttackCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);RightAttackCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);RightAttackCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);RightAttackCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}void ABaseEnemy::DeactiveRightAttackCollision()
{RightAttackCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}float ABaseEnemy::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{if (Health - Damage <= 0.f){Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth);Die();}else{Health -= Damage;}HealthBar->SetPercent(Health / MaxHealth);//更新UI血條return Health;
}void ABaseEnemy::Die()
{EnemyMovementStatus = EEnemyMovementStatus::EEMS_Dead;DeactiveLeftAttackCollision();DeactiveRightAttackCollision();ChaseVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);AttackVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);//敵人死亡,主角要更新攻擊目標Cast<AMainPlayer>(UGameplayStatics::GetPlayerPawn(this, 0))->UpdataAttackTarget();
}void ABaseEnemy::DeathEnd()
{
}bool ABaseEnemy::HasValidTarget()
{return Cast<AMainPlayer>(UGameplayStatics::GetPlayerPawn(this, 0))->MovementStatus != EPlayerMovementStatus::EPMS_Dead;
}

主角死亡與敵人死亡后延時銷毀

  • 首先設置敵人血條在敵人死亡后就隱藏
void ABaseEnemy::Die()
{EnemyMovementStatus = EEnemyMovementStatus::EEMS_Dead;HealthBar->SetVisibility(ESlateVisibility::Hidden);DeactiveLeftAttackCollision();DeactiveRightAttackCollision();ChaseVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);AttackVolume->SetCollisionEnabled(ECollisionEnabled::NoCollision);GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);//敵人死亡,主角要更新攻擊目標Cast<AMainPlayer>(UGameplayStatics::GetPlayerPawn(this, 0))->UpdataAttackTarget();
}
  • 然后去主角的死亡動畫中添加DeathEnd的通知,并在動畫藍圖中調用
    在這里插入圖片描述
    在這里插入圖片描述
  • 編寫DeathEnd死亡邏輯,首先關閉動畫,添加定時器,玩家死后一秒重啟游戲
void AMainPlayer::DeathEnd()
{GetMesh()->bPauseAnims = true;GetMesh()->bNoSkeletonUpdate = true;FTimerHandle DeathTimerHandle;auto Lambda = [](){//TODO RestartLevel();};GetWorldTimerManager().SetTimer(DeathTimerHandle, FTimerDelegate::CreateLambda(Lambda), 1.0, false);
}
  • 敵人這邊也是一樣添加通知動畫藍圖中調用,然后編寫死亡消失邏輯
    在這里插入圖片描述
    在這里插入圖片描述
  • 死亡一秒后銷毀
void ABaseEnemy::DeathEnd()
{GetMesh()->bPauseAnims = true;GetMesh()->bNoSkeletonUpdate = true;FTimerHandle DeathTimerHandle;auto Lambda = [this](){Destroy();};GetWorldTimerManager().SetTimer(DeathTimerHandle, FTimerDelegate::CreateLambda(Lambda), 1.0, false);
}

玩家死亡重新開始游戲

  • 將上面遺留的RestartLevel函數進行新建然后編寫,用UGameplayStatics中的方法進行重新開始游戲
void AMainPlayer::DeathEnd()
{GetMesh()->bPauseAnims = true;GetMesh()->bNoSkeletonUpdate = true;FTimerHandle DeathTimerHandle;auto Lambda = [this](){RestartLevel();};GetWorldTimerManager().SetTimer(DeathTimerHandle, FTimerDelegate::CreateLambda(Lambda), 1.0, false);}void AMainPlayer::RestartLevel()
{FString LevelName = UGameplayStatics::GetCurrentLevelName(this);UGameplayStatics::OpenLevel(this, FName(*LevelName));
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/163312.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/163312.shtml
英文地址,請注明出處:http://en.pswp.cn/news/163312.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TikTok歷史探秘:短視頻中的時間之旅

在數字時代的浪潮中&#xff0c;TikTok嶄露頭角&#xff0c;成為社交媒體領域的一顆耀眼新星。這款短視頻應用以其獨特的創意、時尚和娛樂性質&#xff0c;吸引了全球數以億計的用戶。 然而&#xff0c;TikTok并非一夜之間的奇跡&#xff0c;它背后蘊藏著豐富而有趣的歷史故事…

[ChatGPT]ChatGPT免費,不用翻墻!?——你需要的裝備

系列文章目錄 【AIGC】服務于人類&#xff5c;一種新的人工智能技術-CSDN博客 文章目錄 目錄 系列文章目錄 文章目錄 前言 一、天意云網站 ?編輯 二、使用步驟 可以看到有云服務器、Rstudio以及我們的ChatGPT&#xff0c;我這次主要分享ChatGPT&#xff0c;其他的有機會我再給…

常用服務注冊中心與發現(Eurake、zookeeper、Nacos)筆記(一)基礎概念

基礎概念 注冊中心 在服務治理框架中&#xff0c;通常都會構建一個注冊中心&#xff0c;每個服務單元向注冊中心登記自己提供的服務&#xff0c;將主機與端口號、版本號、通信協議等一些附加信息告知注冊中心&#xff0c;注冊中心按照服務名分類組織服務清單&#xff0c;服務…

電力感知邊緣計算網關產品設計方案-軟件架構(業務流程)

軟件架構(業務流程) 基于前端系統提供的硬件通信平臺,后端系統以控制執行單元為核心,協同控制通信管理、驅動適配、存儲單元等職能單元完成與前端系統的通信數據交互業務,在經歷以下業務流程后,完成設備自適應通信業務功能。 1.外部設備通信前端系統 前端系統連接新的…

OpenAI

OpenAI&#xff0c;在美國成立的人工智能研究公司&#xff0c;核心宗旨在于“實現安全的通用人工智能(AGI)”&#xff0c;使其有益于人類。 OpenAI于2015年由一群科技領袖&#xff0c;包括山姆阿爾特曼&#xff08;Sam Altman&#xff09;、彼得泰爾&#xff08;Peter Thiel&a…

設計師不能忽視的幾個寶藏圖標設計工具

在這個快速變化的時代&#xff0c;設計師對創新和實用工具的需求越來越大。這就要求我們及時跟上潮流&#xff0c;不斷探索和嘗試最新、最有價值的圖標設計工具。只有這樣&#xff0c;我們才能在競爭激烈的設計市場中脫穎而出。以下是我們精心挑選的2024年值得一試的圖標設計工…

C語言實現求n以內最大的k個素數c

以下是C語言實現求n以內最大的k個素數的代碼&#xff1a; #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <math.h>bool is_prime(int num) { // 判斷素數int i;if(num < 2) {return false;}for(i 2; i < sqrt(num); …

服務器安全如何保障

主機安全是指保護計算機主機&#xff08;也稱為服務器、終端或主機設備&#xff09;免受潛在的安全威脅和攻擊的一系列措施和實踐。主機安全旨在防止未經授權的訪問、數據泄露、惡意軟件感染和其他安全漏洞的利用&#xff0c;主機一旦被黑客入侵&#xff0c;企業會面臨很多安全…

相比其他關系型數據庫,AntDB JDBC驅動特性有哪些不同之處

摘要&#xff1a;使用Java語言進行各類應用程序的快速開發成為目前比較主要且流行的開發方式。JDBC是 Java 語言中用來連接和操作關系型數據庫的 API&#xff0c;在業務程序與關系型數據庫通信時&#xff0c;必然會使用JDBC驅動。 本文將通過國產關系型數據庫AntDB中的JDBC為大…

【Effective C++】 (六) 繼承與面向對象設計

【六】繼承與面向對象設計 條款32 &#xff1a; 確保public繼承是"is a"的關系 Item 32: Make sure public inheritance models “is-a”. C面向對象程序設計中&#xff0c;最重要的規則便是&#xff1a;public繼承應當是"is-a"的關系。當Derived public繼…

3.1.2 Linux時間子系統 hrtimer示例使用

文章目錄 結構體定義接口初始化啟動修改取消示例示例1示例2示例3結構體定義 struct hrtimer {struct timerqueue_node node;ktime_t _softexpires;enum hrtimer_restart

生成目錄結構圖 tree命令

tree /f >info.txt tree命令可用于生成漂亮的目錄結構圖&#xff0c;在此之前&#xff0c;我一直以為是手打的…… .| index.html|\---static---css| bar.css| map.css| \---js

jQuery創建、插入、刪除對象

jQuery庫中的一些操作元素的方法 創建元素&#xff1a; $(htmlString)&#xff1a;這個構造器可以用來創建元素&#xff0c;其中htmlString是一個包含HTML標記的字符串。例如&#xff0c;$(<p>Hello, World!</p>)會創建一個<p>元素對象&#xff1b;$("&…

【uniapp】部分圖標點擊事件無反應

比如&#xff1a;點擊這個圖標在h5都正常&#xff0c;在小程序上無反應 css&#xff1a;也設置z-index&#xff0c;padding 頁面上也試過click.native.stop.prevent"changePassword()" 時而可以時而不行&#xff0c; 最后發現是手機里輸入鍵盤的原因&#xff0c;輸…

大型養殖場需要哪些污水處理設備

大型養殖場是一個涉及環境保護和可持續發展的關鍵行業&#xff0c;對于處理養殖場產生的污水有著明確的要求和標準。為了確保污水得到有效處理和處理效果達到國家排放標準&#xff0c;大型養殖場需要配備一系列污水處理設備。以下是幾種常見的污水處理設備&#xff1a; 1. 水解…

Python入門指南之基本概率和語法基礎

文章目錄 一、基本概念二、控制流三、函數四、模塊五、數據結構六、面向對象的編程七、輸入輸出八、異常九、Python標準庫關于Python技術儲備一、Python所有方向的學習路線二、Python基礎學習視頻三、精品Python學習書籍四、Python工具包項目源碼合集①Python工具包②Python實戰…

快速排序演示和代碼介紹

快速排序的核心是(以升序為例)&#xff1a;在待排序的數據中指定一個數做為基準數&#xff0c;把所有小于基準數的數據放到基準數的左邊&#xff0c;所有大于基準數的數據放在右邊&#xff0c;這樣的話基準數的位置就確定了&#xff0c;然后在兩邊的數據中重復上述操作

2023亞太地區數學建模B題思路分析+模型+代碼+論文

目錄 2023亞太地區數學建模A題思路&#xff1a;開賽后第一時間更新&#xff0c;獲取見文末名片 2023亞太地區數學建模B題思路&#xff1a;開賽后第一時間更新&#xff0c;獲取見文末名片 2023亞太地區數學建模C題思路&#xff1a;開賽后第一時間更新&#xff0c;獲取見文末名…

使用 Pinia 的五個技巧

在這篇文章中&#xff0c;想與大家分享使用 Pinia 的五大技巧。 以下是簡要總結&#xff1a; 不要創建無用的 getter在 Option Stores 中使用組合式函數&#xff08;composables&#xff09;對于復雜的組合式函數&#xff0c;使用 Setup Stores使用 Setup Stores 注入全局變量…

基于Python的新浪微博爬蟲程序設計與實現

完整下載&#xff1a;基于Python的新浪微博爬蟲程序設計與實現.docx 基于Python的新浪微博爬蟲程序設計與實現 Design and Implementation of a Python-based Weibo Web Crawler Program 目錄 目錄 2 摘要 3 關鍵詞 4 第一章 引言 4 1.1 研究背景 4 1.2 研究目的 5 1.3 研究意義…