Skip to main content
ARShow
ARShow
 首页 » 资源教程 » UE4教程

【UE4学习】04——官方教程代码

2016年09月10日 16:00:0714690

实现平台:win8.1  UE4.10

按教程练习。

----------------------------------------------

1、实现Pawn移动(input)

MyPawn.h

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "GameFramework/Pawn.h"  
  6. #include "MyPawn.generated.h"  
  7.   
  8. UCLASS()  
  9. class MYPROJECT2_BP_API AMyPawn : public APawn  
  10. {  
  11.     GENERATED_BODY()  
  12.   
  13. public:  
  14.     // Sets default values for this pawn's properties  
  15.     AMyPawn();  
  16.   
  17.     // Called when the game starts or when spawned  
  18.     virtual void BeginPlay() override;  
  19.       
  20.     // Called every frame  
  21.     virtual void Tick( float DeltaSeconds ) override;  
  22.   
  23.     // Called to bind functionality to input  
  24.     virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;  
  25.   
  26.     UPROPERTY(EditAnywhere)  
  27.         USceneComponent* OurVisibleComponent;  
  28.       
  29.     void MoveForward(float Value);  
  30.     void MoveRight(float Value);  
  31.     void Bigger();  
  32.     void Smaller();  
  33.   
  34.     bool bGrowing;  
  35.     FVector CurrentVelocity;  
  36. };  
Mypawn.cpp

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "MyProject2_BP.h"  
  4. #include "MyPawn.h"  
  5.   
  6.   
  7. // Sets default values  
  8. AMyPawn::AMyPawn()  
  9. {  
  10.     // Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  11.     PrimaryActorTick.bCanEverTick = true;  
  12.   
  13.     AutoPossessPlayer = EAutoReceiveInput::Player0;  
  14.   
  15.     RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("MyRootComponent"));  
  16.     UCameraComponent* Mycamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera"));  
  17.     OurVisibleComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("OurVisibleComponent"));  
  18.     Mycamera->AttachTo(RootComponent);  
  19.     Mycamera->SetRelativeLocation(FVector(-250.0f, 0.0f, 250.0f));  
  20.     Mycamera->SetRelativeRotation(FRotator(-45.0f, 0, 0));  
  21.     OurVisibleComponent->AttachTo(RootComponent);  
  22.   
  23.   
  24. }  
  25.   
  26. // Called when the game starts or when spawned  
  27. void AMyPawn::BeginPlay()  
  28. {  
  29.     Super::BeginPlay();  
  30.       
  31. }  
  32.   
  33. // Called every frame  
  34. void AMyPawn::Tick( float DeltaTime )  
  35. {  
  36.     Super::Tick( DeltaTime );  
  37.     float CurrentScale = OurVisibleComponent->GetComponentScale().X;  
  38.     if (bGrowing)  
  39.     {  
  40.         //  在一秒的时间内增长到两倍的大小  
  41.         CurrentScale += DeltaTime;  
  42.     }  
  43.     else  
  44.     {  
  45.         // 随着增长收缩到一半  
  46.         CurrentScale -= (DeltaTime * 0.5f);  
  47.     }  
  48.     // 确认永不低于起始大小,或增大之前的两倍大小。  
  49.     CurrentScale = FMath::Clamp(CurrentScale, 1.0f, 2.0f);  
  50.     OurVisibleComponent->SetWorldScale3D(FVector(CurrentScale));  
  51.   
  52.   
  53. // 基于"MoveX"和 "MoveY"坐标轴来处理移动  
  54.     if (!CurrentVelocity.IsZero())  
  55.     {  
  56.         FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime);  
  57.         SetActorLocation(NewLocation);  
  58.     }  
  59.   
  60.   
  61. }  
  62.   
  63. // Called to bind functionality to input  
  64. void AMyPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)  
  65. {  
  66.     Super::SetupPlayerInputComponent(InputComponent);  
  67.     InputComponent->BindAction("Bigger", IE_Pressed, this, &AMyPawn::Bigger);  
  68.     InputComponent->BindAction("Smaller", IE_Pressed, this, &AMyPawn::Smaller);  
  69.     InputComponent->BindAxis("MoveForward"this, &AMyPawn::MoveForward);  
  70.     InputComponent->BindAxis("MoveRight"this, &AMyPawn::MoveRight);  
  71.   
  72. }  
  73. void AMyPawn::MoveForward(float Value)  
  74. {  
  75.     CurrentVelocity.X = Value * 100.0f;  
  76.       
  77. }  
  78. void AMyPawn::MoveRight(float Value)  
  79. {  
  80.     CurrentVelocity.Y = Value * 100.0f;  
  81. }  
  82.   
  83. void AMyPawn::Bigger()  
  84. {  
  85.     bGrowing = true;  
  86. }  
  87.   
  88. void AMyPawn::Smaller()  
  89. {  
  90.     bGrowing = false;  
  91. }  


==============================================================================

2、实现相机自动切换(注意需要两个camera)

CameraDirector.h

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "GameFramework/Actor.h"  
  6. #include "CameraDirector.generated.h"  
  7.   
  8. UCLASS()  
  9. class MYPROJECT2_BP_API ACameraDirector : public AActor  
  10. {  
  11.     GENERATED_BODY()  
  12.       
  13. public:   
  14.     // Sets default values for this actor's properties  
  15.     ACameraDirector();  
  16.   
  17.     // Called when the game starts or when spawned  
  18.     virtual void BeginPlay() override;  
  19.       
  20.     // Called every frame  
  21.     virtual void Tick( float DeltaSeconds ) override;  
  22.   
  23.     UPROPERTY(EditAnywhere)  
  24.         AActor* CameraOne;  
  25.   
  26.     UPROPERTY(EditAnywhere)  
  27.         AActor* CameraTwo;  
  28.   
  29.     float TimeToNextCameraChange;  
  30.       
  31. };  

CameraDirector.cpp

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "MyProject2_BP.h"  
  4. #include "CameraDirector.h"  
  5. #include "Kismet/GameplayStatics.h"  
  6.   
  7. // Sets default values  
  8. ACameraDirector::ACameraDirector()  
  9. {  
  10.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  11.     PrimaryActorTick.bCanEverTick = true;  
  12.   
  13. }  
  14.   
  15. // Called when the game starts or when spawned  
  16. void ACameraDirector::BeginPlay()  
  17. {  
  18.     Super::BeginPlay();  
  19.       
  20. }  
  21.   
  22. // Called every frame  
  23. void ACameraDirector::Tick( float DeltaTime )  
  24. {  
  25.     Super::Tick( DeltaTime );  
  26.   
  27.     const float TimeBetweenCameraChanges = 2.0f;  
  28.     const float SmoothBlendTime = 0.75;  
  29.     TimeToNextCameraChange -= DeltaTime;  
  30.     if (TimeToNextCameraChange<=0.0f)  
  31.     {  
  32.         TimeToNextCameraChange += TimeBetweenCameraChanges;  
  33.   
  34.         APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0);  
  35.         if (OurPlayerController)  
  36.         {  
  37.             if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr))  
  38.             {  
  39.                 // 立即切换到相机1。  
  40.                 OurPlayerController->SetViewTarget(CameraOne);  
  41.             }  
  42.             else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr))  
  43.             {  
  44.                 // 平滑地混合到相机2。  
  45.                 OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);  
  46.             }  
  47.         }  
  48.   
  49.     }  
  50.   
  51. }  

==============================================================================


3、实现倒计时(timer)(注意宏的用法、C++与bluepringt交互)

CountDown.h

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "GameFramework/Actor.h"  
  6. #include "CountDown.generated.h"  
  7.   
  8. UCLASS()  
  9. class MYPROJECT2_BP_API ACountDown : public AActor  
  10. {  
  11.     GENERATED_BODY()  
  12.       
  13. public:   
  14.     // Sets default values for this actor's properties  
  15.     ACountDown();  
  16.   
  17.     // Called when the game starts or when spawned  
  18.     virtual void BeginPlay() override;  
  19.       
  20.     // Called every frame  
  21.     virtual void Tick( float DeltaSeconds ) override;  
  22.       
  23.     UPROPERTY(EditAnywhere, Category = "CountNumber")  
  24.     int32 CountDownTime;  
  25.   
  26.     UTextRenderComponent* CountDownText;  
  27.       
  28.     void UpdataTimerDisplay();  
  29.       
  30.     void AdvanceTimer();  
  31.   
  32.     UFUNCTION(BlueprintNativeEvent, Category = "CountNumber")  
  33.     void CountdownHasFinish();  
  34.     virtual void CountdownHasFinish_Implementation();  
  35.       
  36.     FTimerHandle CountdownTimerhandle;  
  37.   
  38. };  

CountDown.cpp

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "MyProject2_BP.h"  
  4. #include "CountDown.h"  
  5.   
  6.   
  7. // Sets default values  
  8. ACountDown::ACountDown()  
  9. {  
  10.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  11.     PrimaryActorTick.bCanEverTick = true;  
  12.   
  13.     CountDownText = CreateDefaultSubobject<UTextRenderComponent>(TEXT("CountdownNumber"));  
  14.     CountDownText->SetHorizontalAlignment(EHTA_Center);  
  15.     CountDownText->SetWorldSize(150.0f);  
  16.     RootComponent = CountDownText;  
  17.     CountDownTime = 3.0f;  
  18. }  
  19.   
  20. void ACountDown::UpdataTimerDisplay()  
  21. {  
  22.     CountDownText->SetText((FString::FromInt(FMath::Max(CountDownTime, 0))));  
  23. }  
  24.   
  25. void ACountDown::AdvanceTimer()  
  26. {  
  27.     --CountDownTime;  
  28.     UpdataTimerDisplay();  
  29.     if (CountDownTime<1)  
  30.     {  
  31.         GetWorldTimerManager().ClearTimer(CountdownTimerhandle);  
  32.         CountdownHasFinish();  
  33.     }  
  34. }  
  35.   
  36. //void ACountDown::CountdownHasFinish()  
  37. //{  
  38. //  //CountDownText->SetText(TEXT("GO!"));  
  39. //}  
  40. void ACountDown::CountdownHasFinish_Implementation()  
  41. {  
  42.     //Change to a special readout  
  43.     CountDownText->SetText(TEXT("GO!"));  
  44. }  
  45.   
  46. // Called when the game starts or when spawned  
  47. void ACountDown::BeginPlay()  
  48. {  
  49.     Super::BeginPlay();  
  50.     UpdataTimerDisplay();  
  51.     GetWorldTimerManager().SetTimer(CountdownTimerhandle, this, &ACountDown::AdvanceTimer, 1.0f, true);  
  52.       
  53. }  
  54.   
  55. // Called every frame  
  56. void ACountDown::Tick( float DeltaTime )  
  57. {  
  58.     Super::Tick( DeltaTime );  
  59.   
  60. }  

==============================================================================

4、Pwan与碰撞(粒子特效) 注意碰撞时,按下space才会产生火焰

ColisionPawn.h

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "GameFramework/Pawn.h"  
  6. #include "ColisionPawn.generated.h"  
  7.   
  8. UCLASS()  
  9. class MYPROJECT2_BP_API AColisionPawn : public APawn  
  10. {  
  11.     GENERATED_BODY()  
  12.   
  13. public:  
  14.     // Sets default values for this pawn's properties  
  15.     AColisionPawn();  
  16.   
  17.     // Called when the game starts or when spawned  
  18.     virtual void BeginPlay() override;  
  19.       
  20.     // Called every frame  
  21.     virtual void Tick( float DeltaSeconds ) override;  
  22.   
  23.     // Called to bind functionality to input  
  24.     virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;  
  25.   
  26.     UParticleSystemComponent* MyParticleSystem;  
  27.       
  28.     class UColisionPawnMovementComponent* MyMovementComponent;  
  29.   
  30.     virtual UPawnMovementComponent* GetMovementComponent() const override;  
  31.   
  32.     void MoveForward(float AxisValue);  
  33.     void MoveRight(float AxisValue);  
  34.     void Turn(float AxisValue);  
  35.     void ParticleToggle();  
  36. };  
ColisionPawn.cpp
[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "MyProject2_BP.h"  
  4. #include "ColisionPawn.h"  
  5. #include "ColisionPawnMovementComponent.h"  
  6.   
  7. // Sets default values  
  8. AColisionPawn::AColisionPawn()  
  9. {  
  10.     // Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  11.     PrimaryActorTick.bCanEverTick = true;  
  12.   
  13.     // Our root component will be a sphere that reacts to physics  
  14.     USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));  
  15.     RootComponent = SphereComponent;  
  16.     SphereComponent->InitSphereRadius(40.f);  
  17.     SphereComponent->SetCollisionProfileName(TEXT("Pawn"));  
  18.       
  19.     // Create and position a mesh component so we can see where our sphere is  
  20.     UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));  
  21.     SphereVisual->AttachTo(SphereComponent);  
  22.     static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));  
  23.     if (SphereVisualAsset.Succeeded())    
  24.     {  
  25.         SphereVisual->SetStaticMesh(SphereVisualAsset.Object);  
  26.         SphereVisual->SetRelativeLocation(FVector(0, 0, 0));  
  27.         SphereVisual->SetWorldScale3D(FVector(0.8f));  
  28.     }  
  29.   
  30.     // Create a particle system that we can activate or deactivate  
  31.     MyParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticle"));  
  32.     MyParticleSystem->AttachTo(SphereVisual);  
  33.     MyParticleSystem->bAutoActivate = false;  
  34.     MyParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));  
  35.     static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));  
  36.     if (ParticleAsset.Succeeded())  
  37.     {  
  38.         MyParticleSystem->SetTemplate(ParticleAsset.Object);  
  39.     }  
  40.   
  41.     // Use a spring arm to give the camera smooth, natural-feeling motion.  
  42.     USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));  
  43.     SpringArm->AttachTo(RootComponent);  
  44.     SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);  
  45.     SpringArm->TargetArmLength = 400.0f;  
  46.     SpringArm->bEnableCameraLag = true;  
  47.     SpringArm->CameraLagSpeed = 3.0f;  
  48.   
  49.     // Create a camera and attach to our spring arm  
  50.     UCameraComponent* MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("AcrualCamera"));  
  51.     MyCamera->AttachTo(SpringArm, USpringArmComponent::SocketName);  
  52.   
  53.     // Take control of the default player  
  54.     AutoPossessPlayer = EAutoReceiveInput::Player0;  
  55.   
  56.     // Create an instance of our movement component, and tell it to update the root.  
  57.     MyMovementComponent = CreateDefaultSubobject<UColisionPawnMovementComponent>(TEXT("CustomMovementComponent"));  
  58.     MyMovementComponent->UpdatedComponent = RootComponent;  
  59. }  
  60.   
  61.   
  62. UPawnMovementComponent* AColisionPawn::GetMovementComponent() const  
  63. {  
  64.     return MyMovementComponent;  
  65. }  
  66.   
  67. void AColisionPawn::MoveForward(float AxisValue)  
  68. {  
  69.     if (MyMovementComponent && (MyMovementComponent->UpdatedComponent == RootComponent))  
  70.     {  
  71.         MyMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);  
  72.     }  
  73. }  
  74.   
  75. void AColisionPawn::MoveRight(float AxisValue)  
  76. {  
  77.     if (MyMovementComponent && (MyMovementComponent->UpdatedComponent == RootComponent))  
  78.     {  
  79.         MyMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);  
  80.     }  
  81. }  
  82.   
  83. void AColisionPawn::Turn(float AxisValue)  
  84. {  
  85.     FRotator NewRotation = GetActorRotation();  
  86.     NewRotation.Yaw += AxisValue;  
  87.     SetActorRotation(NewRotation);  
  88. }  
  89.   
  90. void AColisionPawn::ParticleToggle()  
  91. {  
  92.     if (MyParticleSystem && MyParticleSystem->Template)  
  93.     {  
  94.         MyParticleSystem->ToggleActive();  
  95.     }  
  96. }  
  97.   
  98.   
  99. // Called when the game starts or when spawned  
  100. void AColisionPawn::BeginPlay()  
  101. {  
  102.     Super::BeginPlay();  
  103. }  
  104.   
  105. // Called every frame  
  106. void AColisionPawn::Tick( float DeltaTime )  
  107. {  
  108.     Super::Tick( DeltaTime );  
  109.   
  110. }  
  111.   
  112. // Called to bind functionality to input  
  113. void AColisionPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)  
  114. {  
  115.     Super::SetupPlayerInputComponent(InputComponent);  
  116.   
  117.     InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &AColisionPawn::ParticleToggle);  
  118.     InputComponent->BindAxis("MoveForward"this, &AColisionPawn::MoveForward);  
  119.     InputComponent->BindAxis("MoveRight"this, &AColisionPawn::MoveRight);  
  120.     InputComponent->BindAxis("Turn"this, &AColisionPawn::Turn);  
  121. }  


ColisionPawnMovementComponent.h

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "GameFramework/PawnMovementComponent.h"  
  6. #include "ColisionPawnMovementComponent.generated.h"  
  7.   
  8. /** 
  9.  *  
  10.  */  
  11. UCLASS()  
  12. class MYPROJECT2_BP_API UColisionPawnMovementComponent : public UPawnMovementComponent  
  13. {  
  14.     GENERATED_BODY()  
  15. public:  
  16.     virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction);  
  17.       
  18.       
  19.       
  20. };  

ColisionPawnMovementComponent.cpp

[cpp] view plain
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "MyProject2_BP.h"  
  4. #include "ColisionPawnMovementComponent.h"  
  5.   
  6. void UColisionPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)  
  7. {  
  8.     Super::TickComponent(DeltaTime, TickType, ThisTickFunction);  
  9.   
  10.     // Make sure that everything is still valid, and that we are allowed to move.  
  11.     if (!PawnOwner||!UpdatedComponent||ShouldSkipUpdate(DeltaTime))  
  12.     {  
  13.         return;  
  14.     }  
  15.   
  16.     // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick  
  17.     FVector DesireMovementthisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f)*DeltaTime*150.0f;  
  18.     if (!DesireMovementthisFrame.IsNearlyZero())  
  19.     {  
  20.         FHitResult Hit;  
  21.         SafeMoveUpdatedComponent(DesireMovementthisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);  
  22.   
  23.         // If we bumped into something, try to slide along it  
  24.         if (Hit.IsValidBlockingHit())  
  25.         {  
  26.             SlideAlongSurface(DesireMovementthisFrame, 1.f - Hit.Time, Hit.Normal, Hit);  
  27.         }  
  28.     }  
  29. }  

This TickComponent function makes use of a few of the powerful features offered by the UPawnMovementComponentclass. 

ConsumeInputVector reports and clears the value of a built-in variable that we will use to store our movement inputs. 

SafeMoveUpdatedComponent uses Unreal Engine physics to move our Pawn Movement Component while respecting solid barriers. 

SlideAlongSurface handles the calculations and physics involved with sliding smoothly along collision surfaces like walls and ramps when a move results in a collision, rather than simply stopping in place and sticking to the wall or ramp. 

There are more features included in Pawn Movement Components that are worthy of examination, but they are not needed for the scope of this tutorial. Looking at other classes, such as Floating Pawn MovementSpectator Pawn Movement, or Character Movement Component, could provide additional usage examples and ideas.


