Programmeren van een 3D omgeving in Unreal Engine – C++

print
Deze handleiding maakt deel uit van het programmeertraject:


Inhoud


Wat vooraf ging


Inleiding

Blueprint Visual Scripting is gemakkelijk, visueel en toegankelijk maar voor complexe code kunnen ze ook snel onoverzichtelijk worden. Als code complex dreigt te worden kunnen, iets gevorderde programmeurs, opteren om deze complexere code te programmeren in C++.

In deze handleiding zien we hoe we een C++ klasse kunnen aanmaken en gebruiken vanuit Blueprint Project.

Wat deze handleiding dus niet tracht te doen is:

  • leren programmeren in C++, dit is een cursus op zich.
  • starten vanuit een C++-project, we starten nog steeds vanuit onze vertrouwde Blueprint omgeving.

Wat handleiding wel betracht is:

  • Vertrekkend vanuit een Blueprint Project een C++ basisklasse integreren.
  • Een introductie in C++.

wilt u toch een stapje verder gaan in C++ dan verwijs ik u graag naar de officiële handleiding.

Situering van deze handleiding binnen Unreal Engine


Installatie van Visual Studio

Het programmeren zelf gebeurt in Visual Studio. De Visual Studio Community versie is gratis en voldoende voor ons.

  • Lees hier hoe u Visual Studio Community kunt installeren.
  • Bij het installeren zal u moeten aanduiden wat u wenst te installeren. U kunt uiteraard alles installeren maar ziet dat u zeker Game development met C++ aanvinkt.

Voor het installeren Unreal Engine en Visual Studio 2019 verwijs ik naar de officiële handleiding.


Een C++ klasse toevoegen

  • We starten een BlueprintBlankNo Starter Content project als testomgeving en geef het een passende naam (bv. TestC).

Wat wensen we te doen?

We wensen een “basiskarakter” aan te maken via een C++ klasse die bruikbaar moet zijn in Blueprints.

Dit “basiskarakter” is, uiteraard, van het type Character (de Parent Class zal dus Character zijn).

We gaan 2 eenvoudige eigenschappen/variabelen toevoegen:

  • Health (de gezondheid van ons karakter)
  • IsDead (nagaan of ons karakter dood is )

We gaan 2 functionaliteiten toevoegen:

  • CalculateHealth
  • CalculateDead

We gaan een C++ klasse toevoegen voor een basiskarakter.

  • In de Content Browser klikt u op Add NewNew C++ Class (of ga naar FileNew C++ Class indien u dit niet ziet).

Vervolgens moet u de basisklasse (Parent class) kiezen voor onze nieuwe klasse.

  • We wensen een nieuw basiskarakter te maken dus u kiest voor Character en klik op Next.

Vervolgens moet u een naam geven (en eventueel een pad).

  • Geef het bv. de naam BaseCharacter.

Merk op dat er een Header File BaseCharacter.h en een Source File BaseCharacter.cpp wordt aangemaakt.

Een Header File bevat de declaraties van de te gebruiken variabelen en functies.

Een Source File bevat de eigenlijke programmeercode van de functies.

  • Klik op Create Class.

Visual Studio start op en creëert de klasse, dit kan een tijdje duren.

Als alles goed gelukt is start Visual Studio op met de twee bestanden BaseCharacter.h en BaseCharacter.cpp die de kern van onze code bevat. Ik heb ze hier naast mekaar geplaatst.

BaseCharacter.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

UCLASS()
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

BaseCharacter.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "BaseCharacter.h"

// Sets default values
ABaseCharacter::ABaseCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void ABaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}
  • U mag eventueel Unreal Engine beëindigen want we gaan nu verder in Visual Studio.

De basiscode

BaseCharacter.h

Zoals u ondertussen weet bevat een .h (een Header) enkel declaratie van de variabelen en de hoofding (Header) van de methoden/functies.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

UCLASS()
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};
  • De lijnen die beginnen met // zijn commentaarlijnen, deze worden niet gecompileerd en dienen enkel ter verduidelijking van de code. Toch zijn deze belangrijk en worden ze het best veelvuldig gebruikt.
// Fill out your copyright notice in the Description page of Project Settings.
  • #pragma once – is een directive die aangeeft dat een bestand, opgenomen via #include, slechts één keer wordt opgenomen (geopend) door de compiler bij het compileren van het broncodebestand. Dit leidt tot een snellere compilatie.
  • #include – is een directive die aangeeft dat een bestand mee gecompileerd moet worden, en zo dus deel gaat uitmaken van de broncode.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

