4.7 C++ Transition Guide

Dear Community,

Here is a thread where we can all compare our notes as we upgrade projects to 4.7!

Please post the compile errors you encountered and then resolved to help others along!

Also you are welcome to post questions here about compile errors you cant resolve.

Again this is a thread where everyone is welcome to contribute!


**Generated Body**

Time to switch over every .h file you have to use GENERATED_BODY !

Also note you will probably want to add public: if you put your BP vars at the top of the class.

Also note you need to include your class constructor now as well.

this



```


UCLASS()
class AJoyMechPower : public AJoyMechFire
{
    GENERATED_UCLASS_BODY()



```



becomes this



```


UCLASS()
class AJoyMechPower : public AJoyMechFire
{
    GENERATED_BODY()
	
public:
    AJoyMechPower(const FObjectInitializer& ObjectInitializer);


```



RPCs in 4.7

You will likely encounter errors with your function calls like



UFUNCTION(Server,Reliable,WithValidation)
void YourServerFunc();

UFUNCTION(Client,Reliable,WithValidation)
void YourClientFunc();


Please see this note from Epic Ryan G.

Full details in this thread


**Code Sample**

this



```


UFUNCTION(Reliable,Server,WithValidation)
void SERVER_UpdateArmorMorph(FName Name, float Value);


```



becomes this



```


UFUNCTION(Reliable,Server,WithValidation)
void SERVER_UpdateArmorMorph(FName Name, float Value);
bool SERVER_UpdateArmorMorph_Validate(FName Name, float Value);
void SERVER_UpdateArmorMorph_Implementation(FName Name, float Value);


```


♥

Rama

Nice one Rama :slight_smile:

ACharacter::OnJumped_Implmentation() is not virtual

ACharacter::OnJumped() has been changed to a BlueprintNativeEvent and the new explicitly declared ACharacter::OnJumped_Implmentation() is not marked as virtual.

There was already a bug logged here https://answers.unrealengine.com/questions/165695/onjumped-implementation-not-virtual-47-p4.html and the issue has been resolved in github master.

Make OnJumped_Imlementation() in Character.h virtual and you should be sorted.


virtual void OnJumped_Implementation();

**Set Sound Class Volume

FAudioDevice::SetClassVolume does not exist**

I had to change my Victory BP Library node for Setting the volume of any class from this:



bool UVictoryBPFunctionLibrary::VictorySoundVolumeChange(USoundClass* SoundClassObject, float NewVolume)
 {
	FAudioDevice* Device = GEngine->GetAudioDevice();
	if (!Device || !SoundClassObject)
	{
		return false;
	}
	    
	bool bFound = Device->SoundClasses.Contains(SoundClassObject);
	if(bFound)
	{ 
		Device->SetClassVolume(SoundClassObject, NewVolume);
		return true;
	}
	return false;
 }


to this:



bool UVictoryBPFunctionLibrary::VictorySoundVolumeChange(USoundClass* SoundClassObject, float NewVolume)
 {
       if(!SoundClassObject) 
       {
         return false;
       }

	SoundClassObject->Properties.Volume = NewVolume;

	return true; 
 }


Quite the improvement! Thanks Epic!


**More info**

Check out SoundClass.h and in particular:

**FSoundClassProperties**

Enjoy!

Rama

Controller ID

ULocalPlayer::ControllerID is private now, so this



LP->ControllerId


becomes this



LP->GetControllerId()


:slight_smile:

Rama

Header.h



#pragma once
#include "GameFramework/Actor.h"
#include "Euclib.generated.h"

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

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

};


Source.cpp


#include "GameProject.h"
#include "Euclib.h"

// Sets default values
AEuclib::AEuclib()
{}

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


Setting or clearing timers have to be done with TimerHandlers now



FTimerHandle HitHeadFalse;
GetWorldTimerManager().ClearTimer(HitHeadFalse);
GetWorldTimerManager().SetTimer(HitHeadFalse, this, &ABM_EnemyPawn::HitHeadFalse, 0.65f, false,-1.0f);


To play sounds, you have to do it this way now as well


UGameplayStatics::PlaySoundAtLocation(this,HurtSound, this->GetActorLocation(), 1.0, 1.0);

Not a transition guide on its own, but still related and very important to note (also very awesome):

FTimerHandle / Timers in 4.7

Thanks for sharing TheAgent!

To expand on this new use of FTimerHandle, you have to store a var in your .h file in order to clear a timer outside of the scope in which it was declared.

See ShooterGame for example:

ShooterAIController.h



/** Handle for efficient management of Respawn timer */
00064: FTimerHandle **TimerHandle_Respawn**;


.cpp



void AShooterAIController::BeginInactiveState()
{
	Super::BeginInactiveState();

	AGameState* GameState = GetWorld()->GameState;

	const float MinRespawnDelay = (GameState && GameState->GameModeClass) ? GetDefault<AGameMode>(GameState->GameModeClass)->MinRespawnDelay : 1.0f;

	GetWorldTimerManager().SetTimer(**TimerHandle_Respawn**, this, &AShooterAIController::Respawn, MinRespawnDelay);
}


//~~~


void AShooterAIController::GameHasEnded(AActor* EndGameFocus, bool bIsWinner)
{
	// Stop the behaviour tree/logic
	BehaviorComp->StopTree();

	// Stop any movement we already have
	StopMovement();

	// Cancel the repsawn timer
	GetWorldTimerManager().ClearTimer(**TimerHandle_Respawn**);

	// Clear any enemy
	SetEnemy(NULL);

	// Finally stop firing
	AShooterBot* MyBot = Cast<AShooterBot>(GetPawn());
	AShooterWeapon* MyWeapon = MyBot ? MyBot->GetWeapon() : NULL;
	if (MyWeapon == NULL)
	{
		return;
	}
	MyBot->StopWeaponFire();	
}




**Timer Macros For You**

I've created (and have been using) four macros you can use if you like!

SETTIMER passes in a dummy TimerHandle, you cannot clear a timer started this way (this is done even in the ue4 code base if you look)

SETTIMERH lets you pass in a FTimerHandle that you can then use with CLEARTIMER

Remember that the FTimerHandle must be declared in your .h if you want to clear the timer in a different function than the one in which it was started.

Enjoy!



```


#define **SETTIMER**(param1,param2,param3) FTimerHandle TimerHandle; (GetWorldTimerManager().SetTimer(TimerHandle, this, &param1, param2, param3))


#define **SETTIMERH**(handle, param1,param2,param3) (GetWorldTimerManager().SetTimer(handle, this, &param1, param2, param3))

#define CLEARTIMER(handle) (GetWorldTimerManager().ClearTimer(handle))

#define ISTIMERACTIVE(handle) (GetWorldTimerManager().IsTimerActive(handle))


_


```



**Example Use:**

.h


```


FTimerHandle TH_CreateApexCreationPieces;


```



.cpp


```


void AStructureCreation::CreationProcess_Phase1()
{	
	CreationCount = 0; 
	
	**SETTIMERH**(TH_CreateApexCreationPieces,AStructureCreation::T_CreateApexCreationPieces,0.23,true);
} 
void AStructureCreation::T_CreateApexCreationPieces()
{
	//how many of the apex rocks are we creating?
	AStructureCreationApex* DefaultObj = StructureCreationApexBP->GetDefaultObject<AStructureCreationApex>(); 
	if(!DefaultObj)
	{
		UE_LOG(Joy,Error,TEXT("AStructureCreation::CreationProcess_Phase1()>> NO DEFAULT OBJ!!!!!!"));
		**CLEARTIMER**(TH_CreateApexCreationPieces);
		return;
	} 
///....
}


```

Don’t forget, the official release notes (not the forum announcement) spell out a lot of the breaking changes affecting C++ code.

As I learned the hard way, not only are you not required to add a call to Super() in the constructor, but if you do, your editor will crash on startup.

With PCIP gone, how do I now create components?

Just remove the OI. (or PCIP.) from your code.

In AiController the GetFocalPoint is not virtual anymore.

FVector GetFocalPoint() const;

Instead, you can now inherit from:

virtual FVector GetFocalPointOnActor(const AActor *Actor) const;

Edit: Also, after upgrading to 4.7 I had to re-tick the Environment Query System in the Editor Preferences/Experimental.

So, I tried to convert my project to 4.7. I have cleaned up the constructor of my player controller, and get the following error:


Error	7	error C2512: 'APlayerController' : no appropriate default constructor available	C:\Users\Christian\Documents\Unreal Projects\SurvivalSim\Source\SurvivalSim\SurvivalPlayerController.cpp	8	1	SurvivalSim



ASurvivalPlayerController::ASurvivalPlayerController()
{
	
	PrimaryActorTick.bCanEverTick = true;
	bShowMouseCursor = true;

}


Im not sure, if I am even on 4.7 yet, since I cannot convert it without buidling. When I right click on my projectfile and try to switch versions, I only get the message “Failed to generate Project files”. When I try to open my project from the 4.7 editor, it wants to rebuild the project, but then failes. When I now go to VS, I get the above error during compile.

This line is of importance : “Branching Points refactor. BranchingPoints are now regular AnimNotifies, with their MontageTickType set to ‘BranchingPoint’ instead of ‘Queued’.”

This means that if you use a C++ class for your animation instance, and you have previously declared functions like: MontageBranchingPoint_JumpLeaveGround, you’ll have to rename them in AnimNotify_JumpLeaveGround.

How to Find a Path in 4.7 Without UNavigationComponent

UNavigationComponent has been removed, here are two ways you can find paths in 4.7!

Two Options

1. Use the static accessible Navigation System



UNavigationSystem::FindPathToLocationSynchronously(...)


NavigationSystem.h



/** Finds path instantly, in a FindPath Synchronously. 
	 *	@param PathfindingContext could be one of following: NavigationData (like Navmesh actor), Pawn or Controller. This parameter determines parameters of specific pathfinding query */
	UFUNCTION(BlueprintCallable, Category = "AI|Navigation", meta = (HidePin = "WorldContext", DefaultToSelf = "WorldContext"))
	static UNavigationPath* FindPathToLocationSynchronously(UObject* WorldContext, const FVector& PathStart, const FVector& PathEnd, AActor* PathfindingContext = NULL, TSubclassOf<UNavigationQueryFilter> FilterClass = NULL);

	/** Finds path instantly, in a FindPath Synchronously. Main advantage over FindPathToLocationSynchronously is that 
	 *	the resulting path with automatically get updated if goal actor moves more then TetherDistance away from last path node
	 *	@param PathfindingContext could be one of following: NavigationData (like Navmesh actor), Pawn or Controller. This parameter determines parameters of specific pathfinding query */
	UFUNCTION(BlueprintCallable, Category = "AI|Navigation", meta = (HidePin = "WorldContext", DefaultToSelf = "WorldContext"))
	static UNavigationPath* FindPathToActorSynchronously(UObject* WorldContext, const FVector& PathStart, AActor* GoalActor, float TetherDistance = 50.f, AActor* PathfindingContext = NULL, TSubclassOf<UNavigationQueryFilter> FilterClass = NULL);

//----------------------------------------------------------------------//
	// Public querying interface                                                                
	//----------------------------------------------------------------------//
	/** 
	 *	Synchronously looks for a path from @fLocation to @EndLocation for agent with properties @AgentProperties. NavData actor appropriate for specified 
	 *	FNavAgentProperties will be found automatically
	 *	@param ResultPath results are put here
	 *	@param NavData optional navigation data that will be used instead of the one that would be deducted from AgentProperties
	 *  @param Mode switch between normal and hierarchical path finding algorithms
	 */
	FPathFindingResult FindPathSync(const FNavAgentProperties& AgentProperties, FPathFindingQuery Query, EPathFindingMode::Type Mode = EPathFindingMode::Regular);

	/** 
	 *	Does a simple path finding from @StartLocation to @EndLocation on specified NavData. If none passed MainNavData will be used
	 *	Result gets placed in ResultPath
	 *	@param NavData optional navigation data that will be used instead main navigation data
	 *  @param Mode switch between normal and hierarchical path finding algorithms
	 */
	FPathFindingResult FindPathSync(FPathFindingQuery Query, EPathFindingMode::Type Mode = EPathFindingMode::Regular);

	/** 
	 *	Asynchronously looks for a path from @StartLocation to @EndLocation for agent with properties @AgentProperties. NavData actor appropriate for specified 
	 *	FNavAgentProperties will be found automatically
	 *	@param ResultDelegate delegate that will be called once query has been processed and finished. Will be called even if query fails - in such case see comments for delegate's params
	 *	@param NavData optional navigation data that will be used instead of the one that would be deducted from AgentProperties
	 *	@param PathToFill if points to an actual navigation path instance than this instance will be filled with resulting path. Otherwise a new instance will be created and 
	 *		used in call to ResultDelegate
	 *  @param Mode switch between normal and hierarchical path finding algorithms
	 *	@return request ID
	 */
	uint32 FindPathAsync(const FNavAgentProperties& AgentProperties, FPathFindingQuery Query, const FNavPathQueryDelegate& ResultDelegate, EPathFindingMode::Type Mode = EPathFindingMode::Regular);

	/** Removes query indicated by given ID from queue of path finding requests to process. */
	void AbortAsyncFindPathRequest(uint32 AsynPathQueryID);
	
	/** 
	 *	Synchronously check if path between two points exists
	 *  Does not return path object, but will run faster (especially in hierarchical mode)
	 *  @param Mode switch between normal and hierarchical path finding algorithms. @note Hierarchical mode ignores QueryFilter
	 *	@return true if path exists
	 */
	bool TestPathSync(FPathFindingQuery Query, EPathFindingMode::Type Mode = EPathFindingMode::Regular, int32* NumVisitedNodes = NULL) const;

	/** Finds random point in navigable space
	 *	@param ResultLocation Found point is put here
	 *	@param NavData If NavData == NULL then MainNavData is used.
	 *	@return true if any location found, false otherwise */
	bool GetRandomPoint(FNavLocation& ResultLocation, ANavigationData* NavData = NULL, TSharedPtr<const FNavigationQueryFilter> QueryFilter = NULL);

	/** Finds random point in navigable space restricted to Radius around Origin
	 *	@param ResultLocation Found point is put here
	 *	@param NavData If NavData == NULL then MainNavData is used.
	 *	@return true if any location found, false otherwise */
	bool GetRandomPointInRadius(const FVector& Origin, float Radius, FNavLocation& ResultLocation, ANavigationData* NavData = NULL, TSharedPtr<const FNavigationQueryFilter> QueryFilter = NULL) const;



2. Get the Nav data within your follow comp and use instanced versions



bool UYourFollowComp::GetRandomPointInRadius(const FVector& Origin, float Radius, FVector& OutResult)
{
	if (!MovementComp) 
	{
		return false;
	}
	//~~~~~~~~~~~~~~~~~~
	
	//Agent Properties
	const FNavAgentProperties& AgentProperties = MovementComp->GetNavAgentPropertiesRef();
	const ANavigationData * NavData = GetNavDataForProps(AgentProperties);
	if (!NavData) 
	{
		return false;
	}
	
	//Now here you can use Nav Data functions!
	
	NavData->YourDesiredFunction()
}


NavData.h



/** 
	 *	Synchronously looks for a path from @StartLocation to @EndLocation for agent with properties @AgentProperties. NavMesh actor appropriate for specified 
	 *	FNavAgentProperties will be found automatically
	 *	@param ResultPath results are put here
	 *	@return true if path has been found, false otherwise
	 *
	 *	@note don't make this function virtual! Look at implementation details and its comments for more info.
	 */
	FORCEINLINE FPathFindingResult FindPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query) const
	{
		check(FindPathImplementation);
		// this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot
		return (*FindPathImplementation)(AgentProperties, Query);
	}

	/** 
	 *	Synchronously looks for a path from @StartLocation to @EndLocation for agent with properties @AgentProperties. NavMesh actor appropriate for specified 
	 *	FNavAgentProperties will be found automatically
	 *	@param ResultPath results are put here
	 *	@return true if path has been found, false otherwise
	 *
	 *	@note don't make this function virtual! Look at implementation details and its comments for more info.
	 */
	FORCEINLINE FPathFindingResult FindHierarchicalPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query) const
	{
		check(FindHierarchicalPathImplementation);
		// this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot
		return (*FindHierarchicalPathImplementation)(AgentProperties, Query);
	}


Enjoy!

Rama

Regarding the ‘no appropriate constructor’ errors when trying to use no-argument constructors. It appears the release notes were a bit unclear about this.

I’ve had a look through the engine code and concluded that it depends on what class you’re deriving from. Directly from UObject, fine. Otherwise, unsurprisingly, you can only declare a constructor without an FObjectInitializer if your base class also has one. It looks like any engine classes which have been updated to use GENERATED_BODY have been given a default constructor, whilst those still using GENERATED_UCLASS_BODY have not.

AActor has been updated, however I checked a few other engine classes and it looks like most of them haven’t. I’m guessing this will change over time, but I’m not certain.

Well it should be trivial to add a constructor to parent classes.

I don’t think anyone is going to be rebuilding the engine from source every time they want to derive from another class, just to avoid having to write an extra argument into their constructor. :slight_smile: