Macro UFUNCTION()

UFUNCTION () służy do implementacji funkcji w C ++. Możliwe jest tworzenie funkcji C ++, które będą wywoływane w edytorze Blueprints. Możliwe jest również wywołanie w C ++ funkcji, które zostały zaimplementowane w Blueprints.

Blueprint może reprezentować funkcję C ++ na trzy różne sposoby. Zdarzenia są zaimplementowane jako funkcje c ++, który nie zwraca wartości, to znaczy ich typ zwracany jest typu “void”:

Funkcja Blueprint z pinem wykonawczym jest implementowana jako Funkcja C ++, która nie jest oznaczona jako Blueprint “Pure”: 

Jeśli funkcja C ++ jest oznaczona za pomocą Blueprint Pure , jest reprezentowana jako funkcja bez pinu wykonywania, nie może modyfikować danych instancji i może być używana w wyrażeniach. Jest szeroko stosowana w funkcjach typu Get.

Porozmawiajmy teraz o specyfikatorach funkcji . Po pierwsze, każda UFUNCTION() musi mieć specyfikator Category, aby funkcja była poprawnie wyświetlana w menu kontekstowym Blueprint.

Aby funkcja UFUNCTION() została użyta w wykresie Blueprints, musi mieć jeden z tych dwóch specyfikatorów:

  • BlueprintCallable : pojawi się jako funkcja Blueprint z kodem wykonawczym.
  • BlueprintPure : Pojawi się jako pure function.

Możliwe jest zdefiniowanie funkcji C ++, która zostanie zaimplementowana w Blueprint. Aby to zrobić, użyj jednego z tych dwóch specyfikatorów w funkcji  UFUNCTION () :

  • BlueprintImplementableEvent : Funkcja jest zaimplementowana tylko w Blueprint. Jeśli zostanie znaleziona implementacja C ++, wygeneruje błąd kompilacji.
  • BlueprintNativeEvent : umożliwia implementację wersji C ++ funkcji, która będzie używana, jeśli wersja Blueprint nie zostanie zaimplementowana. Wersja C ++ musi mieć nazwę funkcji oraz przyrostek  _Implementation.

Jeśli zwracanym typem funkcji C ++ jest void , zostanie zaimplementowany jako Event w Blueprints. Jeśli funkcja C ++ zwróci wartość, zostanie zaimplementowana jako funkcja Blueprint. 

Sprawdzimy teraz te wszystkie specyfikatory w akcji w przykładowym użyciu.

Przykładowe użycie:

Utwórz klasę C ++ o nazwie TestUFunction, używając klasy Actor jako klasy nadrzędnej. W pliku TestUFunction.h dodaj deklaracje czterech funkcji poniżej funkcji Tick ​​() , jak pokazano w tym kodzie:

...

public:	
  // Called every frame
  virtual void Tick(float DeltaTime) override;
	
  UFUNCTION(BlueprintCallable, Category = Tutorial)
  int32 WelcomeMsg(FString PlayerName);
	
  UFUNCTION(BlueprintPure, Category = Tutorial)
  float CalculateDamage(float PowerAttack, float DefenseValue);
	
  UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = Tutorial)
  void TreasureFound();
	
  UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Tutorial)
  FString NativeExample();
};

W pliku TestUFunction.cpp dodaj implementację trzech funkcji pokazanych poniżej: 

...

int32 ATestUFunction::WelcomeMsg(FString PlayerName)
{
  FString Message = FString::Printf(TEXT("Welcome %s."), *PlayerName);
  
  if(GEngine)
  {
    GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, Message);
  }
  
  //Returns the number of characters in PlayerName
  return PlayerName.Len();
}

float ATestUFunction::CalculateDamage(float PowerAttack, float DefenseValue)
{
  return (PowerAttack * 10) / DefenseValue;
}

FString ATestUFunction::NativeExample_Implementation()
{
  return TEXT("NativeExample: C++ version");
}

Zauważ, że funkcja TreasureFound () nie została zaimplementowana. Dzieje się tak, ponieważ zostanie zaimplementowana tylko w Blueprint ze względu na specyfikator BlueprintImplementableEvent . Funkcja NativeExample () ma specyfikator BlueprintNativeEvent, który umożliwia jej implementację w językach C ++ i Blueprint. Wersja C ++ została zaimplementowana pod nazwą NativeExample_Implementation () .

Skompiluj kod C ++. Utwórz Blueprint na podstawie klasy TestUFunction  C ++  . Aby to zrobić, kliknij prawym przyciskiem myszy klasę TestUFunction i wybierz opcję  Utwórz klasę Blueprint na podstawie TestUFunction .

Kliknij prawym przyciskiem myszy Blueprint Event Graph i zobacz, że cztery funkcje zadeklarowane w C ++ są wymienione w kategorii Tutorial, jak pokazano na poniższym obrazku. Pojawiają się na tej liście, ponieważ mają specyfikator BlueprintCallable lub BlueprintPure .

Nazwiemy cztery funkcje w wydarzeniu BeginPlay w Blueprint . Dodaj funkcje do wykresu zdarzeń, jak pokazano na poniższym obrazku.

Przed testowaniem musimy zaimplementować funkcję TreasureFound () w Blueprint. Na karcie My Blueprint kliknij przycisk Override obok pozycji Funkcje . Zwróć uwagę, że na liście pojawiają się funkcje Treasure Found i Native Example . Kliknij Treasure Found

Zwracany typ funkcji TreasureFound () to void , więc jest zaimplementowany jako Event w Blueprint. Dodaj funkcję Print String z komunikatem:

Skompiluj Blueprint i dodaj instancję na poziomie. Uruchom grę i zobacz komunikaty na ekranie. Każda linia przedstawia wykonanie jednej z funkcji.

Ponieważ wersja Blueprint funkcji NativeExample () nie została zaimplementowana, użyto wersji C ++. Aby ukończyć ten przykład, zaimplementujemy wersję Blueprint NativeExample () .

Na karcie My Blueprint kliknij przycisk Override obok pozycji Functions i wybierz opcję Native Example . Jest implementowana jako funkcja, ponieważ ma wartość zwracaną. Zobacz węzeł Parent: Native Example na poniższym zrzucie ekranu. Ten węzeł wykona wersję C ++ NativeExample () . Pozwala to na dodanie funkcji w Blueprint do tej funkcji. Ale jeśli chcesz, aby wersja Blueprint zastąpiła wersję C ++, po prostu usuń ten węzeł. 

W naszym przykładzie usunę węzeł Parent: Native Example i napisze komunikat w parametrze  Return Value .

