๐
2025-12-03
์์๋ ์ ์๋ ๋ฑ ๊ฐ๊ฐ ๋ค๋ฅธ ์ํธ์์ฉ์ด ํ์ํ ์กํฐ๋ฅผ ์ ์ ๋ฐ ์ค์นํ๋ ๋น๋ฉ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ๋ค.

bool UTSBuildingComponent::ValidatePlacement(FHitResult HitResult)
{
// 1. ์ง๋ฉด ๊ฒ์ฌ (๊ฒฝ์ฌ๋ ์ฒดํฌ)
const float DotProduct = FVector::DotProduct(HitResult.Normal, FVector::UpVector);
if (DotProduct < 0.7f) return false;
// 2. ์ถฉ๋ ์ฒดํฌ
if (!CheckOverlap(HitResult.Location, CheckExtent)) return false;
// 3. LightSource ๋ฒ์ ์ฒดํฌ
if (!IsInLightSourceRange(HitResult.Location)) return false;
return true;
}
๊ฒ์ฆ ์์์ ์๋ฏธ
bool UTSBuildingComponent::IsInLightSourceRange(const FVector& Location) const
{
TArray<AActor*> FoundActors;
UKismetSystemLibrary::SphereOverlapActors(
GetWorld(), Location, LightSourceDetectionRadius,
ObjectTypes, nullptr, IgnoreActors, FoundActors);
for (AActor* Actor : FoundActors)
{
AErosionLightSourceSubActor* LightSource = Cast<AErosionLightSourceSubActor>(Actor);
if (LightSource && LightSource->GetLightscale() > 0.f)
{
return true;
}
}
return false;
}
ํต์ฌ ๋ก์ง
GetLightscale() > 0.f ์ฒดํฌ๋ก ํ์ฑํ๋ ๊ด์๋ง ์ธ์ bool UTSBuildingComponent::CheckOverlap(const FVector& Location, const FVector& Extent)
{
// BoxOverlap์ผ๋ก Pawn, WorldDynamic, WorldStatic ์ฒดํฌ
bool bHasOverlap = UKismetSystemLibrary::BoxOverlapActors(...);
if (!bHasOverlap) return true;
for (AActor* OverlappedActor : OutActors)
{
if (OverlappedActor->IsA(APawn::StaticClass())) return false;
if (OverlappedActor->ActorHasTag(FName("BlockBuilding"))) return false;
}
return true;
}
Extent ๊ณ์ฐ
FBoxSphereBounds Bounds = PreviewMeshComp->GetStaticMesh()->GetBounds();
CheckExtent = Bounds.BoxExtent * 0.9f; // 90% ํฌ๊ธฐ๋ก ์ฒดํฌ
void UTSBuildingComponent::UpdatePreviewMesh(float DeltaTime)
{
bCanPlace = ValidatePlacement(HitResult);
// ์ํ ๋ณ๊ฒฝ ์์๋ง ๋จธํฐ๋ฆฌ์ผ ์
๋ฐ์ดํธ
if (bCanPlace != bLastCanPlace && CachedDynamicMaterial)
{
CachedDynamicMaterial->SetVectorParameterValue(FName("Color"),
bCanPlace ? FLinearColor::Green : FLinearColor::Red);
bLastCanPlace = bCanPlace;
}
}
bLastCanPlace ํ๋๊ทธ๋ก ๋ถํ์ํ ์
๋ฐ์ดํธ ๋ฐฉ์งbool UTSBuildingComponent::CanBuild(int32 RecipeID, int32& OutResultID)
{
if (!GetOwner()->HasAuthority()) return false; // ์๋ฒ์์๋ง
FBuildingRecipeData RecipeData;
if (!BuildingRecipeDataSub->GetBuildingRecipeDataSafe(RecipeID, RecipeData))
return false;
OutResultID = RecipeData.ResultItemID;
// ์ธ๋ฒคํ ๋ฆฌ ๊ฒ์ฆ
for (const FBuildingIngredientData& Ingredient : RecipeData.Ingredients)
{
int32 ItemCount = PlayerInventoryComp->GetItemCount(Ingredient.MaterialID);
if (ItemCount < Ingredient.Count) return false;
}
return true;
}
ConsumeIngredients์์ ์ค์ ์ฌ๋ฃ ์๋น๊ฒ์ฆ ํ์ดํ๋ผ์ธ์ ์์๊ฐ ์๊ฐ๋ณด๋ค ์ค์ํ๋ค. ๋น์ฉ์ด ๋ฎ์ ๊ฒ์ฌ๋ถํฐ ์ํํด์ ๋ถํ์ํ ์ฐ์ฐ์ ์ค์ด๋ ๊ฒ ์ฑ๋ฅ๊ณผ ์ง๊ฒฐ๋๋ค๋ ๊ฑธ ์ฒด๊ฐํ๋ค.
ActorHasTag๋ฅผ ํ์ฉํ ๋ธ๋๋ฆฌ์คํธ ๋ฐฉ์์ด ๊ฐ๋จํ๋ฉด์๋ ํ์ฅ์ฑ์ด ์ข๋ค. ๋ณต์กํ ํ์
์ฒดํฌ ๋์ ํ๊ทธ ํ๋๋ก ์๋ฏธ๋ฅผ ๋ช
ํํ๊ฒ ์ ๋ฌํ ์ ์์ด์ ํธํ๋ค.
๋งค ํฑ๋ง๋ค ์ค๋ฒ๋ฉ์ผ๋ก LightSource๋ฅผ ๊ฒ์ฆํ๋ ๋ถ๋ถ์ด ๋นํจ์จ์ ์ธ๊ฒ ๊ฐ๋ค. ์ด๋ค ๋ฐฉ์์ผ๋ก ์ต์ ํํด์ผ ํผ๋๋ฐฑ๋ ๋ฆ์ง ์๊ณ ๋น์ฉ์ ์ค์ผ ์ ์์์ง ๋ ์ฐพ์๋ด์ผ๊ฒ ๋ค.