Verse - Prosedürel İnşa şablonu, Verse ile yazılan ve Prosedürel İnşa sisteminin özelliklerini sergileyen Fortnite İçin Unreal Editor (UEFN) Re: Imagine London (Londra’yı Yeniden Hayal Et) (Ada Kodu: 1442-4257-4418) adasından örnek bir projedir.
Prosedürel İnşa sistemi Fortnite yerine tamamen Verse’te uygulanmış ve oluşturulmuştur. Bu sistemle, projelerinde örgülerin nasıl bir araya getirileceği üzerinde daha fazla kontrole sahip olabilirsin.
Fortnite aracılığıyla oyununu önceden belirlenmiş bina parçaları (zemin, tavan ve duvar) ile tasarlayabilirsin. Prosedürel İnşa sistemi, olanaklarını genişletir, böylece sistem, örgüsünü yerleştirdikçe hızlı bir şekilde ek vokseller yerleştirebileceğin temel sistemleri belirleyen kategorilerle vokseller oluşturabilirsin.
Bu şablona UEFN Proje Tarayıcısı’ndan erişmek için Özellik Örnekleri > Oyun Örnekleri > Verse - Prosedürel İnşa yolunu izle.
Prosedürel İnşa sistemi iki aşamada çalışır.
İlk aşamada, voksel ızgarasını, yani her hücrenin bir kutu olduğu 3D bir ızgarayı voksel ekleyerek veya kaldırarak düzenlersin.
Voksel ızgarası her değiştirildiğinde ikinci aşama devreye girer. Voksel ızgarası değiştirildiğinde voksel bilgisi alınır ve doğru örgüleri doğru yerlerde oluşturan bir işlem çalıştırılır.
Hem Bina hem de Park voksel kategorilerini ekleyip kaldırabilirsin. Her kategoride, örgülerin nasıl oluşturulacağına ilişkin farklı kurallar yer alır. Bu kurallar, yerleştirilmiş voksellerden gerçek örgülere gitmek için gerçekleştirilecek işlem grafiğini veya ağacını ifade eder.
Şablon Kullanma
Projendeki her bir bölümde root_device sınıfı adlı bir üst seviye sınıf bulunacak. Bir oyuncu oyuna katıldığında root_device sınıfı, oyuncu için kullanıcı arayüzü bilgilerini ayarlayan bir global_player_data oluşturur.
Her inşa bölgesinde, alanın boyutlarını voksel birimleri cinsinden tanımlayan bir build_zone cihazı bulunur. Bu cihazın konumu, alanın başlangıç noktasını tanımlar. İnşa alanı, inşa mekaniklerini işlemek için bir build_system objesi ve bina nesnesi örgüleri oluşturmak için bir spawner_device kullanır. İnşa alanı ayrıca voxel_grid, mesh_grid ve wfc_system içerir.
Her inşa alanında şunlar olacaktır:
build_zone(cihaz) - alanın boyutlarını voksel birimlerinde tanımlar.build_system- inşa mekaniklerini yönetir.spawner_device(cihaz) - bina nesnesi örgülerini oluşturur.spawner_asset_references(cihaz) - oluşturulan tüm nesnelere referans verir.
Oyuncu, bir Etki Aktörü cihazı tarafından tetiklenen bir build_zone bölgesine girdiğinde, bu oyuncunun girdisini ve voksel düzenlemesini işlemek için player_data oluşturulur.
Voksellerle İnşa Etme
Bu şablon, vokselleri X, Y ve Z [integer]() koordinatlarıyla temsil eden bir vector3i [type]() ifadesi sunar.
Aşağıda vector3i ifadesinin kullanıldığı örnek bir kod yer alıyor.
vector3i<public> := struct<computes><concrete>:
@editable
X<public>:int = 0
@editable
Y<public>:int = 0
@editable
Z<public>:int = 0Voksel Izgaraları
Voksel ızgarası, her inşa alanı için mevcut bina vokselinin türü hakkında bilgi depolayan 3D bir hücre ızgarasıdır. Bu, aşağıda gösterildiği gibi voxel_grid sınıfında voxel_cell [objects]() isteğe bağlı referanslarının 1D bir dizisi olarak uygulanır.
# Main array representing the 3D grid of voxels
var Cells<public> : []?voxel_cell = array{}Bir voksel hücresi objesi, aşağıda gösterildiği gibi varsayılan olarak, voksel türü için yalnızca bir [enum]() içerir.
# Category of voxel in our build grid
build_category<public> := enum:
Building
Park
NewBuildingType
# Structure stored for each occupied voxel
voxel_cell<public> := struct<computes>:
Category<public>:build_categoryEnum’ı yukarıda gösterildiği gibi genişleterek daha fazla bina voksel kategorisi ekleyebilirsin.
Aşağıdaki kod, 3D bir voksel koordinatını 1D bir [index]()’e dönüştürür.
# Gets the 1D array index from a 3D location in grid
GetVoxelIndex<public>(X:int, Y:int, Z:int)<transacts>:int=
return (X * (Size.Y*Size.Z)) + (Y * Size.Z) + ZSetVoxel ve ClearVoxel, voksel ızgarasını değiştiren temel fonksiyonlardır.
Işın dökümü
Voksel ızgarası ayarlandıktan sonra sistem, voksel yüzünü veya ışının çarptığı voksel tarafını kontrol etmek için bir ışın çarpışma kontrolü gerçekleştirir. Her vokselin, oyun zarı gibi altı yüzü vardır. Işın dökümü sırasında vurguyu çizmek ve o yüze karşı yeni bir voksel oluşturmak için hem hangi voksele hem de hangi yüze çarptığını bilmen gerekir.
Işın çarpışması kontrolleri çoğunlukla ray_caster sınıfında gerçekleştirilir. Bu işlem, ilk olarak kamera konumu ızgaranın yerel alanına dönüştürülüp ışının hangi vokselde başladığı belirlenerek yapılır. Kamera konumu, daha sonra aşağıda gösterildiği gibi voksel boyutlarına bölünür.
CurrentVoxel := vector3i:
X := Floor[InitialPosition.X / GridSize.X]
Y := Floor[InitialPosition.Y / GridSize.Y]
Z := Floor[InitialPosition.Z / GridSize.Z]
Işının bundan sonra hangi vokselden geçeceğini belirlemek için [Next]() fonksiyonu, her defasında vokselin katı olarak kabul edilip edilmediği kontrol edilerek tekrar tekrar çağrılır.
Bina Sistemi Girdileri
player_data içindeki SelectModeTick_Trace fonksiyonu her kare için çalışır ve voksel düzenlemenin ve imleç güncelleme mantığının büyük bölümünü yönetir. Ateş ve Nişan Al butonlarına ne zaman basılacağını bilmek ve PlacePiece ve DeletePiece mantık değişkenlerini ayarlamak için iki [Input Trigger]() cihazı kullanılır.
Park vokselleri yalnızca yüzey olarak kabul edildiğinden, yani ışınları engellemediği ve yalnızca katı voksellerin (zemin veya binalar) üzerinde var olabileceğinden, bu fonksiyon için ek mantık gerekir. CategoryIsSurfaceOnly fonksiyonunu yalnızca yüzeyde olmasını istediğin yeni bir kategori eklerken güncelleyebilirsin.
Ayrıca, Ateş butonu basılı tutularak birden fazla vokselin bir düzleme hızlı bir şekilde yerleştirileceği Turbo inşa da desteklenir. Bu fonksiyon ayrıca bir oyuncu CheckPlayerOverlapsVoxel fonksiyonuna eklenmeden önce oyuncunun bir voksel içinde olup olmadığını da kontrol eder.
Nesne Üretme
Bu örnek proje, [runtime]()’da nesne üretmeye dayanır. creative_prop_asset, şu anda Verse manifesto dosyalarına otomatik olarak yansıtılmaz. Bu nedenle Verse’te belirli nesnelere referans vermek için bir proxy obje (piece_type_dir sınıfında bir piece_type örneği) kullanman gerekir.
Bu durumda, spawner_asset_references cihazı her nesne için bir @editable alanı ve bir proxy’den asıl öğeye eşleştirme tablosu kullanır. Yeni bir örgü eklemek için önce onun için bir BuildingProp oluşturman, yeni bir proxy eklemen, cihaza bir özellik eklemen, ardından eşleştirme tablosunu güncellemen gerekir (aşağıda gösterildiği gibi). Son olarak, cihazda yeni özelliği yeni nesneyi işaret edecek şekilde yeniden derleyip güncellemelisin.
Building1_corner:piece_type := piece_type{}
@editable
BP_Building1_corner : creative_prop_asset = DefaultCreativePropAsset
PT.Building1_corner => BP_Building1_corner
Verse’te Prosedürel Oluşturma
Bu örnek, Verse’te iki tür prosedürel oluşturma uygular: Şekil Grameri ve Dalga Fonksiyonu Daraltma. 3D binalara Şekil Grameri uygulanırken, 2D (düz) alanlar için Dalga Fonksiyonu Daraltma kullanılır.
Yukarıda, oluşturulan bina nesnelerinin bir örneği yer almaktadır.
Yukarıda oluşturulan Park nesnelerinin bir örneği yer almaktadır.
Her iki teknik için de bir dizi modüler bina türü nesne seti oluşturman gerekir. Bu kod belirlenimcidir; nesneleri yalnızca gerektiğinde siler ve gerektiğinde üretir.
Şekil Grameri
Bir kategorideki tüm vokseller, Şekil Grameri denilen şeyi uygulamak için daha büyük dışbükey kutulara dönüştürülür.
Şekil Grameri, her kuralın bir kutu aldığı ve sonraki kurallar için bir veya daha fazla alt kutu oluşturduğu basit kurallardan oluşur.
Örneğin, bir kural uzun bir kutuyu tek voksellik bir yüksek zemin parçasına dilimlerken köşeler ve duvarlar farklı kurallara atanmış olabilir. Özel bir kural, kutuyla aynı boyut ve konumda bir nesne oluşturabilir.
Her kural, vo_base (etki aktörü işleci) sınıfından türetilen ayrı bir Verse sınıfı olarak tanımlanır. Bunlar, rs_base (kural seti) sınıfından türetilmiş bir sınıf içinde bir kural seti ağacında birleştirilir.
Bu yaklaşım, yeni kurallar oluşturmayı basitleştirir, farklı fikirlerle denemelere olanak tanır, her bina türüne farklı tarzlar atar ve her bina türüne farklı tarzlar atamaya izin verir. Aynı voksel kümesine farklı kurallar uygulamak farklı sonuçlara yol açar.
Aşağıda ` vo_sizecheck` etki aktörü işlecinin basit bir örneğini görebilirsin.
# Check all dimensions of a box are >= a certain size
vo_sizecheck := class(vo_base):
Size:vector3i = vector3i{X:=0, Y:=0, Z:=0}
VO_Pass:vo_base = vo_base{}
VO_Fail:vo_base = vo_base{}
Operate<override>(Box:voxel_box, Rot:prop_yaw, BuildSystem:build_system):void=
if(Box.Size.X >= Size.X and Box.Size.Y >= Size.Y and Box.Size.Z >= Size.Z):
VO_Pass.Operate(Box, Rot, BuildSystem)
else:
VO_Fail.Operate(Box, Rot, BuildSystem)Temel sınıfta tanımlanan Operate fonksiyonunu geçersiz kılar, verilen kutunun boyutunu kontrol eder ve aşağıdaki kurallardan hangisinin (vo_pass veya vo_fail) çağrılacağına karar verir.
Çok sayıda etki aktörü işleci içeren kural setleri Verse’te kolaylıkla ayarlanabilir. setupRules fonksiyonunu geçersiz kılabilir ve kendi [operators]() fonksiyonunu ve bunların [parameters]() değerlerini bildirebilirsin. Başlangıç noktası veya kök işleç, aşağıda gösterildiği gibi VO_root fonksiyonuna atanır.
# Rules for 'Building1' style
rs_building1<public> := class(rs_base):
RuleSetName<override>:string = "Building1"
SetupRules<override>(PT:piece_type_dir):void=
[...]
# Rules for one floor of the building
FloorRules := vo_cornerwallsplit:
VO_CornerLength1 := vo_corner:
VO_Corner := vo_prop:
Prop := PT.Building1_corner
Bu metot, farklı bina kategorileri için yeni işleçler ve kural setleri oluşturmayı kolaylaştırır. Kural setleri InitRuleSets içinde ayrılır ve SelectRuleSet içindeki belirli bir kategori için seçilir; bunların her ikisi de build_system dizisinde bulunur.
Dalga Fonksiyonu Daraltma
Dalga Fonksiyonu Daraltma (Wave Function Collapse, WFC), parçaların birbirine nasıl sığacağını belirleyen kurallara dayalı olarak bir alanı rasgele bir şekilde oluşturmak için bir tekniktir.
Bu uygulamada bir kare seti kullanabilir ve ardından hangisinin bitişik olabileceğini belirleyebilirsin. Her kenara etiketler uygulanır ve yalnızca eşleşen etiketlere sahip kareler yerleştirilebilir.
Aşağıda wfc_mode_factory sınıfından bir kenar etiketi örneği verilmiştir.
WaterEL:wfc_edge_label := wfc_edge_label:
Name:="Water"
Symmetric := true
Aşağıda, yukarıdaki örnek için örgü tanımının bir örneği veriliyor.
Park_Grass_Water_InCorn:wfc_mesh := wfc_mesh:
Props := array:
PT.Park_Grass_Water_Incorn
Name := "Park_Grass_Water_Incorn"
Edges := array:
WaterEL
WaterEL
WaterToGrassEL
GrassToWaterEL
Etiketleri, +Y yönünden başlayıp saat yönünde belirtebilirsin.
Bu örnekteki alt kenar "sudan çime" şeklindedir çünkü her kenar saat yönünde dikkate alınır. Bu sistemle oynanışına yeni etiketler ve örgüler veya yeni WFK modelleri kolayca eklenebilir.
WFC algoritması, ızgara üzerinde bir konum seçer, olası seçeneklerden rastgele seçim yapar (veya “daraltır”), ardından bu seçimin sonuçlarını diğer konumlardaki olası seçeneklere yayar. Bu işlem tüm bölge oluşturulana kadar devam eder.
wfc_system sınıfı, tüm karelerin geçerli durumunu içerir. vo_wfc özel etki aktörü işleci, durumu okur ve doğru örgüleri oluşturur.
Olası Genişletmeler
Aşağıda bu örnek projeyi yeni deneyimlere dönüştürmek için kullanabileceğin birkaç yol yer alıyor.
Yeni bir voksel kategorisi/Şekil Grameri ekle
build_categoryenum’ını genişletYeni kategoriyi işlemek için gereken
caseifadelerini güncellers_basesınıfından türetilen yeni bir kural seti oluşturSelectRuleSetdizisini yeni kategori için yeni kural setini kullanacak şekilde güncelle
Park WSC modeline yeni örgüler ekle
Her yeni örgü için bir Bina Nesnesi oluştur (yukarıdaki Nesne Üretme bölümünde açıklandığı gibi).
wfc_model_factoryiçindekiGetParkModeldeğerine yeni birwfc_edge_label(isteniyorsa) ekle.Her yeni örgü/nesne için yeni bir
wfc_meshörneği ekle ve her kenarın etiketini tanımla.Her yeni örgü için
Model.AddMeshçağrısı yap.
Yeni bir WPC modeli ekle
Yeni model için yeni bir voksel kategorisi ekle.
Yeni modeli oluşturmak için
wfc_model_factorylistesine yeni bir fonksiyon ekle.build_system’a yeni birwfc_modelüyesi ekle (Park_WFCModelgibi).Yeni modeli kullanarak kareler ekleyen
AddParkWFCTilegibi yeni bir fonksiyon ekle.Yeni kategorideki yeni fonksiyonu çağırmak üzere
SelectModeTick_Trace’i değiştir.
Vokselleri prosedürel olarak da üretebilirsin. Örnek projede inşa alanına rastgele bina veya park bölgeleri ekleyen düğmeler bulunuyor. Bu sistem, build_system’in ClearRegiClearRegion ve AddRegion fonksiyonlarını kullanır ve bir rastgele bölüm oluşturma sistemi için başlangıç noktası olarak kullanılabilir.
Verse Performansı
Bu örnek projedeki Verse kodu, kodun güncellemeleri gerçek zamanlı olarak işleyecek kadar hızlı olmasını sağlar. Büyük dizilerle uğraşmak performans sorunlarına yol açabileceğinden aşağıdaki bilgileri aklında bulundur:
Bir diziyi döndürmek için
fordöngüsü kullanmak, onu öğe öğe oluşturmaktan daha hızlıdır çünkü her ekleme, diziyi O(N2) olacak şekilde kopyalar. Örneğin:Verse* set OptionalArray := for(I := 0 .. ArraySize-1): falseBüyük dizileri değerle iletme, bunun yerine bunları bir objeye yerleştirip metotları çağır ya da objeyi ilet.
Çok boyutlu diziler yavaştır çünkü ilk
[]işleci bir kopyayı sonraki[]işlecine iletir.Bir dizide
.Lengthçağrısı yapmak o anda dizinin bir kopyasını oluşturur, bu nedenle büyük dizilerin boyutunu kendin takip etmen daha hızlı olabilir.
Kodun tam olarak hangi bölümlerinin fazla zaman aldığını daha iyi anlamak için profile makrosunu kullanmak da çok faydalıdır.