애니메이션 재생 방식
1. 클라이언트에서 PlayerController를 통해 EnhancedInput을 받으면
2. 서버 RPC로 요청 전송
PlayerController.cpp
....
void ASW_PlayerController::ComboAttack(const FInputActionValue& InputValue)
{
if (ASW_CharacterBase* PlayerCharacter = Cast<ASW_CharacterBase>(GetPawn()))
{
if (!PlayerCharacter->bIsLocked)
{
PlayerCharacter->Server_PlaySkill("ComboAttack");
}
}
}
void ASW_PlayerController::JumpAttack(const FInputActionValue& InputValue)
{
if (ASW_CharacterBase* PlayerCharacter = Cast<ASW_CharacterBase>(GetPawn()))
{
// 점프중일때만 실행되도록
if (!PlayerCharacter->bIsLocked && PlayerCharacter->GetCharacterMovement()->IsFalling())
{
PlayerCharacter->Server_PlaySkill("JumpAttack");
}
}
}
void ASW_PlayerController::NormalSkill(const FInputActionValue& InputValue)
{
if (ASW_CharacterBase* PlayerCharacter = Cast<ASW_CharacterBase>(GetPawn()))
{
if (!PlayerCharacter->bIsLocked)
{
PlayerCharacter->Server_PlaySkill("NormalSkill");
}
}
}
void ASW_PlayerController::SpecialSkill(const FInputActionValue& InputValue)
{
if (ASW_CharacterBase* PlayerCharacter = Cast<ASW_CharacterBase>(GetPawn()))
{
if (!PlayerCharacter->bIsLocked)
{
PlayerCharacter->Server_PlaySkill("SpecialSkill");
}
}
}
void ASW_PlayerController::DashSkill(const FInputActionValue& InputValue)
{
if (ASW_CharacterBase* PlayerCharacter = Cast<ASW_CharacterBase>(GetPawn()))
{
if (!PlayerCharacter->bIsLocked)
{
PlayerCharacter->Server_PlaySkill("DashSkill");
}
}
}
Server_PlaySkill(SkillName)을 통해 서버 RPC로 요청을 한다.
3. 서버에서 애니메이션 재생 + Multicast 전송
Multicast_Server_PlaySkill(FName SkillName)을 통해 서버에서 애니메이션이 재생되도록함
각 스킬에 맞춰서CharacterBase를 상속받은 캐릭터에 스킬 애니메이션이 재생 됨
ComboAttack은 왜 따로 Multicast_ComboAttack()함수를 사용하는가
콤보 어택같은 경우는 다른 스킬들과 구조가 다른데 콤보 어택은 내부적인 상태 변수(CurrentComboIndex, bPendingNextCombo, bCanNextCombo)를 사용해서 연속 공격을 처리하는 방식이다.
그렇기 때문에 클라이언트한테 "함수 자체"를 실행시켜야한다.
그래서 따로 Multicast_ComboAttack()함수를 사용하게 된다.
아래와 똑같이 서버가 아니면 스킬 애니메이션이 재생된다.
4. 클라이언트에서 애니메이션이 재생되도록 멀티캐스트
멀티캐스트로 서버가 아니면 스킬애니메이션이 재생된다.
데미지 처리 방식
1. ApplyDamage노티파를 통해 애니메이션이 ApplyDmaage노티파이를 만나면
Server_ApplySkillDamage(FName SkillName)함수를 호출해서 각 캐릭터에 스킬이름마다 정해진 콜리전과 데미지를 줌
2. 서버에서 대상을 탐지 후 데미지를 계산
SkillsAppliedThisFrame.Contains(SkillName)은 프레임마다 같은 스킬이 두 번 이상 호출되지 않도록 필터링을 해주는 것으로
ApplyDamage노티파이가 서버 + Multicast 애니메이션에서 두 번 발생할 수 있어서 막기위해 필요
SkillsAppliedThisFrame.Add(SkillName)은 중복 방지용으로 한 번 처리된 스킬 이름을 저장한다. 그리고 이 변수를 Tick()함수에서 매 프레임마다 초기화함
ApplySkillDamage(FName SkillName, const TArray<AActor*>& Targets)함수는 SkillDateMap에서 해당 스킬의 데미지 배율, 범위, 타입 정보를 가져오고, 실제 데미지는 TakeDamage()로 처리한다. (데미지는 무조건 서버에서만 적용된다.)
3. 멀티캐스트로 모든 클라이언트에 브로트캐스트
데미지를 적용하는 함수는 아니고, 서버에서 데미지를 처리한 후, 클라이언트에서는 애니메이션만 보여주게 된다.
궁금증 해결
Server_PlaySkill_Implementation(FName SkillName)
Server_ApplySkillDamage_Implementation(FName SkillName)
이 두개도 똑같은 역할이고
Multicast_PlaySkill_Implementation(FName SkillName)
Multicast_ApplySkillDamage_Implementation(FName SkillName)
이 두개도 똑같은 역할로
같은건데 그냥 한개로 통일시키켜서 사용하면 되는거아닌가
결론
❌ 애니메이션 타이밍과 안 맞음 (예: 칼 휘두르기 전에 데미지 들어감)
❌ 클라에선 타격 효과/모션은 보이는데, 맞은 쪽은 데미지 적용 안 되는 경우 발생
❌ AnimNotify가 아예 의미 없어짐
예시: "Brall이 평타 콤보를 날리는 상황"
Server_PlaySkill("NormalSkill") → 스킬 시작: 애니메이션 재생
몽타주 도중 타이밍 맞춰서 AnimNotify("ApplyDamage") 발생
→ 이때 Server_ApplySkillDamage("NormalSkill") 호출
→ 진짜 데미지를 타겟에게 한 번만 적용
리플리케이션 동작 요약
항목위치 (함수/파일)설명
항목 |
위치 (위치/함수) |
원리 |
입력 처리 | ASW_PlayerController::NormalSkill() | 클라 → 서버로 입력 전달 |
서버 스킬 실행 | ASW_CharacterBase::Server_PlaySkill() | 서버에서 애니메이션 실행 |
클라 애니 재생 | Multicast_PlaySkill() | 클라에서 애니 재생 |
데미지 노티파이 | USkillAnimNotify::Notify() | 서버에서만 데미지 처리 시작 |
데미지 중복 방지 | SkillsAppliedThisFrame + Tick() | 매 프레임마다 1회로 제한 |
피격 데미지 처리 | ApplySkillDamage() + TakeDamage() | 서버에서만 체력 감소 처리 |
피격 이펙트 재생 | Multicast_ApplySkillDamage() | 클라이언트 시각적 처리 전용 |
'Unreal Engine 5 > UE5 멀티대전게임 일기' 카테고리의 다른 글
UE5 멀티대전게임 일기(12) KPT 회고 (1) | 2025.04.18 |
---|---|
UE5 멀티대전게임 일기(11) Myth, Void캐릭터 스킬 구현 현황 (0) | 2025.04.15 |
UE5 멀티대전게임 일기(9) Brall, Dubu캐릭터 스킬 구현 현황 (0) | 2025.04.08 |
UE5 멀티대전게임 일기(8) 캐릭터 스킬데미지, 콤보평타데미지 구현방식 (0) | 2025.04.07 |
UE5 멀티대전게임 일기(7) 캐릭터 TakeDamage함수 구현 (0) | 2025.04.07 |