근거리 AI를 기반으로 원거리공격을하는 AI를 만들어보겠습니다
근거리공격 AI의 객체부터 만들어주겠습니다
BasicSkeleton을 기반으로 블루프린트를 복사해 추가해주고 무기를 들려주었습니다
사용할 슈팅 애니메이션을 Mixamo에서 가져온뒤 리타게팅해주겠습니다
공격하는 BehaviorTree에서 Service노드를 조건을 바꿔주겠습니다
원래의 코드입니다
기본코드는 170의 거리기준으로 공격을 하기떄문에 이 170값을
변수로 선언하고 property값으로 변동할수있도록 해주겠습니다
변수로 선언하며 Property로 수정 가능하게 추가
조건을 attackDistance값으로 변동하였습니다
원래있던 BT를 복사후 원거리 AI에게만 적용될 BT의 Attack Distance값을 170에서 700으로 바꿔주었습니다
또한 AI의 Controller 블루프린트도 복사해서 바꾼 BT를 할당시켜주었습니다
Perception의 인지범위를 넓게 설정해주었습니다
원거리AI가 사용하는 AI Controller를 변동해주었습니다
또한 이 에셋의 메테리얼도 Dissolve를 사용할수있게 Node를 추가해주었습니다
하는법은 이전글에 작성해두었으니 궁금하신분은 참고하시길바랍니다
2025.05.08 - [Unreal5 프로젝트 다이어리] - Unreal - Dissolve Material
Unreal - Dissolve Material
Behavior Tree의 AI가 죽었을때 애니메이션이 나오면서 사라지게하는것도 나쁘지않지만갑자기 사라져버리는것보다는 점점 사라지면서 마지막에는 없어지는방식으로 표현한다면 조금더 자연스러
lucodev.tistory.com
그리고 공격 애니메이션을 슈팅 애니메이션으로 바꿔주었습니다
결과물
그럼 크로스보우로 원거리 공격할때 투사체를 추가해주겠습니다
투사체가 나가는 위치는 크로스보우의 입구에서
투사체나가는 함수의 호출은 원거리공격 애니메이션 노티파이에서 불러오겠습니다
보통 원거리공격은 두가지의 피격판정을 가집니다
한개는 ProjectTile과 HitScan입니다
HitScan이란 에임안에 적이 들어와있으면 무조건 맞는 방식
ProjectTile이란 물리법칙을 가지고있는 투사체가 발사되는 방식입니다
예를들면 오버워치의 리퍼의 공격방식을 HitScan이라고합니다
솔져같은 경우 프로젝트타일의 총알방식을 사용한 예시입니다
투사체는 ProjectTile로 생성하겠습니다
.h입니다
UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UPROPERTY(EditAnywhere, Category = "MySettings")
USceneComponent* sceneComp;
UPROPERTY(EditAnywhere, Category="MySettings")
UCapsuleComponent* collisionComp;
UPROPERTY(EditAnywhere, Category = "MySettings")
USkeletalMeshComponent* projectTileComp;
UPROPERTY(EditAnywhere, Category = "MySettings")
UProjectileMovementComponent* projectileMovement;
class ASwordCharacter* swordPlayer;
};
.cpp입니다
ABowSkeletonProjectTile::ABowSkeletonProjectTile()
{
PrimaryActorTick.bCanEverTick = true;
//Scene
sceneComp = CreateDefaultSubobject<USceneComponent>(TEXT("sceneComp"));
RootComponent = sceneComp;
//Collision
collisionComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("SphereComponent"));
collisionComp->SetCollisionProfileName(TEXT("BlockAll"));
collisionComp->SetupAttachment(RootComponent);
//SkeletalMeshComp
projectTileComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("arrowMesh"));
projectTileComp->SetupAttachment(RootComponent);
projectTileComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
//ProjectTile
projectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("projectTileMevement"));
projectileMovement->InitialSpeed = 2500.f;
projectileMovement->MaxSpeed = 2500.f;
projectileMovement->bRotationFollowsVelocity = true;
projectileMovement->ProjectileGravityScale = 0.3f;
}
void ABowSkeletonProjectTile::BeginPlay()
{
Super::BeginPlay();
collisionComp->OnComponentBeginOverlap.AddDynamic(this, &ABowSkeletonProjectTile::OnBeginOverlap);
}
void ABowSkeletonProjectTile::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
FString hitActorName = OtherActor->GetName();
if (hitActorName.Contains(TEXT("SwordCharacter")))
{
swordPlayer = Cast<ASwordCharacter>(OtherActor);
swordPlayer->playerCurrentHp -= 500;
Destroy();
}
}
화살을 할당해주어 ProjectTile을 설정하였습니다
이 projecttile은 0.3의 중력을 가지고있으며 2500의 속도로 움직입니다
만약 플레이어와 오버랩될시 Destroy됩니다
오버랩될시의 추후의 작업은 마지막에서 하겠습니다
잘 이동하는것을 확인할수있습니다
오버랩 이벤트도 문제없이 되는것을 확인할수있습니다
발사 위치를 지정해주겠습니다
발사 위치는 WeaponSocket에서 발사되겠습니다
그럼 스폰함수를 만들겠습니다
사용할 함수 그리고 제가만든 ABowSkeletonProjectTile을 참조할 arrowProjectTile변수를 선언
UFUNCTION()
void SpawnArrow();
UPROPERTY(EditAnywhere, Category="MySettings")
TSubclassOf<ABowSkeletonProjectTile> arrowProjectTile;
프로퍼티로 블루프린트를 할당해줍니다
이미 모션워핑으로플레이어의 위치를 찾을수있으니 FindWarpTarget으로 위치를 불러온다음
WeaponSocket에서 target의 위치방향으로 스폰하면되겠습니다
타겟에서 스폰위치를 빼면 방향이 나옵니다
void ABasicSkeletonEnemy::SpawnArrow()
{
FVector spawnLoc = GetMesh()->GetSocketLocation(TEXT("WeaponSocket"));
if (motionWarpComponent)
{
const FMotionWarpingTarget* target = motionWarpComponent->FindWarpTarget(TEXT("PlayerTarget"));
if (target)
{
FVector targetLoc = target->GetLocation();
FVector direction = targetLoc - spawnLoc;
FRotator spawnRot = direction.Rotation();
FActorSpawnParameters spawnParams;
spawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ABowSkeletonProjectTile* SpawnedArrow = GetWorld()->SpawnActor<ABowSkeletonProjectTile>(arrowProjectTile,spawnLoc,spawnRot,spawnParams);
if (SpawnedArrow)
{
SpawnedArrow->SetLifeSpan(5.0f);
}
}
}
}
AI의 애님인스턴스에서 해당 함수를 호출할 노티파이를 만들어줍니다
void USkeletonEnemyAnimInstance::AnimNotify_SpawnArrow()
{
skeletonEnemy->SpawnArrow();
}
노티파이를 찍어주면
최종결과물
'Unreal5 프로젝트 다이어리' 카테고리의 다른 글
Uneal - EQS Strafe이동 (0) | 2025.05.10 |
---|---|
Unreal - AI에게 HP 프로그래스바 붙히기 (0) | 2025.05.09 |
Unreal - Behavior Tree(5) 스폰 / 디스폰 (0) | 2025.05.08 |
Unreal - Dissolve Material (0) | 2025.05.08 |
Unreal - Behavior Tree(4) AI Perception Team ID (1) | 2025.05.04 |