티스토리 뷰

2D게임이나 3D 탑뷰에서 시야 범위를 표현 하기 위하여 구현하게 된 컴포넌트 입니다.

ProceduralMeshComponent 를 이용하였으며 실시간으로 Mesh를 생성하여 시야를 표현합니다.

간단히 범위, 각도, 트레이스될 장애물 값을 설정 하면 됩니다.



<Line of sight 설정 값>



<Line of sight 사용>



<LineOfSightComponent.h>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "ProceduralMeshComponent.h"
#include "Components/SceneComponent.h"
#include "LineOfSightComponent.generated.h"
struct FSightTraceInfo
{
    bool Hit;
    FVector Point;
    float Dst;
    float Angle;
    FSightTraceInfo() {}
    FSightTraceInfo(bool InHit, FVector InPoint, float InDst, float InAngle)
        :Hit(InHit),
        Point(InPoint),
        Dst(InDst),
        Angle(InAngle)
    {}
};
class UMaterialInterface;
UCLASS( ClassGroup=(LineOfSight), meta=(BlueprintSpawnableComponent) )
class PROJECTA_API ULineOfSightComponent : public UProceduralMeshComponent
{
    GENERATED_BODY()
public:
    /** 디버그 유무  */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LineOfSight")
    bool bDebugIsEnabled;
    /** 시야범위  */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LineOfSight")
    float SightRadius;
    /** 시야각도 ( 0 ~ 360 )  */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LineOfSight", meta = (UIMax = 360, UIMin = 0))
    float SightAngle;
    /** 장애물 타겟 채널  */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LineOfSight")
    TEnumAsByte<ETraceTypeQuery> ObstacleTraceType;
    /** 생성 Mesh 퀄리티 ( 버텍스 갯수 )  */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LineOfSight|MeshInfo")
    float MeshQuality;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LineOfSight|MeshInfo")
    UMaterialInterface* Material;
public:    
    ULineOfSightComponent();
    virtual void BeginPlay() override;
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
private:
    UPROPERTY()
    TArray<FVector> Vertices;
    UPROPERTY()
    TArray<int32> Triangles;
    UPROPERTY()
    TArray<FVector> normals;
    UPROPERTY()
    TArray<FVector2D> UV0;
    UPROPERTY()
    TArray<FLinearColor> vertexColors;
    UPROPERTY()
    TArray<FProcMeshTangent> tangents;
    void DrawLineOfSight();
    FSightTraceInfo GetSightTraceInfoInfo(float Angle);
    FVector DirectionFromAngle(float AngleInDegrees);
};
 
cs


<LineOfSightComponent.cpp>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Fill out your copyright notice in the Description page of Project Settings.
 
#include "LineOfSightComponent.h"
#include "ProceduralMeshComponent.h"
#include "Kismet/KismetSystemLibrary.h"
 
ULineOfSightComponent::ULineOfSightComponent()
{
    PrimaryComponentTick.bCanEverTick = true;
 
    SightRadius = 500.0f;
    SightAngle = 90.0f;
    MeshQuality = 5.0f;
 
    CastShadow = false;
}
 
void ULineOfSightComponent::BeginPlay()
{
    Super::BeginPlay();
 
    if ( Material )
    { 
        SetMaterial(0, Material);
    }
}
 
 
void ULineOfSightComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
 
    DrawLineOfSight();
}
 
void ULineOfSightComponent::DrawLineOfSight()
{
    /**
     *    MeshQuality 값이 클수록 많이 폴리곤을 그리기 위하여 SightAngle값에 MeshQuality를 곱한 값을
     *    SightAngle값으로 나누어 최소 AngleSize 를 구한다.
     *    현재 컴포넌트의 Yaw(Z축) 값을 기준으로 좌측 부터 우측 까지 최소 AngleSize 만큼 더해가면서 장애물을 Trace 하여
     *  버텍스 위치값을 설정한다
     */
    
    int32 AnglePieceCount = FMath::RoundToInt(SightAngle * MeshQuality);
    float DivideAngleSize = SightAngle / AnglePieceCount;
 
    TArray<FVector> ViewPoints;
    
    for (int32 index = 0; index <= AnglePieceCount; ++index)
    {
        FRotator ViewRotation = GetComponentRotation();
        float Angle = ViewRotation.Yaw - (SightAngle / 2.0f) + (DivideAngleSize * index);
        FSightTraceInfo SightTraceInfo = GetSightTraceInfoInfo(Angle);
        ViewPoints.Add(SightTraceInfo.Point);
    }
 
    int32 VertextCount = ViewPoints.Num() + 1;
    Vertices.SetNum(VertextCount);
 
    int32 TriangleCount = (VertextCount - 2* 3;
    Triangles.SetNum(TriangleCount);
 
    Vertices[0= FVector::ZeroVector;
    for (int32 index = 0; index < VertextCount - 1++index)
    {
        //버텍스 월드좌표를 컴포넌트의 로컬좌표로 변경
        Vertices[index + 1= GetComponentTransform().InverseTransformPosition(ViewPoints[index]);
 
        //인덱스 버퍼 설정
        if (index < VertextCount - 2)
        {
            Triangles[index * 3 + 2= 0;
            Triangles[index * 3 + 1= index + 1;
            Triangles[index * 3 ] = index + 2;
        }
    }
 
    CreateMeshSection_LinearColor(0, Vertices, Triangles,normals, UV0, vertexColors, tangents, false);
}
 
FSightTraceInfo ULineOfSightComponent::GetSightTraceInfoInfo(float Angle)
{
    EDrawDebugTrace::Type DrawDebugType = bDebugIsEnabled ? EDrawDebugTrace::ForDuration : EDrawDebugTrace::None;
 
    //Angle 값으로 x축 y축 방향을 구한다
    FVector Dir = DirectionFromAngle(Angle);
    FHitResult OutHit;
    FVector Start = GetComponentLocation();
    FVector End = (Dir*SightRadius) + Start;
    TArray<AActor*> ActorsToIgnore;
 
    if(UKismetSystemLibrary::LineTraceSingle(this,Start,End, ObstacleTraceType,false, ActorsToIgnore, DrawDebugType, OutHit, true))
    {
        return FSightTraceInfo(true, OutHit.ImpactPoint, OutHit.Distance, Angle);
    }
    else
    {
        return FSightTraceInfo(false, End, SightRadius, Angle);
    }
}
 
FVector ULineOfSightComponent::DirectionFromAngle(float AngleInDegrees)
{
    return FVector(FMath::Cos(FMath::DegreesToRadians(AngleInDegrees)), FMath::Sin(FMath::DegreesToRadians(AngleInDegrees)), 0.0f);
}
 
cs


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함