Skompiluj Blueprint i rozpocznij grę. Zwróć uwagę, że wywołano tylko wersję Blueprint NativeExample(). 


Źródło:https://romeroblueprints.blogspot.com/2020/10/the-ufunction-macro.html

Akcje Blueprints w HUD

Schematy mają funkcję o nazwie „Event Dispatcher”, która pozwala na rodzaj komunikacji między planami klas a schematem poziomu. W tym artykule zmodyfikujemy przykład użyty w poprzednim artykule  i stworzymy schemat przedstawiający detonator, który będzie odpowiedzialny za zainicjowanie eksplozji.

Schemat „Detonator”, który stworzymy w tym artykule, zawiera zmienną „Czas” z czasem pozostałym do wybuchu. Kiedy aktor dotknie planu, zostanie on aktywowany i rozpocznie się odliczanie do wybuchu. Gdy zmienna „Czas” ma wartość zero, schemat „Detonator” wygeneruje zdarzenie o nazwie „Detonacja” za pomocą Event Dispatcher .

To kończy misję planu „Detonator”. Schemat „Detonatora” nie odpowiada za sposób, w jaki nastąpi eksplozja. W ten sposób mamy plan, który można wykorzystać w różnych sytuacjach.

Eksplozja jest tworzona na schemacie poziomu, gdy generowane jest zdarzenie „Detonacja” schematu „Detonator”. Stwórz nowy schemat o nazwie „Detonator” oparty na klasie Actor. Dodaj te dwie zmienne:


– Czas : typ całkowity, edytowalna i domyślna wartość 10.- Aktywny : typ Boolean, nie można go edytować i domyślna wartość to „false” (niezaznaczona).
Przejdź do trybu Komponenty. Schemat „Detonatora” jest wizualnie reprezentowany przez dwa komponenty: ramkę i renderowanie tekstu. Kliknij przycisk „Dodaj komponent” i najpierw wybierz „Pudełko”, a następnie „Renderowanie tekstu”:

Kliknij utworzony komponent Box i odznacz opcję „Ukryty w grze” w kategorii „Rendering”, aby pojawił się w trakcie gry.

Kliknij utworzony składnik TextRender i zmień jego nazwę na „ Wyświetl ”. W kategorii „Tekst” we właściwości „Wyrównanie w poziomie” wybierz „Środek”. We właściwości „Kolor renderowania tekstu” wybierz kolor czerwony, jak na poniższym obrazku:

Plan będzie wyglądał następująco:

Wyświetlana wartość 10 została uzyskana ze zmiennej “Czas” w Skrypcie Konstrukcji :

Przełącz się do trybu wykresu i utwórz nowy „EventDispatcher” o nazwie „Detonate”:

Detonator zostanie aktywowany, gdy ktoś go dotknie, więc wykorzystamy wydarzenie „Actor Begin Overlap”. Zmienna „Aktywny” jest używana, aby detonator był aktywowany tylko raz. Wykorzystuje także licznik czasu i zdarzenie niestandardowe, które jest odpowiedzialne za odliczanie detonatora. Scenariusz wyglądał następująco:

Funkcja „Set Timer Delegate” jest po prostu odmianą funkcji „Set Timer”, ale zamiast wpisywać nazwę zdarzenia, użyliśmy czerwonej pinezki niestandardowego zdarzenia, aby utworzyć skojarzenie z parametrem „Delegate” w Funkcja „Ustaw delegata timera”. Powyższy przykład oznacza po prostu, że zdarzenie „Zegar” będzie wywoływane co 1 sekundę.

Zdarzenie „Clock” odpowiada za zmniejszenie jednej zmiennej „time”, zaktualizowanie tekstu komponentu TextRender oraz sprawdzenie, czy zmienna „time” jest równa zero, aby wyczyścić Timer i wywołać Event Dispatcher „Detonate “:

Akcja „Wyczyść licznik czasu” wyłącza licznik czasu. Musisz podać nazwę zdarzenia niestandardowego, które jest skojarzone z licznikiem czasu. W powyższym przykładzie dezaktywujemy timer, aby zatrzymać licznik na zero.

Akcja „Call Detonate” generuje zdarzenie „Detonate”, które stworzyliśmy za pomocą Event Dispatchera. Akcje, które nastąpią w związku z tym wydarzeniem, zostaną utworzone na schemacie poziomu.

Po kompilacji „Detonator”  Blueprint , że jest gotowy do dodania do poziomu. Poniższy obrazek przedstawia „Detonator” w tabeli.

Teraz musimy umieścić akcje na planach poziomów, które spowodują eksplozję. Wybierz schemat „Detonator”, który został umieszczony na poziomie, a następnie otwórz schemat poziomu. Kliknij prawym przyciskiem myszy na wykresie i wybierz „Dodaj detonację”:

„Detonacja” to zdarzenie generowane przez „Detonator” za pomocą Event Dispatcher. Dostosowując przykład z poprzedniego artykułu, skrypt schematu poziomu  wygląda następująco  :

Uruchom grę i dotknij „Detonatora”, aby rozpocząć odliczanie. Kiedy osiągnie zero, generowany jest wybuch:

Korzystając z Event Dispatchera , oddzielamy schemat „Detonatora” generowanej eksplozji. W ten sposób możemy używać “Detonatora” na kilku poziomach generując różne eksplozje bez zmiany schematu “Detonatora”. Dalej:  Zaznacz zdarzenia i akcje ukryte w planach Poprzedni:  Korzystanie ze schematów poziomów Spis treści

Makro UPROPERTY()

Makro UPROPERTY()  służy do odsłaniania zmiennych w edytorze Unreal Engine i zawierania właściwości w systemie zarządzania pamięcią Unreal Engine.

Makro UPROPERTY() jest umieszczona na linii powyżej definicji C ++ zmiennych, jak przedstawiono w przykładzie poniżej:

UPROPERTY(EditAnywhere, Category = Tutorial)
int32 SpecialId;

Parametry UPROPERTY() nazywane są property specifiers . W powyższym przykładzie użyto EditAnywhere i Category, które służą do grupowania zmiennych w oknie właściwości. Użycie cudzysłowowia w nazwie kategorii jest opcjonalne , ale konieczne, jeśli nazwa kategorii zawiera spację

Przyjrzyjmy się grupie sześciu specyfikatorów właściwości. Właściwość może używać tylko jednego specyfikatora z tej grupy sześciu wymienionych poniżej. Te sześć specyfikatorów to połączenie dwóch informacji. Pierwsza informacja dotyczy tego, czy właściwość jest tylko do odczytu (Visible), czy można ją edytować (Edit). Druga informacja dotyczy miejsca, w którym pojawia się właściwość, co może występować tylko w instancjach (InstanceOnly), tylko w edytorze Blueprints (DefaultsOnly) lub w obu (Anywhere).

Oto sześć specyfikatorów:

  • VisibleInstanceOnly : tylko do odczytu, pojawia się w oknie właściwości instancji.
  • VisibleDefaultsOnly :  tylko do odczytu, pojawia się w oknie właściwości Blueprints . 
  • VisibleAnywhere : tylko do odczytu, pojawia się w oknie właściwości instancji i w Blueprints.  
  • EditInstanceOnly :  Editable, pojawia się w oknie właściwości instancji. 
  • EditDefaultsOnly :  Editable, pojawia się w oknie właściwości edytora Blueprints. 
  • EditAnywhere :  Editable, pojawia się w oknie właściwości instancji oraz w edytorze Blueprints.

Wskaźniki do komponentów zwykle mają specyfikator VisibleAnywhere . Zapobiega to zmianie wskaźnika, ale właściwości komponentu można modyfikować.

Dwa poniższe specyfikatory wskazują, w jaki sposób właściwość zostanie użyta w wykresie zdarzeń Blueprint. Jeśli żaden specyfikator nie jest obecny, właściwość nie jest wyświetlana do użycia na wykresie zdarzeń.

  • BlueprintReadOnly : można go odczytać, ale nie można go modyfikować na wykresie zdarzeń.
  • BlueprintReadWrite :  można go odczytać i zmodyfikować na wykresie zdarzeń.

Istnieje inny typ specyfikatora zwanymi Metadata . Oto kilka przykładów:

  • DisplayName = “Property Name” :  nazwa, która będzie wyświetlana w oknie właściwości.
  • ClampMin = “N” : Minimalna wartość, jaką można wprowadzić dla właściwości. Może być używany we właściwościach typu integer i float.
  • ClampMax = “N” : Maksymalna wartość, jaką można wprowadzić dla właściwości. Może być używany we właściwościach typu integer i float. 

Aby użyć specyfikatora metadanych, użyj słowa meta  i umieść specyfikatory w nawiasach, jak pokazano poniżej: 

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Tutorial, 
          meta=(DisplayName="Special Identification", ClampMin="0", ClampMax="999") )
int32 SpecialId;

Przykładowe użycie:

Utwórz klasę C ++ o nazwie TestUProperty, używając klasy Actor jako klasy nadrzędnej. W pliku TestUProperty.h dodaj definicję trzech zmiennych, jak pokazano w poniższym kodzie:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestUProperty.generated.h"

UCLASS()
class TUTOPROJECT_API ATestUProperty : public AActor
{
  GENERATED_BODY()
	
public:	
  // Sets default values for this actor's properties
  ATestUProperty();
	
  UPROPERTY(EditInstanceOnly, Category = Tutorial)
  FString NPCName;
	
  UPROPERTY(VisibleDefaultsOnly, Category = Tutorial)
  float BaseClassVersion;
	
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Tutorial,
            meta=(DisplayName="Special Identification", ClampMin="0", ClampMax="999") )
  int32 SpecialId;

...

W pliku TestUProperty.cpp przypisz wartość początkową do zmiennej BaseClassVersion w konstruktorze:

#include "TestUProperty.h"

ATestUProperty::ATestUProperty()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

  BaseClassVersion = 0.3f;
}

...

Skompiluj kod i dodaj wystąpienie TestUProperty na poziomie. Nie będzie można przenieść instancji na poziom, ponieważ nie zdefiniowaliśmy dla niej RootComponent . Ale możesz wybrać instancję za pomocą narzędzia World Outliner .

Zobacz na karcie Details, że właściwości NPCName i Special Id pojawiają się i można je edytować, ale zmienna BaseClassVersion nie pojawia się, ponieważ specyfikator VisibleDefaultsOnly wskazuje, że powinien pojawić się tylko w Blueprints.

Special Identification to nazwa, która została przypisana do zmiennej SpecialId przy użyciu specyfikatora DisplayName . Specyfikatory ClampMin i ClampMax definiują zakres możliwych wartości dla SpecialId . Jeśli wprowadzisz wartość większą niż 999, edytor automatycznie zmieni się na 999.

Teraz utworzymy Blueprint w oparciu o klasę TestUProperty w języku C ++, aby zobaczyć zmienną BaseClassVersion . Przejdź do klasy C ++ folderu za pomocą Content Browser i kliknij prawym przyciskiem na TestUProperty klasie . Wybierz opcję Utwórz klasę Blueprint na podstawie TestUProperty (Create Blueprint class based on TestUProperty).

Teraz utworzymy Blueprint w oparciu o klasę TestUProperty C ++, aby zobaczyć zmienną BaseClassVersion . Przejdź do folderu C ++ klasy przy użyciu edytora w Content Browser i kliknij prawym przyciskiem myszy na TestUProperty klasie. Wybierz opcję Utwórz klasę Blueprint na podstawie TestUProperty. (Create Blueprint class based on TestUProperty).

Otwórz nowy Blueprint i kliknij przycisk Class Defaults. Zwróć uwagę, że zmienna BaseClassVersion pojawia się, ale nie można jej edytować, a zmienna NPCName nie pojawia się, ponieważ powinna pojawiać się tylko w instancjach.

Kliknij prawym przyciskiem myszy Blueprint EventGraph i wyszukaj, używając słowa „specjalne”. Zwróć uwagę, że pojawiają się funkcje Get i Set dla zmiennej SpecialId . Wynika to ze specyfikatora BlueprintReadWrite . Pozostałe dwie zmienne nie pojawiają się na liście Actions.

Lista Akcje planu wykorzystywała nazwę, która była w specyfikatorze DisplayName zmiennej SpecialId, ale węzły Blueprint używały nazwy zmiennej, takiej jak w kodzie. 


Źródło:https://romeroblueprints.blogspot.com/2020/10/the-uproperty-macro.html

Makro UClass()

Makro UClass() służy do wskazania klasy C ++ i sprawdzenie czy jest częścią systemu Unreal’s Reflection. Jest to konieczne, aby klasa C ++ została rozpoznana przez edytor Unreal Engine. Kolejną zaletą korzystania z UClass() jest to, że można używać systemu zarządzania pamięcią Unreal Engine w klasie C++.

Kompilacja projektu Unreal C++ przebiega w dwóch fazach. W pierwszej fazie UnrealHeaderTool (UHT) odczytuje pliki nagłówkowe C++ w poszukiwaniu makr Unreal generuje niezbędny kod, który zastąpi te makra. W drugiej fazie kompilator C ++ kompiluje wynikowy kod.