Merk op dat “GameFramework/Character.h”, de gekozen Parent Class, wordt opgenomen (Included).

  • De klasse die wordt aangemaakt is een Unreal Class UCLASS().
  • U herkent de door ons gekozen naam van het project gevolgd door _API: TESTC_API,
  • de door ons gekozen naam van de klasse, met een bijkomende voorgaande A (de A van Actor) ABaseCharacter
  • en de naam van de Parent Class ACharacter, eveneens met bijkomende voorafgaande A, want een Character is ook een Actor.
  • de klasse is tevens public: class TESTC_API ABaseCharacter : public ACharacter (zie zodadelijk)
  • GENERATED_BODY() construeert de klasse.
UCLASS()
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

...

};

Een Class bevat eigenschappen (variabelen) en methoden (functies) die bij mekaar horen en een geheel vormen en zo de basis vormen van een object (Object georiënteerd programmeren).

Standaard zijn er geen variabelen aangemaakt, wel een aantal functies. Deze variabelen en functies hebben een bereik (Member Access Control) bepaald door:

  • private – enkel bereikbaar vanuit dezelfde klasse
  • protected – bereikbaar vanuit dezelfde klasse en afgeleide klassen
  • public – van overal bereikbaar

De functie ABaseCharacter(); is de zogenaamde constructor, hierin worden standaardwaarden aan eigenschappen/variabelen toegekend.

public:
	// Sets default values for this character's properties
	ABaseCharacter();

Een virtual override functie kan overschreven worden (virtual) en is overschreven (override). Dit polymorphisme is een eigenschap van object georiënteerd programmeren.

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

Wellicht klinken de functienamen BeginPlay() en Tick() u bekend!

Merk op dat de functie Tick(float DeltaTime) een input-parameter heeft van het type float.

Ik herneem nog even de volledige code van BaseCharacter.h:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

UCLASS()
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

BaseCharacter.cpp

Een .cpp-bestand bevat de uitgewerkte code.

// Fill out your copyright notice in the Description page of Project Settings.

#include "BaseCharacter.h"

// Sets default values
ABaseCharacter::ABaseCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void ABaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}
  • Eerst en vooral merk op dat de verwante Header BaseCharacter.h wordt opgenomen.
#include "BaseCharacter.h"
  • Vervolgens wordt de constructor uitgewerkt. Hier wordt de eigenschap PrimaryActorTick.bCanEverTick op true gezet via PrimaryActorTick.bCanEverTick = true;
    zodat de functionaliteit Tick() iedere frame wordt aangeroepen. De variabelen, die we zo dadelijk gaan aanmaken, zouden we hier eventueel een beginwaarde kunnen geven.
// Sets default values
ABaseCharacter::ABaseCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}
  • Vervolgens worden de functionaliteiten geprogrammeerd. Merk op dat de hoofding (signiature of header) van de functies identiek is aan de declaratie in het Header-bestand. Het eerste deel van de functienaam ABaseCharacter::BeginPlay() voor de :: verwijst naar de klasse waarin de functie is gedeclareerd.
  • Het woordje Super:: verwijst naar de Parent Class, in ons voorbeeld de ACharacter class. Concreet worden in onderstaande voorbeelden de functionaliteit van de Parent Class overgenomen zonder extra toevoeging.
// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void ABaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}
  • Merk op dat deze functies niet veel meer doen dan de basisfuncties, via Super::, aanroepen, er is geen extra functionaliteit toegevoegd (maar we zouden dit nog steeds kunnen doen).

Ik herneem nog even de volledige code van BaseCharacter.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "BaseCharacter.h"

// Sets default values
ABaseCharacter::ABaseCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void ABaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

Variabelen en functies declareren

Declaraties gebeuren in het Header-bestand.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

UCLASS()
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

De Class toegankelijk maken voor Blueprints

De Class toegankelijk maken voor Blueprints gebeurt door de toevoeging van een zogenaamde Class Specifier aan de Class, meer specifiek via de toevoeging van Blueprintable aan UCLASS().

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

//Klasse toegankelijk maken voor Blueprints
UCLASS(Blueprintable)
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

Variabelen declareren

We gaan 2 eenvoudige eigenschappen/variabelen toevoegen:

  • Health (de gezondheid van ons karakter), aanpasbaar via Blueprints. De eigenschap Health zal een getal bevatten, laat ons zeggen een decimaal getal, een float, met een beginwaarde van 100 float Health = 100;.
  • isDead (nagaan of ons karakter dood is ), enkel te lezen, en dus niet aan te passen via Blueprints. De eigenschap isDead kan enkel waar of onwaar zijn (true of false, een boolean dus) en om te beginnen zal deze false zijn bool isDead = false;.

Een variabele wordt voorafgegaan door de directive UPROPERTY() die duidt hoe de variabele kan gebruikt worden en zorgt er ook voor dat de variabele, wanneer deze niet meer wordt gebruikt, verwijderd wordt uit het geheugen (Garbage Collection).

