USpellCardData.h
#pragma once
#include "CoreMinimal.h"
#include "CardData.h"
#include "SpellCardData.generated.h"
// 효과 유형을 정의 하는 열거형 입니다.
UENUM(BlueprintType)
enum class ESpellEffectType : uint8
{
Damage,
Heal,
Buff,
Debuff
};
// 타겟 유형을 정의하는 열거형 입니다.
UENUM(BlueprintType)
enum class ESpellTargetType : uint8
{
SingleEnemy,
SingleAlly,
AllEnemise,
AllAllies,
Self
};
UCLASS()
class TCG_API USpellCardData : public UCardData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spell")
FString EffectDescription; // 마법 효과 설명
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spell")
ESpellEffectType EffectType; // 효과 유형 ( 피해, 회복 등 )
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spell")
ESpellTargetType TargetType; // 타겟 유형 ( 단일 적, 전체 아군등 )
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spell")
int32 EffectAmount; // 효과의 강도 ( 피해량, 회복량 등 )
virtual void ApplyEffect_Implementation(AActor* Target) override;
protected:
void ApplyDamageEffect(AActor* Target);
void ApplyHealEffect(AActor* Target);
void ApplyBuffEffect(AActor* Target);
void ApplyDebuffEffect(AActor* Target);
};
USpellCardData.cpp
#include "SpellCardData.h"
void USpellCardData::ApplyEffect_Implementation(AActor* Target)
{
switch (EffectType)
{
case ESpellEffectType::Damage:
ApplyEffect(Target);
break;
case ESpellEffectType::Heal:
ApplyHealEffect(Target);
break;
case ESpellEffectType::Buff:
ApplyBuffEffect(Target);
break;
case ESpellEffectType::Debuff:
ApplyDebuffEffect(Target);
break;
default:
UE_LOG(LogTemp, Warning, TEXT("EffectType is not Defind"));
break;
}
}
void USpellCardData::ApplyDamageEffect(AActor* Target)
{
switch (TargetType)
{
case ESpellTargetType::SingleEnemy:
// 단일 적에게 피해를 입힌다.
if (Target)
{
// Target에 EffectAmount만큼 피해 적용
UE_LOG(LogTemp, Log, TEXT("Applying %d Damage to single enemy"), EffectAmount);
}
break;
case ESpellTargetType::AllEnemise:
// 모든 적에게 피해를 입힌다
UE_LOG(LogTemp, Log, TEXT("Applying %d Damage to all enemy"), EffectAmount);
// 모든 적을 대상으로 EffectAmount 만큼 피해 적용 로직 추가
break;
default:
UE_LOG(LogTemp, Log, TEXT("TargetType is not Compatible with Damage effect."));
break;
}
}
void USpellCardData::ApplyHealEffect(AActor* Target)
{
switch (TargetType)
{
case ESpellTargetType::SingleAlly:
// 단일 아군 회복
if (Target)
{
// Target에 EffectAmount만큼 회복 적용
UE_LOG(LogTemp, Log, TEXT("Applying %d heal to single ally"), EffectAmount);
}
break;
case ESpellTargetType::AllAllies:
// 모든 아군을 회복
UE_LOG(LogTemp, Log, TEXT("Applying %d heal to all alles"), EffectAmount);
// 모든 아군의 체력을 EffectAmount 만큼 회복 로직 추가
break;
default:
UE_LOG(LogTemp, Log, TEXT("TargetType is not Compatible with Damage effect."));
break;
}
}
void USpellCardData::ApplyBuffEffect(AActor* Target)
{
switch (TargetType)
{
case ESpellTargetType::SingleAlly:
if (Target)
{
// 단일 아군에게 버프 적용
// Target에 EffectAmount만큼 공격력 방어력 증가
UE_LOG(LogTemp, Log, TEXT("Applying buff to single ally : +%d Attack"), EffectAmount);
}
break;
case ESpellTargetType::AllAllies:
// 모든 아군에게 버프 적용
UE_LOG(LogTemp, Log, TEXT("Applying buff to all alles : +%d Attack"), EffectAmount);
// 아군 전체에 공격력 방어력 증가 로직
break;
case ESpellTargetType::Self:
// 자기 자신에게 버프 적용
UE_LOG(LogTemp, Log, TEXT("Applying buff to self : +%d Attack"), EffectAmount);
break;
default:
UE_LOG(LogTemp, Log, TEXT("TargetType is not Compatible with Damage effect."));
break;
}
}
void USpellCardData::ApplyDebuffEffect(AActor* Target)
{
switch (TargetType)
{
case ESpellTargetType::SingleEnemy:
if (Target)
{
// 단일 적에게 디버프 적용
// Target에 EffectAmount만큼 공격력 방어력 감소
UE_LOG(LogTemp, Log, TEXT("Applying Debuff to single enemy : -%d Attack"), EffectAmount);
}
break;
case ESpellTargetType::AllEnemise:
// 모든 적에게 디버프 적용
UE_LOG(LogTemp, Log, TEXT("Applying Debuff to all enemies : -%d Attack"), EffectAmount);
break;
default:
UE_LOG(LogTemp, Warning, TEXT("TargetType is not Compatible with Debuff Effect. "));
break;
}
}
SecretCardData.h
#pragma once
#include "CoreMinimal.h"
#include "CardData.h"
#include "SecretCardData.generated.h"
UENUM(BLueprintType)
enum class ESecretEffectType : uint8
{
DamageEnemyHero, // 적 영웅에게 피해
SummonMinions, // 아군 미니언 소환
CounterSpell, // 마법 카드 무효화
ProtectFriendlyHero, // 아군 영웅 보호
DamageAllEnemies // 모든 적에게 피해
};
UCLASS()
class TCG_API USecretCardData : public UCardData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Secret")
FString TriggerCondition; // 비밀 카드의 발도 조건 설명
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Secret")
FString EffectDescription; // 비밀 카드의 효과 설명
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Secret")
ESecretEffectType EffectType; // 비밀 카드 효과 유형
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Secret")
int32 EffectAmount; // 효과의 강도 ( 피해량, 소환할 미니언 수 등 )
// 비밀 발동 조건을 판별하는 함수
UFUNCTION(BlueprintCallable, Category = "Secret")
bool IsConditionMet(const FString& EventName) const;
// 비밀 발동 함수
UFUNCTION(BlueprintCallable, Category = "Secret")
void ActivateSecret(AActor* Target);
};
SecretCardData.cpp
#include "SecretCardData.h"
bool USecretCardData::IsConditionMet(const FString& EventName) const
{
// 비밀 카드의 조건을 EventName 으로 체크 ( 예 : "HeroAttacjed", "MinionSummoned"
if (EventName == "HeroAttacked" && CardName == "Explosive Trap")
{
return true;
}
if (EventName == "MinionSummoned" && CardName == "Snake Trap")
{
return true;
}
// 추가 조건을 여기에 작성
return false;
}
void USecretCardData::ActivateSecret(AActor* Target)
{
switch (EffectType)
{
case ESecretEffectType::DamageEnemyHero:
if (Target)
{
// 예시 : 적 영웅에게 EffectAmount 만큼 피해를 입힘
UE_LOG(LogTemp, Log, TEXT("Secret Activated : %s deals %d Damage to the enemy hero"), *CardName, EffectAmount);
// 예 : Target->TakeDamage(EffectAmount, ... );
}
break;
case ESecretEffectType::SummonMinions:
// 아군 미니언을 EffectAmount 수 만큼 소환
UE_LOG(LogTemp, Log, TEXT("Secret Activated : %s Summons %d minions"), *CardName, EffectAmount);
// 예 : 미니언 소환 함수 호출
break;
case ESecretEffectType::CounterSpell:
// 적의 마법 카드를 무효화
UE_LOG(LogTemp, Log, TEXT("Secret Activated : %s counter the enemy's spell"), *CardName);
// 마법 카드 무효화 로직
break;
case ESecretEffectType::ProtectFriendlyHero:
// 아군 영웅 보호 로직
UE_LOG(LogTemp, Log, TEXT("Secret Activated : %s protects the friendly hero"), *CardName);
// 보호 효과 적용
break;
case ESecretEffectType::DamageAllEnemies:
// 모든 적에게 EffectAmount 만큼 피해를 줌
UE_LOG(LogTemp, Log, TEXT("Secret Activated : %s deals %d Damage to all enemies"), *CardName, EffectAmount);
break;
default:
UE_LOG(LogTemp, Log, TEXT("Unknown Secret Effect Type for %s "), *CardName);
break;
}
UE_LOG(LogTemp, Log, TEXT("Secret Activate : %s"), *CardName);
// 비밀 카드의 고유 효과를 여기에 구현
}
SecretManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SecretManager.generated.h"
// 비밀 카드의 이벤트가 발생했을떄 실행할 델리게이트를 정의 합니다
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnHeroAttacked);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMinionSummoned);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnManaCostCardPlayed);
class USecretCardData;
UCLASS()
class TCG_API ASecretManager : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASecretManager();
// 비밀 카드 리스트
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Secret")
TArray<USecretCardData*> ActiveSecrets;
// 델리게이트 선언 ( 이벤트 기반으로 발동 )
UPROPERTY(BlueprintAssignable, Category = "Secret Events")
FOnHeroAttacked OnHeroAttacked;
UPROPERTY(BlueprintAssignable, Category = "Secret Events")
FOnMinionSummoned OnMinionSummoned;
UPROPERTY(BlueprintAssignable, Category = "Secret Events")
FOnManaCostCardPlayed OnManaCostCardPlayed;
// 비밀 카드 조건 검사 함수
void CheckAndTriggerSecret(FString EventName, AActor* Target);
};
SecretManager.cpp
#include "SecretManager.h"
#include "SecretCardData.h"
// Sets default values
ASecretManager::ASecretManager()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
void ASecretManager::CheckAndTriggerSecret(FString EventName, AActor* Target)
{
for (USecretCardData* Secret : ActiveSecrets)
{
if (Secret && Secret->IsConditionMet(EventName))
{
Secret->ActivateSecret(Target);
ActiveSecrets.Remove(Secret);
break; // 조건을 만족하는 첫 비밀 카드 발동 후 종료
}
}
}
하스스톤의 턴 구조와 다양한 Phase를 효과적으로 관리하기 위해,
- 상태 머신(State Machine)을 기반으로 게임 Phase를 단계별로 관리하는 구조 활용. 상태 머신을 활용하면 각 Phase 간의 전환과 각 Phase에 대한 조건 처리를 유연하고 구조적으로 구현가능
- 하스스톤에서의 주요 Phase는 다음과 같이 나눌 수 있으며, 각 Phase에는 특정 조건이나 이벤트가 발생할 때 실행되는 상태 전이가 존재
GameRule.h
#pragma once
#include "CoreMinimal.h"
UENUM(BlueprintType)
enum class EGamePhase : uint8
{
GameStart,
TurnStart,
CardDraw,
SummonMinion,
CastSpell,
TurnEnd,
GameEnd
};
class TCG_API GameRule
{
public:
GameRule();
~GameRule();
};
TCGGameMode.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "GameRule.h"
#include "TCGGameMode.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTurnStart);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTurnEnd);
UCLASS()
class TCG_API ATCGGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
// Phase에 대한 Getter
EGamePhase GetCurrentPhase() const;
// Phase에 대한 Setter ( 전원 로직 포함 )
void SetCurrentPhase(EGamePhase NewPhase);
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnTurnStart OnTurnStart;
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnTurnEnd OnTurnEnd;
private:
// 현재 Phase를 나타내는 변수 ( private으로 캡슐화 )
EGamePhase CurrentPhase;
void HandleGameStartPhase();
void HandleTurnStartPhase();
void HandleCardDrawPhase();
void HandleSummonMinionPhase();
void HandleCastSpellPhase();
void HandleTurnEndPhase();
void HandleGameEndPhase();
};
TCGGameMode.cpp
#include "TCGGameMode.h"
void ATCGGameMode::SetCurrentPhase(EGamePhase NewPhase)
{
if (CurrentPhase != NewPhase)
{
// Phase가 실제로 변경된 때만 전환 처리
CurrentPhase = NewPhase;
UE_LOG(LogTemp, Log, TEXT("Phase switched to : %d"), static_cast<int32>(CurrentPhase));
switch (CurrentPhase)
{
case EGamePhase::GameStart:
HandleGameStartPhase();
break;
case EGamePhase::TurnStart:
HandleTurnStartPhase();
break;
case EGamePhase::CardDraw:
HandleCardDrawPhase();
break;
case EGamePhase::SummonMinion:
HandleSummonMinionPhase();
break;
case EGamePhase::CastSpell:
HandleCastSpellPhase();
break;
case EGamePhase::TurnEnd:
HandleTurnEndPhase();
break;
case EGamePhase::GameEnd:
HandleGameEndPhase();
break;
default:
break;
}
}
}
void ATCGGameMode::HandleGameStartPhase()
{
// 선공 및 후공 카드 설정, 동전 제공등 초기화 작업 수행
UE_LOG(LogTemp, Log, TEXT("Game Start Phase"));
OnTurnStart.Broadcast(); // 턴 시작 시 효과 발동
// 다음 Phase로 전환
SetCurrentPhase(EGamePhase::TurnStart);
}
void ATCGGameMode::HandleTurnStartPhase()
{
// 선공 및 후공 카드 설정, 동전 제공등 초기화 작업 수행
UE_LOG(LogTemp, Log, TEXT("turn Start Phase"));
// 다음 Phase로 전환
SetCurrentPhase(EGamePhase::CardDraw);
}
void ATCGGameMode::HandleCardDrawPhase()
{
// 선공 및 후공 카드 설정, 동전 제공등 초기화 작업 수행
UE_LOG(LogTemp, Log, TEXT("Card Draw Phase"));
// 다음 Phase로 전환
SetCurrentPhase(EGamePhase::SummonMinion);
}
void ATCGGameMode::HandleSummonMinionPhase()
{
// 선공 및 후공 카드 설정, 동전 제공등 초기화 작업 수행
UE_LOG(LogTemp, Log, TEXT("SummonMinion Minion Phase"));
// 다음 Phase로 전환
SetCurrentPhase(EGamePhase::CastSpell);
}
void ATCGGameMode::HandleCastSpellPhase()
{
// 선공 및 후공 카드 설정, 동전 제공등 초기화 작업 수행
UE_LOG(LogTemp, Log, TEXT("Cast Spell Phase"));
// 다음 Phase로 전환
SetCurrentPhase(EGamePhase::TurnEnd);
}
void ATCGGameMode::HandleTurnEndPhase()
{
// 선공 및 후공 카드 설정, 동전 제공등 초기화 작업 수행
UE_LOG(LogTemp, Log, TEXT("Turn End Phase"));
OnTurnEnd.Broadcast(); // 턴 종료 시 효과 발동
// 다음 Phase로 전환
SetCurrentPhase(EGamePhase::TurnStart);
}
void ATCGGameMode::HandleGameEndPhase()
{
// 게임 종료 및 승패 처리
UE_LOG(LogTemp, Log, TEXT("Game end Phase"));
}