Announcement

Collapse
No announcement yet.

4.7 C++ Transition Guide

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

  • replied
    Originally posted by Kris View Post
    I'm here to say a big "thank you!" to Rama and all those who contributed to this thread.

    We were holding off from updating to 4.7, but on the 14th we decided that 4.7.x contained fixes that would save us time and effort, so went against our original plan and went to it.

    First stop was here.

    The only issue we've ended up having - C++ wise - was the classic "Everything worked first time... ****".
    You're welcome Kris!

    I am very happy this thread helped speed up your transition to 4.7!

    Have fun today!



    Rama

    Leave a comment:


  • replied
    I'm here to say a big "thank you!" to Rama and all those who contributed to this thread.

    We were holding off from updating to 4.7, but on the 14th we decided that 4.7.x contained fixes that would save us time and effort, so went against our original plan and went to it.

    First stop was here.

    The only issue we've ended up having - C++ wise - was the classic "Everything worked first time... ****".

    Blueprint wise, we've had some misc issues - the outer is a placeholder warning - that mysteriously fixed themselves.
    For anyone experiencing that issue, it seems that running a stand alone version of the game and/or rebuilding the projects solution file sorted it for us.
    Though I don't know why

    Leave a comment:


  • replied
    Originally posted by Elvince View Post
    I will just quote my answer to this:
    Thanks for confirming this..

    Leave a comment:


  • replied
    I will just quote my answer to this:
    Originally posted by Elvince View Post
    hi,

    just for you information, I had a confirmation of how the timerhandle works now:

    "If you have two different timerhandles referencing the same function then each one will have a unique id that can be used independently of each other."

    That's mean that each handle manage its own timer. So if you are overring a class and want to put a timer on a function, you may look at the main class level to see if no one set a timerhandle on it or you will have some surprise that it will be called twice.
    As a principle, I encourage you to set your timer as protected and not private, so you can reuse it in child class.

    Leave a comment:


  • replied
    Originally posted by Rama View Post
    I'm pretty sure that the TimerManager will recognize that the timer was already running and restart the existing timer, because none of the timers in my project got broken by upgrading to 4.7

    However if you want to be absolutely sure you can just clear the timer yourself before restarting

    The only thing that changed for me in 4.7 timers was just how to keep track of them to clear them / check if they are running from a function other than the one in which they were created.

    The behavior of restarting the same timer by just calling it again seems to be unchanged!

    .h
    Code:
    FTimerHandle MyLoopingTimerHandle;
    .cpp

    Code:
    //some func that starts timer
    {
      GetWorldTimerManager().ClearTimer(MyLoopingTimerHandle);
      GetWorldTimerManager().SetTimer(MyLoopingTimerHandle, &YourClass::YourFunc, 0.01,true );
    }
    
    //some func that stops the timer normally
    {
      GetWorldTimerManager().ClearTimer(MyLoopingTimerHandle);
    }
    You could test both ways, calling the clear yourself as above, and then just omitting that first clear, let us know what happens!

    Again all my research so far leads me to believe that if the timer manager is already running a timer and gets a request to start that same timer again, it will just restart the current one.

    If 4.7 timers worked differently than 4.6 and prior it would have broken a lot of code that relied on non-duplication of timers, so I think the extra ClearTimer is unnecessary but might be emotionally comforting (which is always nice when not inefficient).



    Rama
    Sorry for the necro but I had a doubt on this.

    If 2 different FTimerHandle are used on the same function, then 2 instances of that function will be executed right?

    Leave a comment:


  • replied
    Originally posted by Elvince View Post
    hi,

    just for you information, I had a confirmation of how the timerhandle works now:

    "If you have two different timerhandles referencing the same function then each one will have a unique id that can be used independently of each other."

    That's mean that each handle manage its own timer. So if you are overring a class and want to put a timer on a function, you may look at the main class level to see if no one set a timerhandle on it or you will have some surprise that it will be called twice.
    As a principle, I encourage you to set your timer as protected and not private, so you can reuse it in child class.
    Thanks for sharing your research Elvince!

    That's what this thread is for!

    Originally posted by cmartel View Post
    When using GENERATED_BODY(), you can either declare a FObjectInitializer constructor of your own or statically obtain it. It's worth knowing that even if your constructor doesn't have a FObjectInitializer argument, it is still used behind the scenes and you can obtain it using FObjectInitializer::Get().

    So, providing your own:
    Code:
    UCLASS()
    class UMyFactory : public UFactory
    {
    	GENERATED_BODY()
    
    public:
    	UMyFactory( const FObjectInitializer& ObjectInitializer )
    		: Super( FObjectInitializer )
    	{
    	}
    }
    Static Get:
    Code:
    UCLASS()
    class UMyFactory : public UFactory
    {
    	GENERATED_BODY()
    
    public:
    	UMyFactory()
    		: Super( FObjectInitializer::Get() )
    	{
    	}
    }
    The new GENERATED_BODY() macro gives you more control over what's autogenerated, so it'd be a shame to not use it to its full extents.

    -Camille
    Wow this is some great info!

    Thanks for sharing Camille!



    Rama
    Last edited by Rama; 03-26-2015, 10:04 PM.

    Leave a comment:


  • replied
    That did it, thanks .

    Leave a comment:


  • replied
    Originally posted by GreenEyed View Post
    UFactory is one that has not been updated (still uses GENERATED_UCLASS_BODY) so I had to use GENERATED_UCLASS_BODY also in our class that extends it, otherwise I found not way of having a constructor. Either the body was already created or there was no appropriate constructor.

    S!
    You can mix and match the construction macros in an inheritance hierarchy. It's just that since GENERATED_UCLASS_BODY autogenerates only a FObjectInitializer constructor, your derived class has to call the superclass constructor with it somehow. When using GENERATED_BODY(), you can either declare a FObjectInitializer constructor of your own or statically obtain it. It's worth knowing that even if your constructor doesn't have a FObjectInitializer argument, it is still used behind the scenes and you can obtain it using FObjectInitializer::Get().

    So, providing your own:
    Code:
    UCLASS()
    class UMyFactory : public UFactory
    {
    	GENERATED_BODY()
    
    public:
    	UMyFactory( const FObjectInitializer& ObjectInitializer )
    		: Super( FObjectInitializer )
    	{
    	}
    }
    Static Get:
    Code:
    UCLASS()
    class UMyFactory : public UFactory
    {
    	GENERATED_BODY()
    
    public:
    	UMyFactory()
    		: Super( FObjectInitializer::Get() )
    	{
    	}
    }
    The new GENERATED_BODY() macro gives you more control over what's autogenerated, so it'd be a shame to not use it to its full extents.

    -Camille

    Leave a comment:


  • replied
    They also forgot to add public: in HGGamePlayStatics.h after GENERATED_BODY() so all the helper functions are now private .

    Leave a comment:


  • replied
    UFactory is one that has not been updated (still uses GENERATED_UCLASS_BODY) so I had to use GENERATED_UCLASS_BODY also in our class that extends it, otherwise I found not way of having a constructor. Either the body was already created or there was no appropriate constructor.

    S!

    Originally posted by kamrann View Post
    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
    For those who are stuck on "Introduction to UE4 Programming - 3 - Creating the Base Pickup Class" here is the source code for 4.7. Works for me so hopefully it works for you also.

    PickUp.h

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

    #pragma once

    #include "GameFramework/Actor.h"
    #include "PickUp.generated.h"

    UCLASS()
    class TUTORIAL_102_API APickUp : public AActor
    {
    GENERATED_BODY()

    public:

    APickUp(const FObjectInitializer& ObjectInitializer);
    // Sets default values for this actor's properties
    APickUp();

    // True when the pickup is able to be picked up, false if something deactivates the pickup
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PickUp)
    bool bIsActive;

    // Simple collision primitive to use as the root component
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = PickUp)
    USphereComponent* BaseCollisionComponent;

    // StaticMeshComponent to represent the pickup in the level
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = PickUp)
    UStaticMeshComponent* PickupMesh;

    // Function to call when the pickup is collected
    UFUNCTION(BlueprintNativeEvent)
    void OnPickedUp();

    // Declare Blueprint Native Event
    virtual void OnPickedUp_Implementation();

    };






    PickUp.cpp


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

    #include "Tutorial_102.h"
    #include "PickUp.h"


    // Sets default values
    APickUp::APickUp(const class FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
    {

    // The pickup is valid when it is created
    bIsActive = true;

    // Create the root SphereComponent to handle the pickup's collision
    BaseCollisionComponent = ObjectInitializer.CreateDefaultSubobject<USphereComponent>(this, TEXT("BaseSphereComponent"));

    // Set the SphereComponent as the root component
    RootComponent = BaseCollisionComponent;

    // Create the static mesh component
    PickupMesh = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("PickupMesh"));

    // Turn physic on for the static mesh
    PickupMesh->SetSimulatePhysics(true);

    //Attach the StaticMeshComponent to the root component
    PickupMesh->AttachTo(RootComponent);

    // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    }

    void APickUp::OnPickedUp_Implementation(){
    // There is no default behaviour for a pickup when it is picked up
    }

    Leave a comment:


  • replied
    hi,

    just for you information, I had a confirmation of how the timerhandle works now:

    "If you have two different timerhandles referencing the same function then each one will have a unique id that can be used independently of each other."

    That's mean that each handle manage its own timer. So if you are overring a class and want to put a timer on a function, you may look at the main class level to see if no one set a timerhandle on it or you will have some surprise that it will be called twice.
    As a principle, I encourage you to set your timer as protected and not private, so you can reuse it in child class.

    Leave a comment:


  • replied
    I found the 4.7 MoveToActor() Bug.

    It seems that when you set bUsePathFinding=false in MoveToActor(), the AI will no longer update its destination on Moving Actor.

    Spent the whole day rewriting our AI code to find the bug. Hope someone help clear this bug soon...

    Leave a comment:


  • replied
    Scene Proxies in 4.7

    How To Get the PDI for Drawing Purposes


    If you want to do custom drawing for a component's scene proxy that you used to do with DrawDynamicElements prior to 4.7,

    here is the code you can use in 4.7!

    DrawDynamicElements no longer exists, but you can use GetDynamicElements and the Collector to get the PDI!

    Code:
    FPrimitiveSceneProxy* UJoyDrawComp::CreateSceneProxy()
    {
    	class JoyDrawSceneProxy : public FPrimitiveSceneProxy
    	{
    	public:
    		JoyDrawSceneProxy(UJoyDrawComp* InComponent)
    			:	FPrimitiveSceneProxy(InComponent),
    				JoyDraw(InComponent)
    		{
    			bWillEverBeLit = false;
    		}
    
    		//Draw Dynamic Elements is not used any more
    		virtual void GetDynamicMeshElements(
    			const TArray<const FSceneView*>& Views, 
    			const FSceneViewFamily& ViewFamily, 
    			uint32 VisibilityMap, 
    			class FMeshElementCollector& Collector
    		) const override
    		{   
    			//See SphereComponent.cpp for reference
    			for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
    			{
    				if (VisibilityMap & (1 << ViewIndex))
    				{
    					const FSceneView* View = Views[ViewIndex];
    					FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
    					if(!PDI)
    					{
    						continue;
    					}
    					
    					//Draw to PDI !!!
    					JoyDraw->DrawToPDI(PDI);
    				}
    			}
    		}
    		
    		//Should Draw?
    		virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) override
    		{
    			FPrimitiveViewRelevance Result;
    			
    			//~~~ Guarantee It Will Be Drawn ~~~ 
    			Result.bDrawRelevance 		= true;
    			Result.bDynamicRelevance 	= true;
    			Result.bShadowRelevance 	= false; 	//no shadow
    			//~~~~~~~~~~~~~~~~~~~~~~~~~
    			
    			return Result;
    		}
    		
    		//SceneProxy
    		virtual uint32 GetMemoryFootprint( void ) const override { return( sizeof( *this ) + GetAllocatedSize() ); }
    		uint32 GetAllocatedSize( void ) const { return( FPrimitiveSceneProxy::GetAllocatedSize() ); }
    
    	private:
    		UJoyDrawComp* JoyDraw;
    	};
    
    	return new JoyDrawSceneProxy( this );
    }
    This is the code I am using in 4.7 to do custom component rendering in the Editor for The Solus Project

    Click image for larger version

Name:	SceneProxies47.jpg
Views:	1
Size:	349.5 KB
ID:	1068043
    Last edited by Rama; 03-09-2015, 02:47 PM.

    Leave a comment:


  • replied
    If you get errors that the definition for FKey can't be found make sure to add InputCore to the appropriate build cs!

    LoadingScreenModules coming from ShooterGame/Platformer examples tend to have this issue

    Rama

    Leave a comment:

Working...
X