Volgende UPROPERTY() maakt de variabele aanpasbaar (BlueprintReadWrite, EditAnywhere) en voegt een categorie “BaseCharacter” toe:

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "BaseCharacter").

Volgende UPROPERTY() maakt de variabele enkel leesbaar (BlueprintReadOnly, VisibleAnywhere) en voegt een categorie “BaseCharacter” toe:

UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "BaseCharacter").

De variabelen worden dus:

public:

	//De variabele Health is aanpasbaar binnen Blueprints
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "BaseCharacter")
	float Health = 100;

	//De variabele isDead is enkel leesbaar en kan niet worden aangepast via Blueprints
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "BaseCharacter")
	bool isDead = false;

Functies declareren

We gaan 2 functies declareren:

  • CalculateHealth – deze functie zal de variabele Health vermeerderen met een zekere waarde die wordt meegegeven als een parameter (aangezien de Health zowel kan toenemen als afnemen (parameter met een negatieve waarde) kiezen we een neutrale naam voor deze parameter (bv. Delta). Deze functie moet kunnen aangeroepen worden vanuit Blueprints.
  • CalculateDead – deze functie berekent of ons karakter dood is (Health <= 0). Deze functie wordt aangeroepen vanuit de CalculateHealth-functie en niet vanuit Blueprints.

Nat als bij eigenschappen/variabelen kunt u een functie laten voorafgaan door een directive UFUNCTION() die aangeeft hoe de functie kan gebruikt worden.

Volgende UFUNCTION() maakt de functie aanroepbaar (BlueprintCallable) vanuit Blueprints:

UFUNCTION(BlueprintCallable, Category = "BaseCharacter").

De functies worden nu:

	//De functie CalculateDead is enkel gekend binnen deze klasse en niet in Blueprints
	virtual void CalculateDead();

	//De functie CalculateHealth is aanroepbaar vanuit Blueprints
	UFUNCTION(BlueprintCallable, Category = "BaseCharacter")
	virtual void CalculateHealth(float delta);

Het woordje virtual geeft aan dat deze functie eventueel kan overschreven worden is andere afgeleide klassen.
Het woordje void geeft aan dat er geen berekende waarde wordt teruggegeven als output.

De volledige code van BaseCharacter.h:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

//Klasse toegankelijk maken voor Blueprints
UCLASS(Blueprintable)
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:

	//De variabele Health is aanpasbaar binnen Blueprints
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "BaseCharacter")
	float Health = 100;

	//De variabele isDead is enkel leesbaar en kan niet worden aangepast via Blueprints
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "BaseCharacter")
	bool isDead = false;

	//De functie CalculateDead is enkel gekend binnen deze klasse en niet in Blueprints
	virtual void CalculateDead();

	//De functie CalculateHealth is aanroepbaar vanuit Blueprints
	UFUNCTION(BlueprintCallable, Category = "BaseCharacter")
	virtual void CalculateHealth(float delta);

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

Functies programmeren

Functies programmeren (variabelen moeten zoals u weet niet geprogrammeerd worden) gebeurt in het cpp-bestand.

// Fill out your copyright notice in the Description page of Project Settings.

#include "BaseCharacter.h"

// Sets default values
ABaseCharacter::ABaseCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void ABaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

We gaan 2 functies programmeren:

  • CalculateHealth – deze functie zal de variabele Health vermeerderen met een zekere waarde die wordt meegegeven als een parameter (aangezien de Health zowel kan toenemen als afnemen (parameter met een negatieve waarde) kiezen we een neutrale naam voor deze parameter (bv. Delta). Deze functie moet kunnen aangeroepen worden vanuit Blueprints.
  • CalculateDead – deze functie berekent of ons karakter dood is (Health <= 0). Deze functie wordt aangeroepen vanuit de CalculateHealth-functie en niet vanuit Blueprints.

De code voor de functies is (voeg deze gewoon onderaan het cpp-bestand toe:

//Implementeer de CalculateHealth functie.
void ABaseCharacter::CalculateHealth(float Delta)
{
	Health += Delta;
	CalculateDead();
}

//Implementeer de CalculateDead functie.
void ABaseCharacter::CalculateDead()
{
	if (Health <= 0)
		isDead = true;
	else
		isDead = false;

}

Veranderingen tijdens het editeren weergeven

Als we tijdens het werken/editeren in Unreal Engine de waarde van de variabele Health zouden wijzigen wordt dit niet meteen weergeven. We moeten Unreal Engine laten weten dat de eigenschap gewijzigd is (PropertyChanged).

Zonder in detail te gaan, voegen we nog onderstaande code toe die enkel uitgevoerd wordt tijdens het editeren.

In BaseCharacter.h:

//Correct updaten van waarden in de editor
#if WITH_EDITOR
	virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

In BaseCharacter.cpp:

//Correct updaten van waarden in de editor
#if WITH_EDITOR
void ABaseCharacter::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	CalculateHealth(0);

	Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif

BaseCharacter.h

De eindcode van BaseCharacter.h:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"

//Klasse toegankelijk maken voor Blueprints
UCLASS(Blueprintable)
class TESTC_API ABaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:

	//De variabele Health is aanpasbaar binnen Blueprints
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "BaseCharacter")
	float Health = 100;

	//De variabele isDead is enkel leesbaar en kan niet worden aangepast via Blueprints
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "BaseCharacter")
	bool isDead = false;

	//De functie CalculateDead is enkel gekend binnen deze klasse en niet in Blueprints
	virtual void CalculateDead();

	//De functie CalculateHealth is aanroepbaar vanuit Blueprints
	UFUNCTION(BlueprintCallable, Category = "BaseCharacter")
	virtual void CalculateHealth(float delta);

	//Correct updaten van waarden in de editor
#if WITH_EDITOR
	virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

public:
	// Sets default values for this character's properties
	ABaseCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	

};

BaseCharacter.cpp

De eindcode van BaseCharacter.cpp:

// Fill out your copyright notice in the Description page of Project Settings.

#include "BaseCharacter.h"

// Sets default values
ABaseCharacter::ABaseCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void ABaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

//Implementeer de CalculateHealth functie.
void ABaseCharacter::CalculateHealth(float Delta)
{
	Health += Delta;
	CalculateDead();
}

//Implementeer de CalculateDead functie.
void ABaseCharacter::CalculateDead()
{
	if (Health <= 0)
		isDead = true;
	else
		isDead = false;

}

//Correct updaten van waarden in de editor
#if WITH_EDITOR
void ABaseCharacter::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	CalculateHealth(0);

	Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif

Build het C++-project

Nu onze C++ code geschreven is moet deze ook nog geBuild worden zodat deze ook kan gebruikt worden in Unreal Engine.

  • In Visual Studio, in de Solution Explorer, klik rechts op de naam van uw project en kies Build.

  • Ons werk in Visual Studio is gedaan, u kunt nu Visual Studio sluiten.

Een Blueprint maken op basis van onze c++ klasse

  • Start eventueel het project opnieuw op in Ureal Engine.
  • In de Content Browser gaat u naar de folder C++ Classes.
  • Hier ziet u een folder met de naam van uw project.
  • Deze folder bevat de C++ klasse (hier BaseCharacter).
  • Klik rechts op de C++ klasse en kies voor Create Blueprint based on … (op de plaats van de puntjes staat de naam van de C++ klasse).

  • Geef de gewenste naam en klik op Create Blueprint Class.

U komt in de Blueprint Class terecht.

  • Klik op Show Inherited Variables en dan vindt u onder Variables Base Character de aangemaakte variabelen.

  • In de Event Graph hebt u toegang tot de functie CalculateHealth() (niet tot CalculateDead() want deze was niet beschikbaar gemaakt).

Merk op dat u niet het datatype maar wel de waarde van de variabele Health kunt wijzigen want deze was ingesteld als BlueprintReadWrite en EditAnywhere: UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "BaseCharacter").

De waarde van de variabele isDead kunt u niet wijzigen want deze was ingesteld als BlueprintReadOnly en VisibleAnywhere: UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "BaseCharacter").

Zo, we hebben gezien hoe u eventueel kunt vertrekken vanuit een eigen C++ klasse en deze nadien kunt integreren in uw Unreal Engine Blueprint project.


C++ versus Blueprints

In grote lijnen kunt u het volgende stellen:

  • In kleine projecten kunt u gerust alles in Blueprints doen.
  • In grotere projecten programmeert u de basisfunctionaliteiten in C++ en gebruikt u Blueprints voor het werken met content in een level. Anders gezegd: in een groter project steekt u C++ onder de motorkap.


Project Twin Stick Shooter – Building the Hero Character from a C++ Class

De video’s vindt u op Twin Stick Shooter with Blueprint (unrealengine.com).

Bekijk de video’s:

  • Base Character Class
  • Building the Hero character

Masterclasses

Wie een aantal basistechnieken van C++ wilt verkennen kan onderstaande video’s bekijken. Deze video’s zijn gevorderd en enkel voor de liefhebbers.

Getting started with C++

Exposing C++ to Blueprints

Geef een reactie

Deze website gebruikt Akismet om spam te verminderen. Bekijk hoe je reactie-gegevens worden verwerkt.

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.