Как продолжить создавать карту в UE4

    Всем доброго дня


    В прошлой статье описывалось как создать уровень, взяв за референс участок с Яндекс.Карт. Осталось рассказать про «прокладывание» дорог и о тех нюансах, с которыми я столкнулся воспользовавшись World Composition, как методом оптимизации большой карты.



    Дороги


    Конечно, и «нарисованные» растянутой на уровень текстурой, дороги вполне пригодны для использования (например, для авиасимулятора), но для автосимулятора требуется более детальный подход. Наиболее подходящим компонентом для создания дорог в UE является Spline, по крайней мере «длинных непрерывных участков». Я пока не сталкивался с перекрёстками и примыканиями, скорее всего для них придётся отдельные объекты руками создавать.


    Spline существует два вида: Blueprint Splines и Landscape Splines. Первые обладают огромными возможностями по кастомизации и хорошо разобраны у Epic Games на канале. С их помощью, можно управлять созданием сразу и объектов вдоль дороги (указатели, ограждения, канавы, ЛЭП), однако я не нашёл у Blueprint Splines функционала по объединению с ландшафтом, а подгон каждого метра дороги руками под ландшафт (или наоборот) — это не то, с чего хочется начинать проект. По этому я выбрал Landscape Splines для создания дорог на первой версии уровня, просто взяв «Edit Splines Tool» и кликая по текстуре. Получилось две Splines — одна для асфальтовой дороги, другая для грунтовой. Перекрёстки сделаны просто наложением одной на другую. Чтобы Spline «материализовать», необходимо сегментам задать Mesh. Я взял готовый из Vehicle Game, там трасса тоже как Landscape Splines сделана (Epic Games Launcher > Unreal Engine > Library > Vault), вырезав из материала сложный кусок про следы. Получилась отличная грунтовая дорога! Для асфальтовой повторить такой трюк не удалось, по этому взял тот же Mesh, поменял в Material текстуру с грунтовой дороги на асфальтовую (типа этой) и применил её на Spline с Z scale = 0.2, получилось очень прилично. В конце надо не забыть деформировать ландшафт под Splines, что делается буквально одной кнопкой, и дороги готовы:



    World Composition


    Когда искал на YouTube про создание больших уровней, случайно наткнулся на это видео, где достаточно понятно показано про разделение уровня на несколько частей для оптимизации работы как во время игры, так и во время разработки. В UE для этого существует два механизма: World Composition и Level Streaming. Первый можно условно назвать «автоматическим» (вы делите уровень на кусочки по сетке, дальше UE сам занимается выгрузкой кусков из памяти за пределами заданного расстояния от игрока и подгрузкой их по мере перемещения игрока по уровню). Второй же «ручной», вы должны сами на одном уровне расположить Level Streaming Volumes и указать какой другой уровень подгружать (а какой выгружать), при попадании игрока в эту зону.


    Решил попробовать World Composition и переделал уровень из одного 4км на 4км в 16ть уровней по 1км на 1км каждый. Неожиданно для меня не пришлось ничего менять ни в карте высот, ни в материале с текстурой для уровня — всё «растянулось» само по себе на объединённый ландшафт. Единственный минус, замеченный на тот момент — невозможность менять Scale уровня после его создания. Т.е. раньше можно было загрузить карту высот в ландшафт и потом менять его Z-Scale подбирая необходимую высоту в игре, тут же необходимо сразу задать нужный Z-Scale, иначе придётся пересоздавать все уровни (что муторно даже при 16ти).


    При дальнейшей работе с World Composition вылезло ещё несколько важных моментов, на которые я хочу обратить внимание. В принципе, всё это ожидаемо и логично, если постоянно помнить, что этот механизм занимается асинхронной загрузкой уровней относительно положения игрока:

    1. Make current. Добавляя в свой мир объекты, необходимо постоянно следить, какой уровень выбран активным (выделен голубым цветом и жирным шрифтом в окне Levels), потому что именно этому уровню они будут принадлежать и загружаться\выгружаться вместе с ним. Все «глобальные» объекты должны добавляться на Persistent Level. Я случайно добавил PlayerStart на один из «подуровней», в итоге UE не знал где помещать игрока при старте игры, когда ещё ни один уровень не загружен.
    2. MyGameInstance.cpp Примерная последовательность действий UE при загрузке уровня с World Composition: загрузить Persistent Level > загрузить все объекты с Persistent Level > заспаунить игрока > определить список уровней в радиусе от игрока > загрузить все эти уровни и объекты с них. Как минимум пара последних шагов асинхронна, т.е. готовность мира к тому моменту, как игрок в нём появится, определяется производительностью ПК. В силу своего сетапа я достаточно быстро столкнулся с тем, что грузовик с включённой физикой появлялся раньше, чем дорога под ним =) Хорошо, что я оказался не первым и даже не вторым. К сожалению, мои познания в UE не позволили воспроизвести фикс из этого треда один-в-один, просто сделал C++ Class типа «GameInstance» и указал его в свойствах проекта. MyGameInstance.h и MyGameInstance.cpp соответственно:

      #pragma once
      
      #include "CoreMinimal.h"
      #include "Engine/GameInstance.h"
      #include "MyGameInstance.generated.h"
      
      UCLASS()
      class SAMPLE_01_API UMyGameInstance : public UGameInstance
      {
      	GENERATED_BODY()
      	
      public:
      	virtual void LoadComplete(const float LoadTime, const FString& MapName) override;
      };
      

      
      #include "MyGameInstance.h"
      #include "Engine/World.h"
      #include "Engine/WorldComposition.h"
      #include "Engine/LevelStreaming.h"
      
      void UMyGameInstance::LoadComplete(const float LoadTime, const FString & MapName)
      {
      	Super::LoadComplete(LoadTime, MapName);
      	
      	UWorld* World = GetWorld();
      	
      	// WorldComposition won't work in Standalone mode without next statement
      	if ( World->WorldComposition ) {
      		const TArray<ULevelStreaming*> TilesStreaming = World->WorldComposition->TilesStreaming;
      		World->ClearStreamingLevels();
      		World->SetStreamingLevels(TilesStreaming);
      	}
      	
      	// "Pause" game till streaming complete
      	GEngine->BlockTillLevelStreamingCompleted(World);
      }
      


    На этом пока всё. В планах рассказать про создание стартового меню и миникарты в игре. Спасибо.

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 2

      0

      Очень нравится как ты описываешь, но мне как-то не хватает визуала. Интересно посмотреть на результат, к которому ты приходишь. Даже если ты сталкиваешься с какими-то сложностями, которые приводят к каким-то багам, мне бы хотелось увидеть баг, запомнить, как он выглядит, и если я лично с ним встречусь, вспомнить про твою статью и найти для себя в ней практическое решение.

        0
        Побольше картинок пожалуйста

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое