박하의 나날

[06]Battery Collector _ spawn 결정(언제, 어디서, 무엇을)

프로그래밍/Unreal

Spawning Pickups(What, Where, When to Spawn)

06.Defining What to Spawn

SpawnVolume.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
// Fill out your copyright notice in the Description page of Project Settings.
 
#pragma once
 
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"
 
UCLASS()
class BATTERYCOLLECTOR_API ASpawnVolume : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    ASpawnVolume();
 
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    
    // Called every frame
    virtual void Tick( float DeltaSeconds ) override;
    /*Returns the WhereToSpawn subobject*/
    FORCEINLINE class UBoxComponent* GetWhereToSpawn() const { return WhereToSpawn; }
 
    /*Find a random point within the Boxcomponent*/
    UFUNCTION(BlueprintPure, Category = "Spawning")
    FVector GetRandomPointInVolume();
 
private:
    /*Box Component to specify where pickups should be spawned*/
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, 
Category = "Spawning", meta = (AllowPrivateAccess = "true"))
    class UBoxComponent* WhereToSpawn;
};
 
cs

23)UFUNCTION이랑 FORCEINLINE은 호환이 안된다.

만약 GetMesh를 BlueprintCallable로 만들고 싶다면 FORCEINLINE매크로를 사용하지 말아야한다.

26)박스 컴포넌트 볼륨 속의 랜덤한 좌표를 반환해서 벡터값으로 변환한다

33)아이템 스폰 위치를 결정할 박스 컴포넌트


SpawnVolume.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
// Fill out your copyright notice in the Description page of Project Settings.
 
#include "BatteryCollector.h"
#include "SpawnVolume.h"
#include "Kismet/KismetMathLibrary.h"
 
 
// Sets default values
ASpawnVolume::ASpawnVolume()
{
     // Set this actor to call Tick() every frame. 
    PrimaryActorTick.bCanEverTick = false;
    //Create the Box Component to represent the spawn volume
    WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));
    RootComponent = WhereToSpawn;
}
// Called when the game starts or when spawned
void ASpawnVolume::BeginPlay()
{
    Super::BeginPlay();
}
 
// Called every frame
void ASpawnVolume::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );
}
 
FVector ASpawnVolume::GetRandomPointInVolume(){
    FVector SpawnOrigin = WhereToSpawn->Bounds.Origin;
    FVector SpawnExtent = WhereToSpawn->Bounds.BoxExtent;
    
    return UKismetMathLibrary::RandomPointInBoundingBox(
SpawnOrigin, SpawnExtent);
}
 
cs

5)키즈멧 라이브러리: 플레이어 컨트롤러나 각종 유용한 기능

블루프린트뿐만 아니라 코드에서도 값에 접근할 수 있게 해준다.

15)박스 컴포넌트를 루트로 설정한다, 새 컴포넌트가 추가되면 이를 중심으로 삼게된다.

30) 모든 박스 컴포넌트에는 중점과 면까지의 거리값이 벡터 값으로 들어있는데 그 값을 받아온것.

WhereToSpawn를 콜하지 않고 직접가져다 쓸수 있게 됐는데,

현재 함수가 SpawnVolume과  같은 클래스 내에 있기 때문이다. 

만약 게임모드 등과 같이 다른 클래스였다면 GetSpawnVolume등과 같은 함수를 사용했어야 한다.

33,34)GetRandomPointInBoundingBox 함수: 원점과 각 축의 길이를 파라미터로 받는다.

랜덤한 x,y,z값을 각각 만들어내는 것 보다 조금더 쉽게 계산해 낼수 있다.



07.Defining What to Spawn


SpawnVolume.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
// Fill out your copyright notice in the Description page of Project Settings.
 
#pragma once
 
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"
 
UCLASS()
class BATTERYCOLLECTOR_API ASpawnVolume : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    ASpawnVolume();
 
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    
    // Called every frame
    virtual void Tick( float DeltaSeconds ) override;
    /*Returns the WhereToSpawn subobject*/
    FORCEINLINE class UBoxComponent* GetWhereToSpawn() const { return WhereToSpawn; }
 
    /*Find a random point within the Boxcomponent*/
    UFUNCTION(BlueprintPure, Category = "Spawning")
    FVector GetRandomPointInVolume();
 
