Skip to content

Instantly share code, notes, and snippets.

@randomwangran
Created December 9, 2018 15:03
Show Gist options
  • Save randomwangran/319f6988a68e30953404a546dec9d868 to your computer and use it in GitHub Desktop.
Save randomwangran/319f6988a68e30953404a546dec9d868 to your computer and use it in GitHub Desktop.
reply to Daniel

my 2nd reply

Thank you Daniel for helping me though. I am relatively new to cpp programming and want to become a better coder using c++. I’ve read your reply and did some further research. Following is some thing that I still not very clear.

Since GetControlledTank is a pointer of UTankAimingComponent, which is derived from ~ATankAIController

This is not correct. UTankAimingComponent is not derived from ATankAIController, they are unrelated.

Thanks for pointing out my mistake. UTankAimingComponent is not derived from ATankAIController. It is drived from UActorComponent. Then -> UObject -> UObjectBaseUtility -> UObjectBase.

On the other hand, the inheritance hierarchy for ATank is:

ATank -> APawn -> AActor -> UObject -> UObjectBaseUtility ->
UObjectBase

What I wanted to point out in the last post was that UTankAimingComponent and ATank shared the same parent according to the inheritance hierarchy. (UObject).

As you can see Tank and UTankAimingComponent has a same parent AActor. This is the moment I make a huge mistake: different children cannot access each other even they share the parrent.

What do you mean by this? It seems like you’re confusing the class hierarchy with the actual instances of those classes.

What I mean was that, at that time, I thought as long as two class shared the same parent at some levels in the inheritance hierarchy, they could access to each other. Now I know that this is wrong! You can not do that. For example, my parents have three children. I am one of them. Even I share the same parent as my brother, I cannot access my brother’ brain, which is his private function.

The new issue is: can children class access the public function or variables in their parent class? I suspect the answer is yes!

If void UTankAimingComponent::AimAt(FVector HitLocation) is something like void UObject::AimAt(FVector HitLocation) and they are public inherit from upper parents till the same parent (UObject). Then both Tank and UTankAimingComponent can access this AimAt function.

ATank* ATankAIController::GetControlledTank() const
{
    return Cast<ATank>(GetPawn());
}
  

UTankAimingComponent* ATankAIController::GetAimingComponent() const { return Cast<UTankAimingComponent>(GetPawn()); } The first function is correct the second is not. GetPawn returns a pointer to the same object, casting it doesn’t magically make it get the thing that you’re looking for, it just says treat it as if it were this type instead. It compiles because the class hierarchy you described, APawn and UTankAimingComponent share a common base class though this isn’t what you want to do.

I am just curious about why

UTankAimingComponent* ATankAIController::GetAimingComponent() const { 
    return Cast<UTankAimingComponent>(GetPawn()); 
}

not work, but it works for ATank*?

I jump into the source code of GetPawn():

/** Getter for Pawn */
FORCEINLINE APawn* GetPawn() const { return Pawn; }

The ATank is okay because ATank is drived from APawn. UTankAimingComponent fails, becasue its base does not include APawn.

Thanks for pointing out this point. The reason why the compiler did not complain was that they share a common base class: UObject.

When I was writing the code like:

return Cast<ATank>(GetPawn());

It is natually to think that GetPawn() should find it’s Pawn, especially when it is a request from a component. Unreal should be able to understand my intention. Why they did not implement this straigthforward thought? Do I miss something?

Consider the following

class AAnimal{};
class ACat : public AAnimal{ /*code*/ };
class ADog : public AAnimal{ /*code*/ };

ADog* GetDog();

void DoSomething(AAnimal* Animal)
{
    ACat* Cat = Cast<ACat>(Animal);
    Cat->Meow();
}

int main(){ 
    ADog* Dog = GetDog(); 
    DoSomething(Dog); 
}
  

The DoSomething function takes an AAnimal but then casts it to ACat*, in main I give it a ADog* which means the cast will fail and attempting to dereference it on the next line after the cast will be undefined behaviour (crash in Unreal) because Cat will be nullptr since the cast failed.

Thanks for providing the code snippet. It really helps me to understand.

I think Cast function you provided here make sense. I just want to use your DoSomething function to make an another example. In reality DoSomething is Speaking. When ADog call DoSoething or Speaking, the program should always return Wof Wof. If someone new programmer (like me) screw it up by using the Cast function, the program should immediately stop. If the programmer really want to let ADog pronounce Meow Meow, it is the programmer’s responsibilty to implement a new virtual function in the Base class AAnimal called: DogMimicCatSpeaking. And then use this new function from ADog to print out the desired behvior, i.e. print out Meow Meow.

However, you can see, by adding this new function (DogMimicCatSpeaking), I polluted the our Base class AAnimal, which should not deal with making specific sound. This function should be implement in the derived class.

For Cast in c++, I’ve learn that: everytime you cast something, you have to make sure that it is succeeded. To achieve this:

   void DoSomething(AAnimal* Animal)
   {
       ACat* Cat = Cast<ACat>(Animal);
	 
	 if(Cat) // adding a checker to protect potential crash
           Cat->Meow();
   }

After playing with Cast, I started to wondering which Cast is used in Unreal?

There are many Cast types in c++:

  • dynamic_cast
  • static_cast
  • … (perheps more)

In the head file Casts.h

// Dynamically cast an object type-safely.
template <typename To, typename From>
FORCEINLINE To* Cast(From* Src)
{
    return TCastImpl<From, To>::DoCast(Src);
}

What does it mean? Is Cast in the following code a dynamic casting, which will convert the base-class pointer into derived-class pointer?

ATank* ATankAIController::GetControlledTank() const
{
    return Cast<ATank>(GetPawn());
}

It seems yes, but why don’t we use?

return dynamic_cast<ATank*>(GetPawn());

TankAimingComponent is a protected member of ATank and should remain protected, what you’re trying to accomplish would mean creating a function on ATank that returns the TankAimingComponent. Meaning the code you should have by the end would be

GetControlledTank()->GetAimingComponent()->AimAt(HitLocation);
  

I use your suggestoin in TankAIController.cpp in my commit: https://github.com/randomwangran/cpp/blob/4903f7304a0c02b15983c36c88ffa025a503753a/Unreal/Section04/Source/BattleTank/Private/TankAIController.cpp

    void ATankAIController::Tick(float DeltaTime)
    {
	Super::Tick(DeltaTime);
	if (GetPlayerTank())
	{
		// TODO Move towards the player
		
		// Aim towards the player
		GetControlledTank()->GetAimingComponent()->AimAt(HitLocation);
	}
    }

But fail to compile:

CompilerResultsLog: Error: ~\\Unreal\Section04\Source\BattleTank\Private\TankAIController.cpp(32) : error C2039: 'GetAimingComponent': is not a member of 'ATank'
CompilerResultsLog: Error: ~\unreal\section04\source\battletank\public\Tank.h(10) : note: see declaration of 'ATank'
CompilerResultsLog: Error: ~\\Unreal\Section04\Source\BattleTank\Private\TankAIController.cpp(32) : error C2065: 'HitLocation': undeclared identifier
CompilerResultsLog: ERROR: UBT ERROR: Failed to produce item: ~\\Unreal\Section04\Binaries\Win64\UE4Editor-BattleTank-685.dll

GetAimingComponent is not a member of ATank

My solution is different from Ben’s solution: https://github.com/UnrealCourse/04_BattleTank/blob/54f0adbad057940cb229f4541a727f2383b53e9b/BattleTank/Source/BattleTank/Private/Tank.cpp

I don’t declare in my Tank.h:

    void ATank::AimAt(FVector HitLocation)
    {
	TankAimingComponent->AimAt(HitLocation);
    }

Because when I accepted his challenge, I think I need to completely refactor AimAt function in the UTankAimingComponent.h

I succesfully made it in my commit:

https://github.com/randomwangran/cpp/commit/b3c8415642f7a2ac0fc0a7a77126ddd45a4ba043

This is really a long reply, but I’ve learn a lot of new stuff and refesh my mind on old stuff. I hope it is not daunting you.

Ran

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment