前言

虚幻引擎在定义关卡时,将关卡设定为是一个World,从关卡类型可以得知UWorld类型。一个世界具备一个世界的规则。两个不同的关卡可以使用相同的规则,但是不是同一个规则。就如同我们的法律中规定盗窃是犯罪,其他国家一样也有这样的法律规则。

并且虚幻引擎在一定意义上不允许有两个世界同时存在,所以当我们切换关卡时,旧有的关卡对象必然会被释放掉!而新关卡对象与旧有关卡无关。

那是否可以携带旧有关卡中的对象到新关卡呢?答案是可以的!只不过我们需要借助虚幻引擎中的无缝漫游设置。

注意:蓝图无法直接实现,需要依赖C++

步骤

  1. 开启无缝漫游,需要在Gamemode的代码中设置,或是在Gamemode蓝图面板中设置(二选一)。
c++
1
2
3
4
5
ACarryThroughGameModeBase::ACarryThroughGameModeBase()
{
    //应用无缝漫游 Gamemode构造函数中设置
    bUseSeamlessTravel = true;
}

或是在继承Gamemode的蓝图类面板中设置

GameMode细节面板
GameMode细节面板

  1. C++中Gamemode重写父类GetSeamlessTravelActorList函数
c++
1
virtual void GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList) override;

注意,此函数在切换关卡使用无缝漫游时会被调用,调用时,需要将漫游过关卡的Actor添加到参数ActorList中,即可将Actor携带过关。

实现代码参照

c++
1
2
3
4
5
6
7
8
9
void ACarryThroughGameModeBase::GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList)
{
    //需要优先调用,蓝图传入会覆盖ActorList
    K2_GetSeamlessTravelActorList(ActorList);
    //将自身存储数组数据追加到List数组中
    ActorList.Append(TravelActorList);
    //必须调用父类函数
    Super::GetSeamlessTravelActorList(bToTransition, ActorList);
}

其中K2_GetSeamlessTravelActorList是我添加的函数,用于向蓝图暴露,蓝图可重写,函数声明如下

c++
1
2
UFUNCTION(BlueprintImplementableEvent, DisplayName="GetSeamlessTravelActorList")
void K2_GetSeamlessTravelActorList(TArray<AActor*>& ActorList);

TravelActorList是我在GameMode中添加的用来存储保存过关Actor的数组。

ACarryThroughGameModeBase.h源码

c++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#pragma once
 
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "CarryThroughGameModeBase.generated.h"
 
/**
 * 
 */
UCLASS()
class CARRYTHROUGH_API ACarryThroughGameModeBase : public AGameModeBase
{
    GENERATED_BODY()
 
 
    ACarryThroughGameModeBase();
     
protected:
 
    virtual void GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList) override;
 
    UFUNCTION(BlueprintCallable)
    void AddTravelActorList(AActor* Actor);
    UFUNCTION(BlueprintImplementableEvent, DisplayName="GetSeamlessTravelActorList")
    void K2_GetSeamlessTravelActorList(TArray<AActor*>& ActorList);
 
protected:
     
    UPROPERTY()
    TArray<AActor*> TravelActorList;
};

ACarryThroughGameModeBase.cpp源码

c++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "CarryThroughGameModeBase.h"
 
ACarryThroughGameModeBase::ACarryThroughGameModeBase()
{
    //应用无缝漫游
    bUseSeamlessTravel = true;
}
 
void ACarryThroughGameModeBase::GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList)
{
    //需要优先调用,蓝图传入会覆盖ActorList
    K2_GetSeamlessTravelActorList(ActorList);
    //将自身存储数组数据追加到List数组中
    ActorList.Append(TravelActorList);
    //必须调用父类函数
    Super::GetSeamlessTravelActorList(bToTransition, ActorList);
}
 
void ACarryThroughGameModeBase::AddTravelActorList(AActor* Actor)
{
    TravelActorList.Add(Actor);
}

蓝图中重写函数截图

BP_Gaemode继承自ACarryThroughGameModeBase
BP_Gaemode继承自ACarryThroughGameModeBase

  1. 启动切换关卡 切换关卡时注意需要独立进程启动,并且不要使用Openlevel节点!可使指令节点,启用无缝切换地图。
    指令命令参照(注意分割空格):ServerTravel /Game/Maps/l02

也可以在C++中调用代码切换,源码结构如下:

c++
1
2
3
4
5
6
7
8
9
/**
     * Jumps the server to new level.  If bAbsolute is true and we are using seemless traveling, we
     * will do an absolute travel (URL will be flushed).
     *
     * @param URL the URL that we are traveling to
     * @param bAbsolute whether we are using relative or absolute travel
     * @param bShouldSkipGameNotify whether to notify the clients/game or not
     */
bool ServerTravel(const FString& InURL, bool bAbsolute = false, bool bShouldSkipGameNotify = false);

源码位置:UWorld

效果展示

原始关卡
原始关卡
新关卡
新关卡

切换后Actor原有状态不变,数据不变。在一些设计中,仍需要考虑携带过关的Actor是否会关联其他对象,关联对象是否要被携带过关,所以这也是一个复杂的设计。

工程链接:传送门

提取码:2gcl

引擎版本: 5.1