============================================================

5、User Interface With UMG

Work-In-Progress Code

HowTo_UMG.Build.cs

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.using UnrealBuildTool;public class HowTo_UMG : ModuleRules{
    public HowTo_UMG(TargetInfo Target)
    {
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

        //PrivateDependencyModuleNames.AddRange(new string[] {  });

        // Uncomment if you are using Slate UI
        PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");
        // if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64))
        // {
        //      if (UEBuildConfiguration.bCompileSteamOSS == true)
        //      {
        //          DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
        //      }
        // }
    }}

2. Extend Our Game Mode

  • SHARE:
  1. The menus we create will be made from User Widgets. We'll need to write a function that creates and displays a new User Widget, and then call that function when the game starts. We'll also need to keep track of what we have created so that we can remove it later. Since each project already comes with a custom GameMode class, we can simply open ours, which is defined in HowTo_UMGGameMode.h. The following functions and properties will need to be added to the bottom of the class:

    public:
        /** Called when the game starts. */
        virtual void BeginPlay() override;
    
        /** Remove the current menu widget and create a new one from the specified class, if provided. */
        UFUNCTION(BlueprintCallable, Category = "UMG Game")
        void ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass);protected:
        /** The widget class we will use as our menu when the game starts. */
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UMG Game")
        TSubclassOf<UUserWidget> StartingWidgetClass;
    
        /** The widget instance that we are using as our menu. */
        UPROPERTY()
        UUserWidget* CurrentWidget;
  2. In order to use User Widgets in our code, and add the following line to the top of the #include section:

    #include "Blueprint/UserWidget.h"
  3. Moving to HowTo_UMGGameMode.cpp now, we need to fill out the bodies of the two functions we declared. We'll start with overridingBeginPlay():

    void AHowTo_UMGGameMode::BeginPlay(){
        Super::BeginPlay();
        ChangeMenuWidget(StartingWidgetClass);}

    When overriding functions from a parent class (referenced by the word Super), as we do here with BeginPlay, it is often important to call the parent class' version of that function. Since our version of the function is only meant to add one step to the end of the existing procedure, we call Super::BeginPlay in the first line of the function.

  4. Next, still in HowTo_UMGGameMode.cpp, we need to define how we change between menus. We will need to remove whatever User Widget we have active from the viewport, if any. Then we can create and a new User Widget and add it to the viewport.

    void AHowTo_UMGGameMode::ChangeMenuWidget(TSubclassOf<UUserWidget> NewWidgetClass){
        if (CurrentWidget != nullptr)
        {
            CurrentWidget->RemoveFromViewport();
            CurrentWidget = nullptr;
        }
        if (NewWidgetClass != nullptr)
        {
            CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), NewWidgetClass);
            if (CurrentWidget != nullptr)
            {
                CurrentWidget->AddToViewport();
            }
        }}

    This code will create instances of any Widgets we provide and put them on the screen. It also removes them, so that only one is active at a time, although Unreal Engine can handle displaying and interacting with many Widgets at once. We never need to destroy a Widget directly, because removing it from the viewport and clearing (or changing) all variables that reference it will cause it to be cleaned up by Unreal Engine's garbage collection system.


We have built the code framework to create and display menus, and remove them when they are no longer needed. We're ready to return to theUnreal Editor and design our menu assets!

