Overriding UInterface functions in C++

One side effect of UInterfaces allowing inheritance in C++ is that we can override default implementations in subclasses as well as in Blueprint. This recipe shows you how to do so.

Getting ready

Follow the recipe Calling native UInterface functions from C++ in which a Physics Cube is created so that you have the class ready.

How to do it...

  1. Create a new Interface called Selectable.
  2. Define the following functions inside ISelectable:
    virtual bool IsSelectable();
    
    virtual bool TrySelect();
    
    virtual void Deselect();
  3. Provide a default implementation for functions like this:
    boolISelectable::IsSelectable()
    {
      GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "Selectable");
      return true;
    }
    
    boolISelectable::TrySelect()
    {
      GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "Accepting Selection");
      return true;
    }
    
    voidISelectable::Deselect()
    {
      unimplemented();
    }
  4. Create a class based on APhysicsCube called SelectableCube.
  5. #include "Selectable.h" inside the SelectableCube class' header.
  6. Modify the ASelectableCube declaration like this:
    class UE4COOKBOOK_API ASelectableCube : public APhysicsCube, public ISelectable
  7. Add the following functions to the header:
    ASelectableCube();
    virtual void NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVectorHitLocation, FVectorHitNormal, FVectorNormalImpulse, constFHitResult& Hit) override;
  8. Implement the functions:
    ASelectableCube::ASelectableCube()
    : Super()
    {
      MyMesh->SetNotifyRigidBodyCollision(true);
    }
    
    voidASelectableCube::NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVectorHitLocation, FVectorHitNormal, FVectorNormalImpulse, constFHitResult& Hit)
    {
      if (IsSelectable())
      {
        TrySelect();
      }
    }
  9. Create a new class, called NonSelectableCube, which inherits from SelectableCube.
  10. NonSelectableCube should override the functions from SelectableInterface:
    virtual bool IsSelectable() override;
    
    virtual bool TrySelect() override;
    
    virtual void Deselect() override;
  11. The implementation file should be altered to include the following:
    boolANonSelectableCube::IsSelectable()
    {
      GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "Not Selectable");
      return false;
    }
    
    boolANonSelectableCube::TrySelect()
    {
      GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, "Refusing Selection");
      return false;
    }
    
    voidANonSelectableCube::Deselect()
    {
      unimplemented();
    }
  12. Place an instance of SelectableCube into the level at a certain range above the ground, and play your game. You should get messages verifying that the actor is selectable, and that it has accepted the selection, when the cube hits the ground.
    How to do it...
  13. Remove SelectableCube and replace it with an instance of NonSelectableCube to see the alternative messages indicating that this actor isn't selectable, and has refused selection.

How it works...

  1. We create three functions inside the Selectable interface.
  2. IsSelectable returns a Boolean to indicate if the object is selectable. You could avoid this and simply use TrySelect, given that it returns a Boolean value to indicate success, but, for example, you might want to know if the object inside your UI is a valid selection without having to actually try it.
  3. TrySelect actually attempts to select the object. There's no explicit contract forcing users to respect IsSelectable when trying to select the object, so TrySelect is named to communicate that the selection may not always succeed.
  4. Lastly, Deselect is a function added to allow objects to handle losing the player selection. This could involve changing the UI elements, halting sounds or other visual effects, or simply removing a selection outline from around the unit.
  5. The default implementations of the functions return true for IsSelectable (the default is for any object to be selectable), true for TrySelect (selection attempts always succeed), and issues a debug assert if Deselect is called without being implemented by the class.
  6. You could also implement Deselect as a pure virtual function if you wish.
  7. SelectableCube is a new class inheriting from PhysicsCube, but also implementing the ISelectable interface.
  8. It also overrides NotifyHit, a virtual function defined in AActor that triggers when the actor undergoes a RigidBody collision.
  9. We call the constructor from PhysicsCube with the Super() constructor call inside the implementation of SelectableCube. We then add our own implementation, which calls SetNotifyRigidBodyCollision(true) on our static mesh instance. This is necessary, because by default, RigidBodies (such as PrimitiveComponents with a collision) don't trigger Hit events as a performance optimization. As a result, our overridden NotifyHit function would never be called.
  10. Within the implementation of NotifyHit, we call some of the ISelectable interface functions on ourselves. Given that we know we are an object that inherits from ISelectable, we don't need to cast to an ISelectable* in order to call them.
  11. We check to see if the object is selectable with IsSelectable, and if so, we try to actually perform the selection using TrySelect.
  12. NonSelectableCube inherits from SelectableCube, so we can force the object to never be selectable.
  13. We accomplish this by overriding the ISelectable interface functions again.
  14. Within ANonSelectableCube::IsSelectable(), we print a message to the screen so we can verify that the function is being called, and then return false to indicate that the object isn't selectable at all.
  15. In case the user doesn't respect IsSelectable(), ANonSelectableCube::TrySelect() always returns false to indicate that the selection wasn't successful.
  16. Given that it is impossible for NonSelectableCube to be selected, Deselect() calls unimplemented(), which throws an assert warning that the function was not implemented.
  17. Now, when playing your scene, each time SelectableCube/NonSelectableCube hits another object, causing a RigidBody collision, the actor in question will attempt to select itself, and print messages to the screen.

See also

  • Refer Chapter 6, Input and Collision, which shows you how to Raycast from the mouse cursor into the game world to determine what is being clicked on, and could be used to extend this recipe to allow the player to click on items to select them
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.34.198