protected:
    /**The pickup to spawn*/
    UPROPERTY(EditAnywhere, Category = "spawn")
    TSubclassOf<class APickup> WhatToSpawn;
 
private:
    /*Box Component to specify where pickups should be spawned*/
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, 
Category = "Spawning", meta = (AllowPrivateAccess = "true"))
    class UBoxComponent* WhereToSpawn;
 
    //Handle spawning a new pickup
    void SpawnPickup();
};
 
cs

32) TSubclassOf<class ~> APickup클래스나 자손클래스만으로 한정하여 사용할수있다.

UCLASS포인터와 유사하기때문에 전방선언, 


SpawnVolume.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
// Fill out your copyright notice in the Description page of Project Settings.
 
#include "BatteryCollector.h"
#include "SpawnVolume.h"
#include "Kismet/KismetMathLibrary.h"
#include "Pickup.h"
 
 
// Sets default values
ASpawnVolume::ASpawnVolume()
{
     // Set this actor to call Tick() every frame. 
    PrimaryActorTick.bCanEverTick = false;
    //Create the Box Component to represent the spawn volume
    WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));
    RootComponent = WhereToSpawn;
}
 
// Called when the game starts or when spawned
void ASpawnVolume::BeginPlay()
{
    Super::BeginPlay();    
}
 
// Called every frame
void ASpawnVolume::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );
}
 
FVector ASpawnVolume::GetRandomPointInVolume(){
    /***/
    FVector SpawnOrigin = WhereToSpawn->Bounds.Origin;
    FVector SpawnExtent = WhereToSpawn->Bounds.BoxExtent;
    
    return UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtent);
}
 
void ASpawnVolume::SpawnPickup(){
    //1.If we have set something to spawn:
    if (WhatToSpawn != NULL){
        //2.Check for a valid world:
        UWorld* const World = GetWorld();
        if (World){
            //3.Set the spawn parameters
            FActorSpawnParameters SpawnParams;
            SpawnParams.Owner = this;
            SpawnParams.Instigator = Instigator;
            /**Get a random location to spawn at*/
            FVector SpawnLocation = GetRandomPointInVolume();            
            //Get a random rotation for the spawned item
            FRotator SpawnRotation;
            SpawnRotation.Yaw = FMath::FRand()*360.0f;
            SpawnRotation.Pitch = FMath::FRand()*360.0f;
            SpawnRotation.Roll = FMath::FRand()*360.0f;
            //4.spawn the pickup
            APickup* const SpawnedPickup = World->SpawnActor<APickup>(WhatToSpawn, 
SpawnLocation, SpawnRotation, SpawnParams);
        }
        
    }
        
}
cs

7)헤더가 아니라 소스파일에 인클루드 하는 이유:

헤더에서는 전방선언만으로 잘통하지만 pickup클래스를 직접다루게 되면 좀더 많은 정보가 필요할거라 

소스파일에 인클루드한 것이다.

즉, 다른곳에서라도 spawnVolume.h를 인클루드할 필요가 있으면, 그거만 인클루드하면 된다.

이렇게 해서 헤더를 분리되게하고 의존성을 떨어뜨리는것.

40~)아이템 만드는 단계

//1. If we have set something to spawn 생성할 것이 설정되어 있다면

//2. Check for a valid world 월드가 유효한지 확인

//3. Set the spawn parameters 생성범위를 설정하고

//4. spawn the pickup 아이템 생성

49) 누가 아이템을 만들었는지, 누가 만들게 했는지 명시

51) 아이템이 랜덤한 위치에서 소환하게 한다.

53) 아이템에 랜덤한 회전을 준다.


08.Setting Timers for Spawning 

SpawnVoume.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
// Fill out your copyright notice in the Description page of Project Settings.
 
#pragma once
 
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"
 
UCLASS()
class BATTERYCOLLECTOR_API ASpawnVolume : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    ASpawnVolume();
 
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    
    // Called every frame
    virtual void Tick( float DeltaSeconds ) override;
    /*Returns the WhereToSpawn subobject*/
    FORCEINLINE class UBoxComponent* GetWhereToSpawn() const { return WhereToSpawn; }
 
    /*Find a random point within the Boxcomponent*/
    UFUNCTION(BlueprintPure, Category = "Spawning")
    FVector GetRandomPointInVolume();
 
