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şama için bir 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 ekleyebilir ve kaldırabilirsin. Her kategori, örgülerin nasıl oluşturulacağına ilişkin farklı kurallara sahiptir. 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.
Şablonu Kullanma
Projendeki her bir bölümde root_device class
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ı vardır. 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 bir voxel_grid
, bir mesh_grid
ve bir wfc_system
içerir.
Her inşa alanında şunlar olacaktır:
-
build_zone
(device) - alanın boyutlarını voksel birimlerinde tanımlar. -
build_system
- inşa mekaniklerini yönetir. -
spawner_device
(device) - bina nesnesi örgülerini oluşturur. -
spawner_asset_references
(cihaz) - oluşturulan tüm nesnelere referans verir.
Bir oyuncu, bir Etki Aktörü cihazı tarafından tetiklenen bir build_zone
’a girdiğinde, oyuncunun girdilerini 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.
# `int` bileşenleriyle 3 boyutlu vektör
vector3i<public> := struct<computes><concrete>:
@editable
X<public>:int = 0
@editable
Y<public>:int = 0
@editable
Z<public>:int = 0
Voksel 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.
# 3D voksel ızgarasını temsil eden ana dizi
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.
# İnşa ızgaramızdaki voksel kategorisi
build_category<public> := enum:
Building
Park
NewBuildingType
# Her dolu voksel için depolanan yapı
voxel_cell<public> := struct<computes>:
Category<public>:build_category
Enum’ı 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.
# Izgara içindeki bir 3D konumdan 1D dizi dizinini alır
GetVoxelIndex<public>(X:int, Y:int, Z:int)<transacts>:int=
return (X * (Size.Y*Size.Z)) + (Y * Size.Z) + Z
SetVoxel
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 düğmelerine 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 ek mantık gerektirir. CategoryIsSurfaceOnly
işlevini yalnızca yüzeyde olmasını istediğin yeni bir kategori eklerken güncelleyebilirsin.
Ayrıca, Ateş düğmesi 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 Şekil Grameri ve Dalga Fonksiyonu Daraltması olmak üzere iki tür prosedürel oluşturma uygular. 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 alıyor.

Yukarıda, oluşturulan park nesnelerinin bir örneği yer alıyor.
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.
# Bir kutunun tüm boyutlarının belirli bir boyuttan büyük veya ona eşit (>= ) olduğunu kontrol et.
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_pas
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
işlevine atanır.
# ’Building1’ tarzı için kurallar
rs_building1<public> := class(rs_base):
RuleSetName<override>:string = "Building1"
SetupRules<override>(PT:piece_type_dir):void=
[...]
# Binanın bir katı için kurallar
FloorRules := vo_cornerwallsplit:
VO_CornerLength1 := vo_corner:
VO_Corner := vo_prop:
Prop := PT.Building1_corner
VO_Face := vo_prop:
Prop := PT.Building1_face
VO_Covered := vo_prop:
Prop := PT.Building1_box
VO_Wall := vo_wall:
VO_Width1 := vo_facecoveragecheck_1voxel:
VO_Clear := vo_prop:
Prop := PT.Building1_face
VO_Covered := vo_prop:
Prop := PT.Building1_box
VO_Centre := RoofTileCheck
VO_TooSmall := vo_tilefill:
VO_Tile := FourWay
set VO_Root = vo_floorsplit:
VO_FloorHeight1 := FloorRules
Bu metot, farklı bina kategorileri için yeni işleçler ve kural setleri oluşturmayı kolaylaştırır. Kural kümeleri, 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 veriliyor.
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_category
enum’ını genişlet -
Yeni kategoriyi işlemek için gereken
case
ifadelerini güncelle -
rs_base
sınıfından türetilen yeni bir kural seti oluştur -
SelectRuleSet
dizisini 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_factory
içindekiGetParkModel
’a 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_factory
listesine yeni bir fonksiyon ekle. -
build_system
’a yeni birwfc_model
üyesi ekle (Park_WCCModel
gibi). -
Yeni modeli kullanarak kareler ekleyen
AddParkWFCTile
gibi yeni bir fonksiyon ekle. -
Yeni kategorideki yeni fonksiyonu çağırmak üzere
SelectModeTick_Trace
’i değiştir.
(w:600)
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
’ın ClearRegion
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
for
döngüsü kullanmak, onu öğe öğe oluşturmaktan daha hızlıdır çünkü her ekleme, diziyi O(N2) olacak şekilde kopyalar. Örneğin:
* set OptionalArray := for(I := 0 .. ArraySize-1):
false
-
Bü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.