Makro UClass() jest umieszczane w wierszu powyżej definicji klasy C++. Poniższy kod to początek pliku TutoProjectCollectable.h, który został utworzony w części I.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TutoProjectCollectable.generated.h"

UCLASS()
class TUTOPROJECT_API ATutoProjectCollectable : public AActor
{
	GENERATED_BODY()

... 

Dla tej klasy UnrealHeaderTool wygeneruje kod, który zostanie umieszczony w pliku TutoProjectCollectable.generated.h oraz kod, który zastąpi makro GENERATED_BODY().

Przykładem kodu wygenerowanego przez UnrealHeaderTool jest funkcja StaticClass(), która służy do zwracania wskaźnika do klasy UClass reprezentującej klasę C++. Ta funkcja jest statyczna, co oznacza, że nie potrzebujesz instancji, aby ją wywołać, wystarczy użyć nazwy klasy.

Używamy funkcji StaticClass() w klasie ATutoProjectGameMode do zdefiniowania klasy HUD używanej przez Game Mode:

HUDClass = ATutoProjectHUD::StaticClass();

Makro UClass () ma parametry znane jako specyfikatory klas, które definiują zachowanie klasy. Dwa najczęstsze to:

  • Blueprintable: wskazuje, że Blueprints można tworzyć przy użyciu klasy C++ jako klasy nadrzędnej.
  • BlueprintType: wskazuje, że klasa C ++ może być używana jako typ zmiennej w Blueprints.

Specyfikator Blueprintable jest dziedziczony przez klasy potomne (podklasy). Na przykład klasa AActor ma ten specyfikator, więc jeśli utworzysz klasę C ++ przy użyciu klasy AActor jako klasy nadrzędnej, nie musisz umieszczać Blueprintable w nowej klasie.

Istnieje również specyfikator NotBlueprintable. Możesz go użyć, jeśli tworzysz podklasę AActor, ale nie chcesz zezwalać na tworzenie Blueprints w oparciu o Twoją klasę C++.

Przykładowe użycie:

Utwórz klasę C++ o nazwie TestUClass, używając klasy Object jako klasy nadrzędnej. Klasa Object jest klasą bazową dla obiektów Unreal Engine. Konieczne jest zaznaczenie opcji “Show All Classes“, aby klasa Object została wyświetlona jak na poniższym zrzucie ekranu.

W pliku TestUClass.h dodaj specyfikatory Blueprintable i BlueprintType w nawiasach UClass(), jak w poniższym kodzie:

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "TestUClass.generated.h"

UCLASS(Blueprintable, BlueprintType)
class TUTOPROJECT_API UTestUClass : public UObject
{
	GENERATED_BODY()
	
};

Skompiluj kod. Przejdź do nowego folderu klasy C++ za pomocą Content Browser oraz kliknij prawym przyciskiem myszy na klasę. Sprawdź, czy opcja Create Blueprint Class na podstawie TestUClass jest włączona (class based on) jak na poniższym zrzucie. Gdyby w naszym przykładzie nie było możliwości wykonania BlueprintType, ta opcja zostałaby wyłączona.

Utwórz nową zmienną w Blueprint. Kliknij Variable Type, aby wybrać typ. Użyj pola wyszukiwania, aby przefiltrować typy i zobaczyć, czy klasa TestUClass jest dostępna do użycia jako konkretny rodzaj. Gdyby nie miał BlueprintType w tej klasie, nie zostałby wyświetlony.


Źródło:https://romeroblueprints.blogspot.com/2020/10/the-uclass-macro.html

Wyświetlanie Komunikatów na Ekranie i w Dzienniku

Kiedy uczymy się programowania lub próbujemy znaleźć błąd w kodzie, bardzo przydatne jest renderowanie niektórych komunikatów na ekranie lub w dzienniku, aby sprawdzić, czy kod działa tak, jak powinien.

Zanim pokażę funkcje komunikatów, omówię typ FString silnika Unreal Engine. Ten typ jest używany do reprezentowania sekwencji znaków. Użyliśmy typu FString w pierwszej części, aby wyrenderować informacje o graczu na ekranie za pomocą klasy AHUD, jak pokazano w tym artykule.

Klasa FString ma funkcję o nazwie Printf(), która buduje FString na podstawie formatowania tekstu i innych parametrów wstawianych do tekstu. Poniższy przykład pokazuje użycie Printf():

FString PlayerInfo = FString::Printf(TEXT("Rank: %d - Name: %s - Health: %f"), 
                                     3, TEXT("Romero"), 0.4);

/*
  The PlayerInfo variable will have this content:
  "Rank: 3 - Name: Romero - Health: 0.4"
*/

Tekst formatujący zawiera znaki specjalne, które zaczynają się od “%” i zostaną zastąpione wartościami innych parametrów w odpowiedniej kolejności. Znaki specjalne reprezentują różne typy wartości. W przykładzie użyto tych znaków:

  • %d: Używany dla wartości całkowitych w formacie dziesiętnym.
  • %s: Używany do ciągów (sekwencji znaków).
  • %f: Używany do wartości zmiennoprzecinkowych.

Zachowaj ostrożność podczas używania “%s” ze zmiennymi FString. Musisz umieścić operator * przed nazwą zmiennej, jak pokazano w poniższym przykładzie.

FString Name = TEXT("Romero");

FString PlayerName = FString::Printf(TEXT("Player: %s"), *Name);

Aby wyświetlić wiadomość na ekranie, możesz użyć tego kodu:

if(GEngine)
{
  GEngine->AddOnScreenDebugMessage(-1, 8, FColor::Red, TEXT("Test message!"));
}

Spójrzmy na każdą część tego kodu:

  • GEngine: wskaźnik reprezentujący silnik. Aby z niego skorzystać, musisz dodać wiersz #include “Engine / Engine.h” na początku pliku cpp.
  • if (GEngine): Przed użyciem wskaźnika należy sprawdzić, czy jest on prawidłowy. Jeśli GEngine jest pustym wskaźnikiem, wyrażenie if będzie fałszywe, a kod wewnątrz if nie zostanie wykonany. Jeśli GEngine ma jakąkolwiek inną wartość, wyrażenie if będzie prawdziwe i kod zostanie wykonany.
  • AddOnScreenDebugMessage (): funkcja klasy UEngine, która wyświetla komunikat na ekranie.
  • -1: nazwa tego parametru to klucz. Jeśli wpiszesz wartość dodatnią, poprzednia wiadomość z tym samym kluczem zostanie usunięta z ekranu.
  • 8: Ten parametr wskazuje czas w sekundach, przez jaki wiadomość powinna być widoczna na ekranie.
  • FColor :: Red: kolor użyty w wiadomości.
  • TEKST („Wiadomość testowa!”): Wiadomość, która zostanie zapisana na ekranie. Może to być zmienna typu FString.

Edytor Unreal posiada panel Output Log, do którego można uzyskać dostęp z menu Window>Developer Tools>Output Log.

Aby wyświetlić komunikat w dzienniku, użyj tego kodu:

UE_LOG(LogTemp, Warning, TEXT("Test message!"));

LogTemp to kategoria Logów już dostępna w Unreal. Ostrzeżenie to poziom komunikatu dziennika. Komunikaty poziomu ostrzegawczego są wyświetlane na żółto, a komunikaty poziomu błędów są wyświetlane na czerwono. Parametr message akceptuje znaki specjalne w taki sam sposób jak funkcja Printf().

Przykładowe użycie:

Utwórz klasę C++, używając Actor jako klasy nadrzędnej. Użyj nazwy MsgActor dla nowej klasy.

Użyjemy Scene Component jako Root Component, aby aktor miał Location. W pliku MsgActor.h dodaj wskaźnik do USceneComponent poniżej deklaracji konstruktora.

public:	
  // Sets default values for this actor's properties
  AMsgActor();
	
  UPROPERTY(VisibleAnywhere)
  USceneComponent* RootScene;

W pliku MsgActor.cpp, wewnątrz konstruktora, zostanie utworzony USceneComponent, a odniesienie zostanie zapisane w zmiennej RootScene. Następnie RootScene zostanie zdefiniowany jako RootComponent tego aktora.

AMsgActor::AMsgActor()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

  RootScene = CreateDefaultSubobject<USceneComponent>("RootScene");
  RootComponent = RootScene;
}

W pliku MsgActor.cpp należy umieść ten kod w funkcji BeginPlay():

void AMsgActor::BeginPlay()
{
  Super::BeginPlay();
    
  FString Message = FString::Printf(TEXT("BeginPlay of %s - Location: %s"), 
                                    *(GetName()), *(GetActorLocation().ToString()) );
  
  if(GEngine)
  {
    GEngine->AddOnScreenDebugMessage(-1, 8, FColor::Red, Message);
  }
  
  UE_LOG(LogTemp, Warning, TEXT("%s"), *Message);  
}

Funkcja GetName() zwraca nazwę instancji. Funkcja GetActorLocation() zwraca FVector. Typ FVector ma funkcję ToString(), która konwertuje zawartość FVector na FString.

Skompiluj kod i dodaj aktor MsgActor do poziomu. Uruchom grę i zobacz komunikaty na ekranie oraz w oknie dziennika wyjściowego, jak pokazano na poniższym zrzucie ekranu.


Źródło:https://romeroblueprints.blogspot.com/2020/10/showing-messages-on-screen-and-in-log.html

Klasa TutoProjectGameMode – referencji funkcji klasy HUD

Stworzyliśmy zmienne stanu gry w klasie ATutoProjectGameMode z modyfikatorem chronionego dostępu, aby uniemożliwić innym klasom C ++ bezpośrednią modyfikację wartości tych zmiennych.

Jednak klasa ATutoProjectHUD musi mieć dostęp do niektórych z tych zmiennych, aby móc wyrenderować ich wartości na ekranie. Stwórzmy więc publiczne funkcje Get, które zwracają wartości tych zmiennych.

Zmienne, do których musi mieć dostęp klasa ATutoProjectHUD, to PlayerLevel, Score, Time i bGameOver.

Otwórz plik TutoProjectGameMode.h i dodaj deklaracje funkcji z poniższego kodu, które są po void ItemCollected().

...

public:
	ATutoProjectGameMode();

	void StartGame();

	void ItemCollected();

	int32 GetPlayerLevel();

	int32 GetScore();

	int32 GetTime();

	bool  IsGameOver();

Typ int32 przed nazwą funkcji wskazuje, że funkcja zwraca wartość całkowitą. W przypadku zmiennych logicznych (prawda / fałsz), konwencja funkcji Get polega na użyciu Is plus nazwa zmiennej, jak w IsGameOver ().

Funkcje Get są bardzo proste, po prostu zwracają wartości zmiennych. Otwórz plik TutoProjectGameMode.cpp i dodaj definicje funkcji na końcu pliku, jak pokazano w kodzie poniżej.

int32 ATutoProjectGameMode::GetPlayerLevel()
{
	return PlayerLevel;
}

int32 ATutoProjectGameMode::GetScore()
{
	return Score;
}

int32 ATutoProjectGameMode::GetTime()
{
	return Time;
}

bool ATutoProjectGameMode::IsGameOver()
{
	return bGameOver;
}

Jeśli chcesz, aby inne klasy modyfikowały wartość chronionej lub prywatnej zmiennej, możesz utworzyć publiczną funkcję Set. Nie będziemy potrzebować funkcji Set w klasie ATutoProjectGameMode, ale poniżej znajduje się przykład funkcji Set, która modyfikuje zmienną Score.

void ATutoProjectGameMode::SetScore(int32 NewScore)
{
	Score = NewScore;
}

Jedną z zalet używania funkcji Set jest to, że można sprawdzić poprawność nowej wartości przed zapisaniem jej w zmiennej.

Inną zmianą, którą należy wprowadzić w klasie ATutoProjectGameMode, jest zdefiniowanie, że klasa ATutoProjectHUD będzie używana jako HUD gry. Odbywa się to w pliku TutoProjectGameMode.cpp w konstruktorze ATutoProjectGameMode ().

Konieczne jest dodanie #include „TutoProjectHUD.h”, aby klasa ATutoProjectGameMode znalazła definicję klasy ATutoProjectHUD. Przypisanie nowej klasy HUD odbywa się za pomocą tej linii:

HUDClass = ATutoProjectHUD::StaticClass();

Funkcja StaticClass() jest funkcją Static, która zwraca odwołanie do klasy. Funkcje statyczne należą do klasy, to znaczy nie potrzebują instancji do wykonania.

Początek pliku TutoProjectGameMode.cpp wygląda następująco:

#include "TutoProjectGameMode.h"
#include "TutoProjectCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "TutoProjectHUD.h"

ATutoProjectGameMode::ATutoProjectGameMode()
{
  // set default pawn class to our Blueprinted character
  static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(
         TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
	
  if (PlayerPawnBPClass.Class != NULL)
  {
    DefaultPawnClass = PlayerPawnBPClass.Class;
  }

  HUDClass = ATutoProjectHUD::StaticClass();
}

...

Przypisanie DefaultPawnClass różni się od przypisania HUDClass, ponieważ jest używany Blueprint o nazwie ThirdPersonCharacter, który został utworzony przez szablon Third Person. Klasą nadrzędną tego Blueprinta jest TutoProjectCharacter.

Gdyby te przypisania klas w trybie gry były wykonywane w Blueprints, wyglądało by to tak w zakładce klas naszego Game Mode:


Źródło:https://romeroblueprints.blogspot.com/2020/08/tutoprojectgamemode-class-get-functions.html

Klasa TutoProjectHUD – używanie pointerów C++

W tym artykule zamierzamy stworzyć klasę TutoProjectHUD odpowiedzialną za renderowanie na ekranie zmiennych stanu gry. Jak widzimy te zmienne należą do klasy TutoProjectGameMode. Będziemy musieli użyć wskaźnika, aby uzyskać dostęp do publicznych zmiennych i funkcji klasy TutoProjectGameMode w klasie TutoProjectHUD.

Najpierw utwórzmy klasę TutoProjectHUD. W Content Browser przejdź do folderu TutoProject, który znajduje się w folderze C ++ Classes. Kliknij prawym przyciskiem myszy wolne miejsce w Content Browser i wybierz opcję New C+ Class …, tak jak pokazano na poniższym obrazku.

Na następnym ekranie musisz wybrać klasę nadrzędną, która będzie używana jako typ dla nowej klasy. Przewiń opcje, aż znajdziesz klasę HUD. Wybierz klasę HUD i kliknij przycisk Dalej.

W polu Name wpisz TutoProjectHUD. W polu Path zachowaj domyślną lokalizację foldera projektu. Następnie kliknij przycisk Create Class.

W tym artykule skupimy się tylko na tym, jak utworzyć wskaźnik do klasy TutoProjectGameMode i jak uzyskać odniesienie do trybu gry używanego przez grę. W innym artykule zobaczymy funkcje używane do renderowania treści na ekranie.

Pointer przechowuje adres pamięci. Możemy użyć wskaźnika, aby odwołać się do instancji klasy. Definiujesz, że zmienna jest wskaźnikiem, używając operatora * obok typu zmiennej. Poniższy wiersz kodu przedstawia definicję zmiennej o nazwie TutoProjectGameMode, która jest wskaźnikiem do instancji klasy ATutoProjectGameMode.

ATutoProjectGameMode* TutoProjectGameMode;

Powyższa linia definiuje wskaźnik, ale nie przechowuje jeszcze odwołania do instancji klasy ATutoProjectGameMode. W funkcji BeginPlay() otrzymamy referencję do instancji klasy ATutoProjectGameMode używanej przez grę i zapiszemy ją w naszym wskaźniku.

Otwórz plik TutoProjectHUD.h, dodaj etykietę protected: oraz deklaracje wskaźnika TutoProjectGameMode i funkcji BeginPlay(), jak pokazano w kodzie poniżej.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "TutoProjectHUD.generated.h"

UCLASS()
class TUTOPROJECT_API ATutoProjectHUD : public AHUD
{
	GENERATED_BODY()

protected:

	class ATutoProjectGameMode* TutoProjectGameMode;

	virtual void BeginPlay() override;
		
};

Zauważ, że istnieje słowo kluczowe class przed definicją wskaźnika TutoProjectGameMode. Było to konieczne, ponieważ klasa ATutoProjectHUD nie zna klasy ATutoProjectGameMode. Jest to sposób na poinformowanie kompilatora, że istnieje klasa o nazwie ATutoProjectGameMode i że definicja klasy zostanie znaleziona później podczas kompilacji. Jest to również znane jako Forward Declaration.

Zamiast używać słowa kluczowego class, możesz dodać #include „TutoProjectGameMode.h” na początku, ponieważ ten plik nagłówkowy zawiera definicję klasy ATutoProjectGameMode. Jeśli to możliwe, unikaj dodawania więcej #include w plikach nagłówkowych .h. Najlepiej umieścić je w plikach wdrożeniowych, czyli w .cpp.

Plik TutoProjectHUD.cpp musi mieć następującą zawartość:

#include "TutoProjectHUD.h"
#include "TutoProjectGameMode.h"

void ATutoProjectHUD::BeginPlay()
{
  Super::BeginPlay();

  TutoProjectGameMode = GetWorld()->GetAuthGameMode<ATutoProjectGameMode>();
}

Przeanalizujmy linię odpowiedzialną za uzyskanie i przechowywanie odniesienia do trybu gry w naszym wskaźniku:

  • TutoProjectGameMode: To jest nasza zmienna wskaźnikowa.
  • GetWorld(): ta funkcja zwraca wskaźnik do wystąpienia klasy UWorld, która reprezentuje bieżącą mapę.
  • ->: Gdy masz wskaźnik, musisz użyć operatora ->, aby uzyskać dostęp do funkcji i zmiennych instancji.
  • GetAuthGameMode(): Ta funkcja zwraca wskaźnik do instancji trybu gry używanej przez mapę. Funkcja używa template parameter C ++, który jest klasą przekazywaną między <>.

Funkcja GetAuthGameMode podejmie próbę przesłania trybu gry do klasy ATutoProjectGameMode. Cast to konwersja typu. Ta konwersja będzie działać tylko wtedy, gdy mapa używa trybu gry TutoProjectGameMode (game mode wybierany w World Settings). Jeśli nie znasz trybu gry w Unreal Engine, sprawdź mój inny artykuł.

Następny obraz pokazuje, jak wyglądałaby funkcja / zdarzenie BeginPlay (), gdyby zostało to zrobione w Blueprints.

W celach dydaktycznych nazwę klasy używaną w węźle Cast umieściłem jako ATutoProjectGameMode, aby ułatwić skojarzenie z kodem C ++. Ale pamiętaj, że przedrostek A jest konwencją Unreal Engine, która istnieje tylko w kodzie C ++.

Nie zapomnij umieścić linii #include „TutoProjectGameMode.h” na początku pliku TutoProjectHUD.cpp, w przeciwnym razie podczas kompilacji pojawi się następujący błąd:

error C2027: use of undefined type 'ATutoProjectGameMode'
pointer to incomplete class type is not allowed

Jeśli pojawi się błąd tego typu związany z jakąkolwiek klasą Unreal Engine, przejdź do Unreal Engine API Reference i wyszukaj klasę. Dokumentacja pokazuje, jak dołączyć plik nagłówkowy klasy. Jako przykład, poniższy obrazek przedstawia kilka informacji o klasie UCanvas.


Źródło:https://romeroblueprints.blogspot.com/2020/08/tutoprojecthud-class-using-c-pointers.html

Klasa TutoProjectCollectable – Components

W tym artykule stworzymy klasę TutoProjectCollectable i zdefiniujemy jej komponenty. Ta klasa jest aktorem i reprezentuje przedmiot, który może zebrać gracz.

W Content Browser przejdź do folderu TutoProject, który znajduje się w folderze C ++ Classes. Kliknij prawym przyciskiem myszy wolne miejsce i wybierz opcję “New C ++ Class …” obrazku.

Na następnym ekranie wybierz Actor jako klasę parent (nadrzędną) i kliknij przycisk Next.

Na ostatnim ekranie wpisz TutoProjectCollectable w polu Name i kliknij przycisk Create Class

Użyjemy Static Mesh component, aby wizualnie przedstawić tę klasę w grze. Static Mesh, która ma być używany, to SM_Statue z materiałem M_Metal_Gold. Są nieodłączną częścią zawartości Starter Content. Static Mesh będzie wyglądać następująco:

Otwórz plik TutoProjectCollectable.h. Zwróć uwagę, że kod wygenerowany przez Unreal Engine dwukrotnie używa modyfikatora public: access. To jest prawidłowy kod, ale wolimy trzymać publiczne zmienne i funkcje w jednym miejscu. Skopiuj deklarację funkcji Tick (float DeltaTime), umieść ją pod konstruktorem ATutoProjectCollectable() i usuń drugi public: block.

Utworzymy dwa wskaźniki dla komponentów, jeden dla składnika Static Mesh, a drugi dla składnika sceny. Komponent sceny zostanie użyty jako Root, abyśmy mogli zdefiniować względne położenie Static Mesh. Publiczny: blok TutoProjectCollectable.h będzie wyglądał następująco:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TutoProjectCollectable.generated.h"

UCLASS()
class TUTOPROJECT_API ATutoProjectCollectable : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATutoProjectCollectable();

	// Called every frame
	virtual void Tick(float DeltaTime) override;
    
   	UPROPERTY(VisibleAnywhere)
	USceneComponent* RootScene;

	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* StaticMesh;

...

Omówimy makro UPROPERTY () w następnym artykule.

Konfiguracja komponentów odbywa się w pliku TutoProjectCollectable.cpp wewnątrz konstruktora ATutoProjectCollectable(). Poniższy kod przedstawia tworzenie USceneComponent, do którego będzie odwoływał się wskaźnik RootScene. Następnie następuje przypisanie wskaźnika do RootComponent w celu wskazania, że ten składnik będzie głównym elementem Actor Root.

RootScene = CreateDefaultSubobject<USceneComponent>("RootScene");
RootComponent = RootScene;

Tworzenie składnika Static Mesh jest również wykonywane za pomocą funkcji CreateDefaultSubobject(). Static Mesh Component jest dołączany do komponentu sceny za pomocą funkcji SetupAttachment(). Następnie odpowiedź na kolizję jest definiowana jako nakładanie się, aby Static Mesh nie blokował ruchu gracza.

StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>("StaticMesh");
StaticMesh->SetupAttachment(RootScene);
StaticMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);

Gdyby dwa komponenty zostały utworzone w Blueprincie, wyglądałyby to następująco:

Stworzyliśmy Static Mesh Component, ale teraz musimy zdefiniować, który Static Mesh będzie używany. Do wyszukiwania pliku Static Mesh w projekcie używana jest struktura o nazwie FObjectFinder. Jeśli wyszukiwanie powiedzie się, Static Mesh zostanie przypisany do komponentu.

ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFile(
    TEXT("/Game/StarterContent/Props/SM_Statue.SM_Statue"));

  if (MeshFile.Succeeded())
  {
    StaticMesh->SetStaticMesh(MeshFile.Object);
  }

Aby uzyskać ścieżkę do zasobu w projekcie, kliknij prawym przyciskiem myszy na zasób i wybierz opcję Copy Reference, jak pokazano na poniższym zrzucie ekranu.

Definicja materiału używanego przez Static Mesh Component jest bardzo podobna do definicji Static Mesh. Jeśli materiał zostanie znaleziony, zostanie zdefiniowany jako materiał indeksu 0 Static Mesh.

Kod konstruktora w pliku TutoProjectCollectable.cpp będzie wyglądał następująco:

// Sets default values
ATutoProjectCollectable::ATutoProjectCollectable()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

  RootScene = CreateDefaultSubobject<USceneComponent>("RootScene");
  RootComponent = RootScene;

  StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>("StaticMesh");
  StaticMesh->SetupAttachment(RootScene);
  StaticMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);

  ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFile(
    TEXT("/Game/StarterContent/Props/SM_Statue.SM_Statue"));

  if (MeshFile.Succeeded())
  {
    StaticMesh->SetStaticMesh(MeshFile.Object);
  }

  ConstructorHelpers::FObjectFinder<UMaterial> MaterialFile(
    TEXT("/Game/StarterContent/Materials/M_Metal_Gold.M_Metal_Gold"));

  if (MaterialFile.Succeeded())
  {
    StaticMesh->SetMaterial(0, MaterialFile.Object);
  }
}

Źródło:https://romeroblueprints.blogspot.com/2020/08/tutoprojectcollectable-class-components.html

Float, Struct, oraz FVector

W tym artykule przyjrzymy się niektórym koncepcjom potrzebnym do ustawienia Aktora w przestrzeni 3D.

Najpierw porozmawiamy o zmiennoprzecinkowych typach zmiennych. Te typy zmiennych są używane do reprezentowania liczb dziesiętnych. Główne typy to float, który ma 32 bity i double, który ma 64 bity.

Większość kodu Unreal Engine używa typu zmiennoprzecinkowego dla wartości dziesiętnych. Oto kilka przykładów zmiennych typu float:

float var1;

float var2 = 7.45F;

float var3 = 8.f;  // To samo co 8.0f 

Przyrostek f lub F jest konieczny, aby wskazać, że wartość jest typu float, ponieważ domyślnie wartości z kropką są oznaczane jako typ double.

Kolejną ważną koncepcją jest konstrukcja. Struktura służy do gromadzenia w jednym miejscu kilku powiązanych zmiennych. Zmienne, które są częścią struktury, mogą mieć różne typy. Możesz także mieć struktury zawierające inne struktury.

Aby zdefiniować strukturę w C ++, użyj słowa kluczowego struct. W C ++ możliwe jest dodawanie funkcji do struktur, czyniąc je bardzo podobnymi do klasy. Jedyną różnicą między strukturą a klasą w C ++ jest to, że elementy struktury są domyślnie publiczne, a elementy klasy są domyślnie prywatne.

W Unreal Engine struktury są reprezentowane przez typy, które mają przedrostek F, na przykład jako FVector i FTransform. Jeśli używasz typu / klasy, która ma przedrostek U lub A, musisz utworzyć wskaźnik. Ale dla typów z prefiksem F nie trzeba tworzyć wskaźnika.

Struktura FVector zawiera trzy zmienne typu float, którymi są X, Y i Z. Poniższy przykład ilustruje tworzenie dwóch zmiennych typu FVector zawierających te same wartości dla X, Y i Z.

FVector MyVector1;
MyVector1.X = 500.f;
MyVector1.Y = 700.f;
MyVector1.Z = 300.f;

FVector MyVector2 = FVector(500.f, 700.f, 300.f);

Zauważ, że aby uzyskać dostęp do zmiennych strukturalnych, pomiędzy nazwą struktury a nazwą zmiennej użyto kropki (.). Używasz -> tylko podczas używania wskaźników.

Klasa AActor ma funkcję ustawiania położenia aktora w przestrzeni 3D. Nazwa funkcji to SetActorLocation () i otrzymuje ona jako parametr FVector z nową lokalizacją.

Poniższy przykład przedstawia dwa sposoby wywołania metody SetActorLocation ():

FVector NewLocation = FVector(300.f, 200.f, 100.f);
SetActorLocation(NewLocation);

//It is the same as:

SetActorLocation( FVector(300.f, 200.f, 100.f) );

Źródło:https://romeroblueprints.blogspot.com/2020/08/float-struct-and-fvector.html

Klasa TutoProjectCollectable – UPROPERTY()

W Unreal Engine istnieje makro o nazwie UPROPERTY(), które służy do udostępniania zmiennych edytorowi.

Stwórzmy zatem dwie zmienne w klasie TutoProjectCollectable, aby zobaczyć w praktyce użycie UPROPERTY().

Otwórz plik TutoProjectCollectable.h i umieść definicję zmiennych UEVersion i SpecialId poniżej zmiennych składowych, jak pokazano w poniższym kodzie:

...

public:	
	// Sets default values for this actor's properties
	ATutoProjectCollectable();

	// Called every frame
	virtual void Tick(float DeltaTime) override;
    
   	UPROPERTY(VisibleAnywhere)
	USceneComponent* RootScene;

	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* StaticMesh;
    
	UPROPERTY(VisibleAnywhere, Category = Tutorial)
	float UEVersion;

	UPROPERTY(EditAnywhere, Category = Tutorial)
	int32 SpecialId;

...

Makro UPROPERTY może odbierać specyfikatory i jest związane z definicją zmiennej znajdującej się poniżej. Przykładami specyfikatorów są VisibleAnywhere, co oznacza, że właściwość nie może być edytowana, ale jest widoczna we wszystkich oknach właściwości, oraz EditAnywhere, co oznacza, że właściwość można edytować we wszystkich oknach właściwości.

Innym specyfikatorem UPROPERTY jest Category, która określa, w jakiej kategorii właściwość będzie wyświetlana w oknie właściwości.

Ponieważ zmienna UEVersion nie może być edytowana ze względu na użycie VisibleAnywhere, przypiszemy jej wartość w konstruktorze, który znajduje się w pliku TutoProjectCollectable.cpp:

...

// Sets default values
ATutoProjectCollectable::ATutoProjectCollectable()
{

	UEVersion = 4.25f;

...

Sprawdź użycie UPROPERTY, skompiluj projekt i dodaj TutoProjectCollectable do poziomu. Wybierz instancję i zobacz zmienne UPROPERTY na karcie Details w kategorii Tutorial. Zmienna UEVersion jest tylko do odczytu, a zmienną SpecialId można edytować, jak pokazano na tym obrazku:

Uwaga dotycząca wskaźników do komponentów – są one zdefiniowane jako UPROPERTY ze specyfikatorem VisibleAnywhere. Oznacza to, że wskaźnika nie można zmienić w edytorze, ale właściwości komponentu można modyfikować w edytorze. Nie ma potrzeby definiowania ich jako EditAnywhere.

Stworzymy zmienne do przechowywania granic obszaru gry. Będą one używane podczas tworzenia nowych instancji TutoProjectCollectable i podczas działania gry.

Umieść definicję nowych zmiennych w pliku TutoProjectCollectable.h po zmiennych UEVersion i SpecialId, jak poniżej:

...

	UPROPERTY(VisibleAnywhere, Category = Tutorial)
	float UEVersion;

	UPROPERTY(EditAnywhere, Category = Tutorial)
	int32 SpecialId;

	UPROPERTY(EditDefaultsOnly, Category = "Game Area")
	float XMinimum;
		
	UPROPERTY(EditDefaultsOnly, Category = "Game Area")
	float XMaximum;

	UPROPERTY(EditDefaultsOnly, Category = "Game Area")
	float YMinimum;

	UPROPERTY(EditDefaultsOnly, Category = "Game Area")
	float YMaximum;

	UPROPERTY(EditDefaultsOnly, Category = "Game Area")
	float FloorZValue;

...

Zmienne obszaru gry są definiowane jako UPROPERTY ze specyfikatorem EditDefaultsOnly. Oznacza to, że te zmienne można edytować w edytorze Blueprints, jeśli utworzysz Blueprints przy użyciu TutoProjectCollectable jako klasy nadrzędnej, ale nie pojawią się one w oknach właściwości instancji.

Istnieje inny specyfikator UPROPERTY o nazwie EditInstanceOnly. W tym przypadku zmienna pojawia się w oknie właściwości instancji, ale nie pojawia się w edytorze Blueprint.

Zmienne są pogrupowane w kategorii o nazwie „Obszar gry”. Użycie cudzysłowów jest konieczne ze względu na spację, która istnieje w nazwie kategorii.

Wartości zmiennych obszaru gry są przypisane w pliku TutoProjectCollectable.cpp, wewnątrz konstruktora:

...

// Sets default values
ATutoProjectCollectable::ATutoProjectCollectable()
{

	UEVersion = 4.25f;

	//Default Values for Game Area
	XMinimum	= -1800.0f;
	XMaximum	=  1000.0f;
	YMinimum	= -1400.0f; 
	YMaximum	=  1400.0f;
	FloorZValue	=   130.0f;

...

Źródło:https://romeroblueprints.blogspot.com/2020/09/tutoprojectcollectable-class-uproperty.html