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.
Follow the recipe Calling native UInterface functions from C++ in which a Physics Cube is created so that you have the class ready.
Selectable
.ISelectable
:virtual bool IsSelectable(); virtual bool TrySelect(); virtual void Deselect();
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(); }
APhysicsCube
called SelectableCube
.#include "Selectable.h"
inside the SelectableCube
class' header.ASelectableCube
declaration like this:class UE4COOKBOOK_API ASelectableCube : public APhysicsCube, public ISelectable
ASelectableCube(); virtual void NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVectorHitLocation, FVectorHitNormal, FVectorNormalImpulse, constFHitResult& Hit) override;
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(); } }
NonSelectableCube
, which inherits from SelectableCube
.NonSelectableCube
should override the functions from SelectableInterface
:virtual bool IsSelectable() override; virtual bool TrySelect() override; virtual void Deselect() override;
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(); }
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.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.Selectable
interface.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.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.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.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.Deselect
as a pure virtual
function if you wish.SelectableCube
is a new class inheriting from PhysicsCube
, but also implementing the ISelectable
interface.NotifyHit
, a virtual
function defined in AActor
that triggers when the actor undergoes a RigidBody collision.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.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.IsSelectable
, and if so, we try to actually perform the selection using TrySelect
.NonSelectableCube
inherits from SelectableCube
, so we can force the object to never be selectable.ISelectable
interface functions again.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.IsSelectable()
, ANonSelectableCube::TrySelect()
always returns false
to indicate that the selection wasn't successful.NonSelectableCube
to be selected, Deselect()
calls unimplemented()
, which throws an assert warning that the function was not implemented.SelectableCube
/NonSelectableCube
hits another object, causing a RigidBody collision, the actor in question will attempt to select itself, and print messages to the screen.18.220.34.198