3. Create Menu Widget Blueprints

  1. In Unreal Editor, we can press the Compile button to build our new code. This will allow us to use User Widgets as menus.

    【UE4学习】04——官方教程代码 UE4教程 第1张

  2. We will now create the User Widgets that our GameMode will use as menus. This is done with the Add New button in the Content Browser. The Widget Blueprint class is found in the User Interface category. We need to create two of these, one named MainMenu and one named NewGameMenu. Our game will begin at the Main Menu, and will have an option to proceed to the New Game Menu.

    【UE4学习】04——官方教程代码 UE4教程 第2张

  3. Double-clicking the MainMenu Widget we've just created will take us to the Blueprint Designer, where we can create our menu layout.

  4. Let's drag a Button and a Text from the Common section of the Palette Panel to the Graph. This Button will eventually be used to open the New Game Menu.

    【UE4学习】04——官方教程代码 UE4教程 第3张

  5. The first step in making our layout look right is to adjust the location and size of our Button. We should make the following changes:

    • Set the size to 200x200.

    • Set the position to (200, 100).

    • Rename it NewGameButton, just to make it easier to recoginze when we hook up functionality to it later on.

    【UE4学习】04——官方教程代码 UE4教程 第4张

  6. Since we're not drawing a custom set of images for the Button, we can label it by dragging and dropping the Text Block onto it and making the following changes:

    • Set the text to say New Game.

    • Change the Visibility to Hit Test Invisible. This will prevent the Text Block from intercepting mouse-clicks that are intended for the Button underneath.

    • Set the name to NewGameText. This is not needed, but it's a good habit.

    【UE4学习】04——官方教程代码 UE4教程 第5张

  7. Next, we'll want to make a "Quit" feature with a second Button and Text Block. Set those up in the same way as the New Game Button and Text Block except the following changes.

    • Set the name of the Button to QuitButton

    • Set the position of the button to 600, 100

    • Set the name of the Text Block to QuitText

  8. After that, we can add Events to our Buttons so that we can run code when a Button is clicked. This is done by locating and pressing the + next to the appropriate Event name in the Details Panel. In this case, OnClicked is the event we want to use. Create this event for both the NewGameButton amd QuitButton Widgets.

    【UE4学习】04——官方教程代码 UE4教程 第6张

    Designers can build functionality with Blueprint scripting here, or C++ programmers can connect nodes that call exposed functions.

  9. For the Event called OnClicked(NewGameButton), we'll want to:

    • Connect a ChangeMenuWidget node to use the function we added to our GameMode earlier.

    • Set the New Widget Class field on the ChangeMenuWidget node to the NewGameMenu asset.

    【UE4学习】04——官方教程代码 UE4教程 第7张

  10. For the OnClicked(QuitButton) Event, we'll want to:

    • Connect a Quit Game node.

    【UE4学习】04——官方教程代码 UE4教程 第8张


4. Configure Our Game Mode

With our main menu built, we can set up a GameMode asset that will load it as soon as the level starts.

  1. In the Content Browser, we will add a Blueprint Class based on our project's GameMode. This makes it possible to set the exposed variables on those two classes to whatever values we want. To do this:

    • Click the Add button in the Content Browser.

    【UE4学习】04——官方教程代码 UE4教程 第9张

    • Pick HowTo_UMGGameMode as the parent class. It will be listed in the All Classes section.

    【UE4学习】04——官方教程代码 UE4教程 第10张

    • Name the resulting Blueprint asset MenuGameMode.

  2. In order to see our mouse cursor in-game, we'll need to create a Blueprint of the PlayerController as we did with our GameMode.

    • Click the Add button in the Content Browser again.

    • Select Player Controller from the Common Classes section.

    • Name the Blueprint MenuPlayerController.

  3. Edit MenuPlayerController.

    • Check the Show Mouse Cursor box.

    【UE4学习】04——官方教程代码 UE4教程 第11张

  4. Edit MenuGameMode.

    • The Starting Widget Class must be set to the MainMenu asset in order to bring up our menu when the game begins.

    • The Default Pawn Class should be set to Pawn instead of DefaultPawn so that our player will not be able to fly around while in the menu.

    • The Player Controller Class should be set to the MenuPlayerController asset we created so that the mouse cursor will be shown in-game.

    【UE4学习】04——官方教程代码 UE4教程 第12张

  5. In order for our Blueprint to be used, we must return to the Level Editor window and change the World Settings for our current Level via the Settings button.

    【UE4学习】04——官方教程代码 UE4教程 第13张

    It is also possible to set the default GameMode in the Project Settings menu, under the Maps and Modes section. If you use this method, all of your levels will default to the GameMode you choose, unless overridden individually. Which method you use depends on how you prefer to set up your project.

  6. The World Settings Panel will open up. By default, it will be docked with the Details Panel, but it can be moved elsewhere. We need to set the Game Mode Override field to our MenuGameMode asset.

    【UE4学习】04——官方教程代码 UE4教程 第14张


Our custom GameMode asset is now in effect on our level, configured to load our Main Menu, and use our Player Controller that shows the mouse cursor. If we run the game now, our Quit button will work as expected, and our New Game button will take us to an empty menu screen. Our next step will be to set up the New Game menu.

5. Build A Second Menu

  1. In the Content Browser, find and open the NewGameMenu asset we created earlier. This menu will contain a name-entry Text Box, aButton to play the game that cannot be pressed until a name is entered, and a Button to return to the main menu.

  2. To create the name entry box, we'll drag a Text Box (not a Text Block) into the layout.

    【UE4学习】04——官方教程代码 UE4教程 第15张

  3. The Text Box should be configured with the following values:

    • Change the name to NameTextEntry

    • Position is (325, 200). This leaves room for a Text Block placed to the left of the Text Box.

    • Size is 250x40.

    • Font Size (under the "Style" heading) is 20.

    【UE4学习】04——官方教程代码 UE4教程 第16张

  4. We can create the Play Game Button with a Text Block label the same way we created the Buttons on the previous menu.

    • For the Button: Change the name to PlayGameButton, Position to 200, 300, Size to 200, 100

    • For the Text Block: Change the name to PlayGameText, set Visibility to Hit Test Visible, and drag it on top of the PlayGameButton

  5. The Play Game Button will have a special feature - it will be enabled only when the name entered in the Text Box is not empty. We can use Unreal Motion Graphics' (UMG) bind feature to create a new function for the Is Enabled field (under the Behavior section).

    【UE4学习】04——官方教程代码 UE4教程 第17张

    If there were complex rules for determining what constitutes a valid player name in our game, or if we needed to save the name to a C++ variable, we might expose a UFUNCTION on our GameMode or as a static function somewhere within our project. However, since we only care that the name string is not empty, we can script it right here in our Widget.

  6. To ensure that the Button is enabled if and only if the Text Box is not empty, we can convert the text from the Text Box to a string and then check that its length is greater than zero. Here is how that logic would appear:

    【UE4学习】04——官方教程代码 UE4教程 第18张

  7. Let's add one more Button so we can back out and get to our Main Menu from here. This will be just like the Play Game Button from our Main Menu, but it will be positioned relative to the lower-right corner instead of the upper-left. To accomplish this, click the Anchorsdropdown in the Details Panel for the Button and find the appropriate graphical representation in the pop-up menu.

    • Change the name to MainMenuButton

    • Set the Position to -400, -200

    • Set the Size to 200x100

    【UE4学习】04——官方教程代码 UE4教程 第19张

    Positioning our anchor at the lower-right corner does not change how size and position values work, so we'll need to make our position values negative in order to be on the screen. Size values will remain positive.

  8. We will now add scripting to our new Buttons by once again adding OnClicked events. The Main Menu Button will simply reload the Main Menu Widget, while the Play Game Button will deactivate our menu entirely by having no new Widget provided in the call to ourChangeMenuWidget function. This is shown by the phrase Select Class being displayed instead of the name of an actual class or asset.

    【UE4学习】04——官方教程代码 UE4教程 第20张

    After deactivating the menu with the Play Game Button, we will be unable to do anything further in our game. This is the point at which we would normally load the first level, play an introductory cutscene, or spawn and possess a Pawn.

  9. We should now have two screens that look roughly as follows:

    【UE4学习】04——官方教程代码 UE4教程 第21张

    【UE4学习】04——官方教程代码 UE4教程 第22张


评论列表暂无评论
发表评论