protected:
    /**The pickup to spawn*/
    UPROPERTY(EditAnywhere, Category = "spawn")
    TSubclassOf<class APickup> WhatToSpawn;
 
    FTimerHandle SpawnTimer;
    //Minimum spawn delay
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
    float SpawnDelayRangeLow;
    //Maximum spawn delay
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
    float SpawnDelayRangeHigh;
 
private:
    /*Box Component to specify where pickups should be spawned*/
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, 
Category = "Spawning", meta = (AllowPrivateAccess = "true"))
    class UBoxComponent* WhereToSpawn;
 
    //Handle spawning a new pickup
    void SpawnPickup();
    //The current spawn delay
    float SpawnDelay;
};
 
cs

34)FTimerHandle형은 UPROPERTY를 달아줄필요가 없다. 

FTimerHandle형이 어떠한 값이 아니기때문이다.

UPROPERTY 에디터에서 편집 가능하고 가비지 콜렉션에도 포함될수 있다.



SpawnVolume.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
// Fill out your copyright notice in the Description page of Project Settings.
 
#include "BatteryCollector.h"
#include "SpawnVolume.h"
#include "Kismet/KismetMathLibrary.h"
#include "Pickup.h"
 
// Sets default values
ASpawnVolume::ASpawnVolume()
{
     // Set this actor to call Tick() every frame. 
    PrimaryActorTick.bCanEverTick = false;
    //Create the Box Component to represent the spawn volume
    WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));
    RootComponent = WhereToSpawn;
    //Set the spawn delay range
    SpawnDelayRangeLow = 1.0f;
    SpawnDelayRangeHigh = 4.5f;
}
 
// Called when the game starts or when spawned
void ASpawnVolume::BeginPlay()
{
    Super::BeginPlay();    
 
    SpawnDelay = FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);    
    GetWorldTimerManager().SetTimer(SpawnTimer, this&ASpawnVolume::SpawnPickup, SpawnDelay, false);
}
 
// Called every frame
void ASpawnVolume::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );
}
 
FVector ASpawnVolume::GetRandomPointInVolume(){
    FVector SpawnOrigin = WhereToSpawn->Bounds.Origin;
    FVector SpawnExtent = WhereToSpawn->Bounds.BoxExtent;
    
    return UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtent);
}
 
void ASpawnVolume::SpawnPickup(){
 
    //1.If we have set something to spawn:
    if (WhatToSpawn != NULL){
        //2.Check for a valid world:
        UWorld* const World = GetWorld();
        if (World){
            //3.Set the spawn parameters
            FActorSpawnParameters SpawnParams;
            SpawnParams.Owner = this;
            SpawnParams.Instigator = Instigator;
            /**Get a random location to spawn at*/
            FVector SpawnLocation = GetRandomPointInVolume();
            //Get a random rotation for the spawned item
            FRotator SpawnRotation;
            SpawnRotation.Yaw = FMath::FRand()*360.0f;
            SpawnRotation.Pitch = FMath::FRand()*360.0f;
            SpawnRotation.Roll = FMath::FRand()*360.0f;
            //4.spawn the pickup
            APickup* const SpawnedPickup = World->SpawnActor<APickup>(
WhatToSpawn, SpawnLocation, SpawnRotation, SpawnParams);
 
            SpawnDelay = FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);            
            GetWorldTimerManager().SetTimer(SpawnTimer, this&ASpawnVolume::SpawnPickup, SpawnDelay, false);
        }
        
    }
}
cs

28)게임속 실행중인 모든 타이머를 추적한다.

핸들을 가져다가 

spawnTimer 클래스를 위해서 spawnPickup에 바인딩(this)하고 spawndelay만큼의 시간이 흐르고나면 호출. 

false 반복은 안함.

66, 67) 아이템 생성시 범위 내 값으로 타이머 시간을 랜덤하게 새로뽑기 위해서



detail에서 시간이랑 스폰할 것을 조절할수 있게 된다.