之前说过,UE4没有特定的像JAVA一样的interface接口类。通过虚基类完成继承和方法的重写。
但是,UE4定义的接口类有一套固定的用法,这种用法对你的操作行为进行了限定,但对C++的本质并不影响。
1.接口中的函数需要用如下宏定义进行限定:UFUNCTION(BlueprintNativeEvent,BlueprintCallabe,Category = "Interface")
BlueprintNativeEvent是将该方法进行了限定,在C++和蓝图中都可以重写,且该方法有返回值时为方法,无返回值时为事件。
2.类(例如ACTOR类)中实现了方法,需要在方法名后加上"_Implementation"字样,表示该方法是从接口继承而来,即将在类中实现。
3.在执行重写了的接口方法时候,不能直接调用,而是通过“Execute_方法名”进行调用。
4.不论接口中的方法有没有参数,在Execute时候,都需要传递参数。这个参数就是实现类(例如ACTOR类)的对象。
定义一个接口类:
1 // Fill out your copyright notice in the Description page of Project Settings.
2
3 #pragma once
4
5 #include "CoreMinimal.h"
6 #include "UObject/Interface.h"
7 #include "Interactable.generated.h"
8
9 // This class does not need to be modified.
10 UINTERFACE(MinimalAPI)
11 class UInteractable : public UInterface
12 {
13 GENERATED_BODY()
14 };
15
16 /**
17 *
18 */
19 class MYPROJECT7_API IInteractable
20 {
21 GENERATED_BODY()
22
23 // Add interface functions to this class. This is the class that will be inherited to implement this interface.
24 public:
25
26 UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interactable")
27 void performance();
28
29 UFUNCTION(BlueprintNativeEvent,BlueprintCallable,Category = "Test")
30 void testing();
31
32 UFUNCTION(BlueprintNativeEvent,BlueprintCallable,Category = "TestNoUse")
33 int noUse();
34
35
36 };
定义一个类DOOR,实现该接口
1 // Fill out your copyright notice in the Description page of Project Settings.
2
3 #pragma once
4
5 #include "CoreMinimal.h"
6 #include "Engine/StaticMeshActor.h"
7 #include "Interactable.h"
8 #include "openable.h"
/*
版权所有:虎牙维护世界和平
博客园地址:https://www.cnblogs.com/dlak/
*/
9 #include "Door.generated.h"
10
11 /**
12 *
13 */
14 UCLASS()
15 class MYPROJECT7_API ADoor : public AStaticMeshActor,public IInteractable
16 {
17 GENERATED_BODY()
18
19 public:
20 ADoor();
21
22 UFUNCTION()
23 virtual void performance_Implementation() override;
24
25 UFUNCTION()
26 virtual void testing_Implementation() override;
27
28 UFUNCTION()
29 virtual int noUse_Implementation() override;
30 private:
31 int a;
32 };
33
34
35 // Fill out your copyright notice in the Description page of Project Settings.
36
37
38 #include "Door.h"
39 #include "UObject/ConstructorHelpers.h"
40 #include "Components/StaticMeshComponent.h"
41 #include "Engine.h"
42
43
44 ADoor::ADoor() {
45 UStaticMeshComponent* doorMesh = GetStaticMeshComponent();
46 auto mesh = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
47 if (mesh.Succeeded()) {
48 doorMesh->SetStaticMesh(mesh.Object);
49 doorMesh->SetMobility(EComponentMobility::Movable);
50 doorMesh->SetGenerateOverlapEvents(true);
51 }
52 doorMesh->SetWorldScale3D(FVector(0.3,2,3));
53 SetActorEnableCollision(true);
54
55 a = 20;
56 }
57
58
59
60 void ADoor::performance_Implementation()
61 {
62 GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, TEXT("the door will open!"));
63 AddActorLocalOffset(FVector(0.0f, 0.0f, 200.0f));
64 }
65
66
67
68 void ADoor::testing_Implementation()
69 {
70 GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Blue, TEXT("Testing"));
71 }
72
73
74 int ADoor::noUse_Implementation() {
75 a = 0;
76 return a;
77 }
我们来看看这些方法在蓝图中是什么样的。
这两个函数都是VOID类型的,没有返回值,所以变成了事件。
而这个函数是有返回值的,所以就是函数了。
定义一个pawn类用来做触发
1 // Fill out your copyright notice in the Description page of Project Settings.
2
3 #pragma once
4
5 #include "CoreMinimal.h"
6 #include "GameFramework/Pawn.h"
7 #include "Interactable.h"
8 #include "openable.h"
9 #include "Camera\PlayerCameraManager.h"
10 #include "MyPawn.generated.h"
11
12 UCLASS()
13 class MYPROJECT7_API AMyPawn : public APawn,public IInteractable
14 {
15 GENERATED_BODY()
16
17 public:
18 // Sets default values for this pawn's properties
19 AMyPawn();
20
21 protected:
22 // Called when the game starts or when spawned
23 virtual void BeginPlay() override;
24
25 public:
26 // Called every frame
27 virtual void Tick(float DeltaTime) override;
28
29 // Called to bind functionality to input
30 virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
31
32 void TryInteract(); //作为触发交互的方法,在input里面设置F键,作为交互触发按键,这里不上图了
33
34 private:
35 FVector keyInput;
36
37 void forward(float value);
38 };
以下是pawn类的源文件:
1 // Fill out your copyright notice in the Description page of Project Settings.
2
3
4 #include "MyPawn.h"
5 #include "Components\InputComponent.h"
6 #include "CollisionQueryParams.h"
7 #include "WorldCollision.h"
8 #include "Door.h"
9
10
11 // Sets default values
12 AMyPawn::AMyPawn()
13 {
14 // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
15 PrimaryActorTick.bCanEverTick = true;
16 keyInput = FVector::ZeroVector;
17 }
18
19 // Called when the game starts or when spawned
20 void AMyPawn::BeginPlay()
21 {
22 Super::BeginPlay();
23
24 }
25
26 // Called every frame
27 void AMyPawn::Tick(float DeltaTime)
28 {
29 Super::Tick(DeltaTime);
30 AddActorLocalOffset(keyInput);
31
32 }
33
34 // Called to bind functionality to input
35 void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
36 {
37 Super::SetupPlayerInputComponent(PlayerInputComponent);
38 PlayerInputComponent->BindAction("getdoor",IE_Released,this,&AMyPawn::TryInteract);
39 PlayerInputComponent->BindAxis("forward",this,&AMyPawn::forward);
40
41 }
42
43
44 void AMyPawn::TryInteract()
45 {
46 APlayerController* mycontroller = Cast<APlayerController>(Controller); //获得当前玩家控制器
47 if (mycontroller) {
48 APlayerCameraManager* myCameraManager = mycontroller->PlayerCameraManager; //将玩家控制器下的游戏摄像机管理组件交给APlayerCameraManager对象管理
49
50 auto startLocation = myCameraManager->GetCameraLocation();//读取的摄像机位置
51 auto endLocation = startLocation + (myCameraManager->GetActorForwardVector() * 100); //读取摄像机前方100个单位的位置
52
53 FCollisionObjectQueryParams params; // FCollisionObjectQueryParams类是用来处理碰撞物体信息的。
54 FHitResult hit;
55
//Pawn在世界里拥有一个射线,用于检测物体的,SweepSingleByObjectType就是来扫描单个碰到的物体,它可以设置许多属性。
56 GetWorld()->SweepSingleByObjectType(hit,
57 startLocation, endLocation,
58 FQuat::Identity, //自身旋转属性,每次旋转从固定值开始
59 FCollisionObjectQueryParams(FCollisionObjectQueryParams::AllObjects), //检查所有物体,不管是什么,都检查
60 FCollisionShape::MakeSphere(100), //设置碰撞体大小,圆柱形100个单位大小。
61 FCollisionQueryParams(FName("Interaction"), true, this));//给检测到已经碰撞的物体一个名字,开启复杂碰撞检测,且忽略自身。
62
63 if (hit.Actor != nullptr) {
64 auto find = hit.Actor->GetClass();
65 UObject* t = nullptr;
66 if (find->ImplementsInterface(UInteractable::StaticClass())) {
67 IInteractable::Execute_performance(hit.Actor.Get());
//这里需要强调的是,虽然performance在接口中没有参数,但是这里必须传递。
//是因为performance方法在DOOR类中实现了,功能是改变了STATICMESH的位移,所以需要传入一个对象。
68 }
69
70 IInteractable* my = Cast<IInteractable>(hit.Actor.Get());
71 my->Execute_testing(hit.Actor.Get());
72 my->Execute_noUse(hit.Actor.Get());
73 //传递参数的理由如上。
74 }
75 }
76 }
77
78 void AMyPawn::forward(float value)
79 {
80 keyInput.X = value * 5;
81 }
也可以这样:
1 if (hit.Actor != nullptr) {
2 ADoor* theDoor = Cast<ADoor>(hit.Actor);
3 if (theDoor) {
4 //IOpenable::Execute_showTime(theDoor);
5 theDoor->Execute_showTime(theDoor);
6 }
7 }
8
9 }
运行结果如下: