Son adım, tüm parçaları bir oyun yöneticisi aracılığıyla bir araya getirmektir. Oyun yöneticisi, oynanış objelerinin oyunculara atanmasını ve oyun döngüsünün akışını kontrol eder. Oyun yöneticisi şunları yapar:
Oyunculara tahta ve mini tahta gibi oynanış objeleri atar.
Oyun döngüsünün mantığını (örn. bir hamle gerçekleştiğinde ne olacağını) kontrol eder.
Bir oyuncunun ne zaman kazandığını ve oyunun nerede biteceğini belirler.
Hamle Türlerini Tanımla
Sıranın geldiği oyuncu, oyun tahtasında bir koordinat seçer. Bir koordinat seçildikten sonra iki farklı hamle türü vardır:
Saldırı: Belirtilen konumdaki piyonu yok etmeyi dene.
Açığa Çıkar: Belirtilen bir konumun belirli bir yarıçapı dahilindeki tüm piyonları göster.
DataTypes modülünde, hamle türlerini tanımlayan aşağıdaki enum’ı ekle:
using{/Verse.org/Simulation}
using{/Verse.org/Random}
using{/UnrealEngine.com/Temporary/SpatialMath}
DataTypes<public> := module:
...
move_type<public> := enum<open>:
Attack
Bu enum açık olduğundan gelecekte istediğin zaman daha fazla hamle türü ekleyebilirsin.
Oyun Yöneticisini Oluştur
Ardından game_manager.verse adında yeni bir Verse dosyası oluştur ve game_manager adında yeni bir Kreatif cihazı ekle. Bu, oyunun akışını kontrol etmek için oyun dünyasında yer alan tek bir obje olacaktır.
Oyuncu Başına Objeleri Tanımla
Her oyuncunun bir oyun tahtası ve bir mini tahtanın yanı sıra hangi karelere saldırdığını gösteren bir obje, bir hamlenin yapıldığını gösteren bir olay ve seçilen koordinatın değiştiğini gösteren bir olay da dahil olmak üzere kendisiyle ilişkilendirilmiş çeşitli objeleri vardır. per_player_objects adında yeni bir sınıf tanımla:
using { /Verse.org/Simulation }
per_player_objects<public> := class:
@editable
Board<public>:board
@editable
Miniboard<public>:miniboard
var AttackedTiles<public>:[]tile_coordinate = array{}
MoveEvent<public>:event(tuple(tile_coordinate, move_type)) = event(tuple(tile_coordinate, move_type)){}
CoordinateChangeEvent<public>:event(tile_coordinate) = event(tile_coordinate){}Oyun Yöneticisini Tanımla
Oyun yöneticisi sınıfının, oyuncuları objeleriyle ilişkilendirmesi gerekir. Verse’te bir oyuncuyu bir objeyle ilişkilendirmenin ideal yollarından biri weak_map kullanmaktır. Oyun yöneticisi sınıfına aşağıdaki alanları ekle:
using { /Verse.org/Simulation }
per_player_objects<public> := class:
@editable
Board<public>:board
@editable
Miniboard<public>:miniboard
var AttackedTiles<public>:[]tile_coordinate = array{}
MoveEvent<public>:event(tuple(tile_coordinate, move_type)) = event(tuple(tile_coordinate, move_type)){}
CoordinateChangeEvent<public>:event(tile_coordinate) = event(tile_coordinate){}
Oyuncu Objeleri Ata
Oyuncular oyuna katıldıkça her oyuncuya PerPlayerObjects’ten PerPlayerManagement objesine kadar oyuncu objeleri ata. İlk olarak oyundaki tüm oyuncuları al, ardından her bir oyuncuya oyuncu objeleri ata. Yeterli oyuncu objesi yoksa hatayı işle. Oyun yöneticine aşağıdaki AssignPlayerObjects fonksiyonunu ekle:
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
...
game_manager := class(creative_device):
AssignPlayerObjects():void =
for (Index -> Player : GetPlayspace().GetPlayers()):
if:
Kazanma Koşulunu Tanımla
Yapılacak bir sonraki şey, bir oyuncunun kazanma koşulunu ne zaman karşıladığını belirlemektir. Oyuncunun tahtada bulup yok edebileceği başka piyon yoksa oyuncu oyunu kazanmış demektir. Bu, doğrudan tahtadaki Pawns dizisinin uzunluğu sorgulanarak yapılır. Oyun yöneticisine aşağıdaki WinConditionMet fonksiyonunu ekle:
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
...
game_manager := class(creative_device):
WinConditionMet(Player:player)<decides><transacts>:void =
# Player wins if no pawns remain
Print("Pawns remaining: {PerPlayerManagement[Player].Board.Pawns.Length}")
Bu fonksiyon ancak ve ancak girdi oyuncusu için bulunacak ve imha edilecek başka piyon kalmadığında başarılı olur.
Saldırı Hamlesi
Artık oyun objelerini her oyuncuya nasıl atayacağını ve bir oyuncunun kazanıp kazanmadığını nasıl belirleyeceğini öğrendiğine göre, bir sonraki adım bir saldırı hamlesi sırasında ne olacağını tanımlamaktır. Bir oyuncu rakip karelerinden birine saldırdığında aşağıdaki adımlar gerçekleşir:
Tahta üzerinde, saldırı koordinatında bir piyon olup olmadığını belirle.
Var ise:
Piyonu oyuncunun tahtasından kaldır.
Rakibin mini tahtasında bir isabet işaretleyicisi ayarla.
Yok ise:
Rakibin mini tahtasında bir ıskalama işaretleyicisi ayarla.
game_manager sınıfına aşağıdaki tanıma sahip OnAttack adında bir fonksiyon ekle:
OnAttack(Instigator:player, Recipient:player, TileCoordinate:tile_coordinate):void =
if:
InstigatorObjects := PerPlayerManagement[Instigator]
RecipientObjects := PerPlayerManagement[Recipient]
then:
# Determine if the attack is a hit
var MarkerType:marker_type = marker_type.Miss
Print("Attack coordinate: Left: {TileCoordinate.Left}, Forward: {TileCoordinate.Forward}")
Açığa Çıkar Hamlesi
Diğer hamle türü açığa çıkarma hamlesidir. Bu, saldırı hamlesi üzerinde çalışmak için bazı ek ayarlar gerektirir. Öncelikle, UtilityFunctions modülüne üç yeni fonksiyon ekle:
operator'-': İkitile_coordinateobjesi için çıkarma ikili (binary) işlemini tanımla.Abs: Birtile_coordinate’ın bileşen bazlı mutlak değerini al.ManhattanDistance: İkitile_coordinateobjesi arasındaki Manhattan veya Taksi mesafesini al.
UtilityFunctions<public> := module:
using{DataTypes}
...
Abs(TileCoordinate:tile_coordinate)<transacts>:tile_coordinate =
tile_coordinate:
Left := Abs(TileCoordinate.Left)
Forward := Abs(TileCoordinate.Forward)
Manhattan Mesafesi, kare ızgarası üzerindeki ana yönler boyunca gezinerek iki tile_coordinate objesi arasındaki mesafeyi hesaplar. Daha fazla bilgi için https://en.wikipedia.org/wiki/Taxicab_geometry sayfasına bakabilirsin.
Artık işlevsellikler tanımlandığına göre, OnReveal fonksiyonunun davranışını tanımlayabilirsin. Bir oyuncu, rakibi için bir tile_coordinate’ın belirli bir yarıçapı içindeki piyonları açığa çıkarmayı seçtiğinde aşağıdaki adımlar gerçekleşir:
Oyuncunun tahtasında,
ManhattanDistance’a göre girdi koordinatından itibaren ayarlanmış birRevealDistancedahilindeki tüm piyonları bul.Bu mesafe dahilindeki her piyon için bir açığa çıkarma efekti oynat.
game_manager sınıfına aşağıdaki tanıma sahip OnReveal adında bir fonksiyon ekle:
OnReveal(Instigator:player, Recipient:player, TileCoordinate:tile_coordinate):void =
if:
InstigatorObjects := PerPlayerManagement[Instigator]
RecipientObjects := PerPlayerManagement[Recipient]
then:
for:
Pawn : InstigatorObjects.Board.Pawns
PawnTileCoordinate := InstigatorObjects.Board.GetTileCoordinate[Pawn]
ManhattanDistance(PawnTileCoordinate, TileCoordinate) < RevealDistance
do:
Oyuncu Sırası
Ardından, bir oyuncunun sırasının nasıl göründüğünü belirle. Sıra bir oyuncuya geldiğinde, sinyali verilebilecek iki farklı olaydan birini bekle: MoveEvent veya CoordinateChangeEvent. Bu olaylardan birinin sinyali verildiğinde diğer olayı bırak ve bu olayları bir race koşulu içine yan yana koy. Bir koordinat değişikliğinin sinyali verildiğinde aynı oyuncu bir hamle türü seçene kadar oynamaya devam etmelidir. Dolayısıyla bir sonraki oyuncuya, yalnızca saldırı veya açığa çıkarma seçildiğinde geç.
game_manager sınıfına aşağıdaki tanıma sahip OnTurn fonksiyonunu ekle:
OnTurn(Player:player, Opponent:player)<suspends>:void =
if (PlayerObjects := PerPlayerManagement[Player]):
loop:
var Continue:logic = false
race:
block:
# Listens for a call to PerPlayerManager[Player].CoordinateChangeEvent.Signal(:tile_coordinate)
TileCoordinate := PlayerObjects.CoordinateChangeEvent.Await()
block:
# Listens for a call to PerPlayerManager[Player].MoveEvent.Signal(:tile_coordinate,:move_type)
Oyun Döngüsünü Tanımla
Oyuncu sırası artık tanımlandığına göre sonunda birincil oyun döngüsünü oluşturabilirsin. Oyun döngüsü içinde aşağıdaki adımlar gerçekleşir:
Tüm oyuncuları al.
Bir oyuncuyu sıranın kendisinde olması, diğerini ise birinci oyuncunun hamle yapmasını beklemesi için ata.
Oyunculardan biri kazanana kadar döngüyü sürdür (döngüde her biri dönüşümlü olarak oynar).
Bunu yapmak için game_manager sınıfına aşağıdaki GameLoop fonksiyonunu ekle:
GameLoop()<suspends>:void =
Players := GetPlayspace().GetPlayers()
if :
Players.Length = 2
var TurnPlayer:player = Players[0]
var OtherPlayer:player = Players[1]
then:
loop:
OnTurn(TurnPlayer, OtherPlayer)
if (WinConditionMet[TurnPlayer]):
Oynanış Başlangıcı
Yapılacak son şey, her oyuncuya oyuncu objeleri atamak ve oyun döngüsünü başlatmaktır. OnBegin fonksiyonuna AssignPlayerObjects ve GameLoop için yapılacak çağrılar ekleyerek bunu otomatikleştir:
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
AssignPlayerObjects()
GameLoop()Özet
Özetlemek gerekirse, bu sayfada aşağıdaki adımlar açıklanmıştır:
Oyun hamlelerini tanımla.
Oyun döngüsünü oluştur.
Bir kazanma koşulunun ne zaman karşılandığını belirle.
Bu deneyimi benzersiz bir şekilde kendine ait kılmak için hâlâ yapabileceğin birçok şey var. Aşağıdaki fikirlerden yola çıkabilirsin:
Bir kullanıcı arayüzü tasarlamak ve uygulamak.
Oyuncu hamlelerinin sinyalinin, oyun yöneticisine ne zaman ve nasıl verildiğini ayarlamak.
Oyun dünyasını ve ortamını tasarlamak.
Saldırılar ve açığa çıkarmalar için efektler oluşturmak.
Müzik tasarımı ve set dekoru eklemek.
Bu ana oynanış sınıflarını temel alarak yeni şeyler oluşturmaktan, onların parçalarını kullanmaktan ve her şeyi kendine özel kılmayı denemekten çekinme.
Dosyalar
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { DataTypes }
using { UtilityFunctions }
per_player_objects<public> := class:
@editable
Board<public>:board
@editable