Announcement

Collapse
No announcement yet.

4.7 C++ Transition Guide

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • replied
    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.

    Leave a comment:


  • replied
    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

    Code:
    UNavigationSystem::FindPathToLocationSynchronously(...)

    NavigationSystem.h

    Code:
    /** 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

    Code:
    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

    Code:
    /** 
    	 *	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
    Last edited by Rama; 02-25-2015, 08:30 PM.

    Leave a comment:


  • replied
    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.

    Leave a comment:


  • replied
    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:

    Code:
    Error	7	error C2512: 'APlayerController' : no appropriate default constructor available	C:\Users\Christian\Documents\Unreal Projects\SurvivalSim\Source\SurvivalSim\SurvivalPlayerController.cpp	8	1	SurvivalSim
    Code:
    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.

    Leave a comment:


  • replied
    In AiController the GetFocalPoint is not virtual anymore.
    PHP Code:
    FVector GetFocalPoint() const; 
    Instead, you can now inherit from:
    PHP Code:
    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.
    Last edited by _neo_; 02-25-2015, 02:22 PM. Reason: EQS Tick

    Leave a comment:


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

    Leave a comment:


  • replied
    Originally posted by SaxonRah View Post
    Header.h
    Code:
    #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
    Code:
    #include "GameProject.h"
    #include "Euclib.h"
    
    // Sets default values
    AEuclib::AEuclib()
    {}
    
    // Called when the game starts or when spawned
    void AEuclib::BeginPlay()
    {
        Super::BeginPlay();  
    }
    With PCIP gone, how do I now create components?

    Leave a comment:


  • replied
    Originally posted by SaxonRah View Post
    Header.h
    Code:
    #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
    Code:
    #include "GameProject.h"
    #include "Euclib.h"
    
    // Sets default values
    AEuclib::AEuclib()
    {}
    
    // Called when the game starts or when spawned
    void AEuclib::BeginPlay()
    {
        Super::BeginPlay();  
    }
    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.

    Leave a comment:


  • replied
    Don't forget, the official release notes (not the forum announcement) spell out a lot of the breaking changes affecting C++ code.
    Last edited by ngrts; 02-25-2015, 12:33 AM. Reason: s/all/a lot/g

    Leave a comment:


  • replied
    FTimerHandle / Timers in 4.7

    Originally posted by TheAgent View Post
    Setting or clearing timers have to be done with TimerHandlers now

    Code:
    FTimerHandle HitHeadFalse;
    GetWorldTimerManager().ClearTimer(HitHeadFalse);
    GetWorldTimerManager().SetTimer(HitHeadFalse, this, &ABM_EnemyPawn::HitHeadFalse, 0.65f, false,-1.0f);
    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

    Code:
    /** Handle for efficient management of Respawn timer */
    00064: FTimerHandle TimerHandle_Respawn;
    .cpp
    Code:
    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!

    Code:
    #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
    Code:
    FTimerHandle TH_CreateApexCreationPieces;
    .cpp
    Code:
    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;
    	} 
    ///....
    }
    Last edited by Rama; 02-25-2015, 12:26 AM.

    Leave a comment:


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

    New: Greatly improved the quality of C++ IDE code completion and error checking with Unreal Engine C++ projects
    Xcode and Visual Studio projects will now have more reliable IntelliSense results
    We now emit compiler definitions and include paths for editor targets (instead of runtime targets), which are much more inclusive
    Fixed project targets "infecting" IntelliSense definitions of other target's project files

    Leave a comment:


  • replied
    Setting or clearing timers have to be done with TimerHandlers now

    Code:
    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

    Code:
    UGameplayStatics::PlaySoundAtLocation(this,HurtSound, this->GetActorLocation(), 1.0, 1.0);
    Last edited by TheAgent; 02-24-2015, 11:31 PM.

    Leave a comment:


  • replied
    Originally posted by Unreal C++ Programming Improvements
    • No special constructors needed! Your class constructors no longer need to take an Object Initializer argument.
    Header.h
    Code:
    #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
    Code:
    #include "GameProject.h"
    #include "Euclib.h"
    
    // Sets default values
    AEuclib::AEuclib()
    {}
    
    // Called when the game starts or when spawned
    void AEuclib::BeginPlay()
    {
        Super::BeginPlay();  
    }

    Leave a comment:


  • replied
    Controller ID

    ULocalPlayer::ControllerID is private now, so this

    Code:
    LP->ControllerId
    becomes this

    Code:
    LP->GetControllerId()


    Rama

    Leave a comment:


  • replied
    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:

    Code:
    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:

    Code:
    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
    Last edited by Rama; 02-24-2015, 09:08 PM.

    Leave a comment:

Working...
X