1) Oyuncu: Devlet makine + bileşen tabanlı mimari.
Player için olağan bileşenler: HealthSystem, MovementSystem, InventorySystem, ActionSystem. Bunların hepsi gibi sınıflar class HealthSystem
.
Update()
Orada kullanılmasını tavsiye etmiyorum (Her zamanki gibi bazı eylemler için ihtiyaç duymadığınız sürece, sağlık sistemindeki güncellemelerden bir anlam çıkarmazlar. Bunlar nadiren meydana gelir. Ayrıca düşünebileceğiniz bir durum - oyuncu zehirlenir ve ona ihtiyacınız olur. zaman zaman sağlık kaybediyor - burada coroutines kullanmayı öneriyorum.Başka bir sağlık veya çalışan gücü sürekli yenilerken, sadece şu anki sağlığı veya gücü alırsınız ve zaman geldiğinde o seviyeye kadar doldurmak için koroini çağırırsınız. Hasar gördü ya da tekrar koşmaya başladı .. Tamam, bu biraz kapalıydı ama umarım faydalı olmuştur) .
Durumlar: LootState, RunState, WalkState, AttackState, IDLEState.
Her devlet miras kalır interface IState
. IState
Örneğimizde sadece örnek olarak 4 yöntem var.Loot() Run() Walk() Attack()
Ayrıca, class InputController
her kullanıcının Girişini kontrol ettiğimiz bir yere sahibiz.
Şimdi gerçek örnek olarak: InputController
Oyuncunun herhangi bir tuşa basıp basmadığını WASD or arrows
ve sonra da o tuşuna basıp basmadığını kontrol ederiz Shift
. O sadece basılırsa WASD
o zaman diyoruz _currentPlayerState.Walk();
bu happends ve sahip olduğumuz currentPlayerState
eşit olacak şekilde WalkState
daha sonra WalkState.Walk()
biz tüm bileşenlerin bu devlet için gerekli olan - bu durumda MovementSystem
biz oyuncu hamle yapmak, böylece public void Walk() { _playerMovementSystem.Walk(); }
sen burada ne var bakın? - İkinci davranış katmanımız var ve bu kod sürdürme ve hata ayıklama için çok iyi.
Şimdi ikinci davaya: peki ya WASD
+ Shift
basarsak? Ama önceki durumumuz öyleydi WalkState
. Bu durumda Run()
haber verilecek InputController
(bu kadar karışmaz, Run()
biz var çünkü denir WASD
+ Shift
check InputController
değil çünkü WalkState
). Dediğimiz zaman _currentPlayerState.Run();
içinde WalkState
- biz anahtara sahip olduğunu biliyoruz _currentPlayerState
için RunState
ve biz de bunu Run()
bir WalkState
ve biz kaybetmek eyleme bu çerçeveyi istemiyoruz çünkü farklı bir devlet ile artık bu yöntem içinde tekrar diyoruz ama. Ve şimdi elbette ararız _playerMovementSystem.Run();
.
Ancak LootState
oyuncu düğmeyi bırakana kadar yürüyemiyor veya koşamıyorsa ne olacak? Eh, bu durumda yağmalamaya başladığımızda, örneğin düğmeye E
basıldığında çağrı _currentPlayerState.Loot();
yaparız LootState
ve şimdi oradan çağırırız. Orada örneğin, menzil içinde yağma yapacak bir şey varsa almak için toplama yöntemini çağırırız. Animasyonun olduğu ya da başlattığımız yer olan coroutine'i çağırıyoruz ve koroutinin kırılmadığı sürece, oynatıcının hala düğmeyi tutup tutmadığını kontrol ediyoruz. Ama ya oyuncu basarsa WASD
? - _currentPlayerState.Walk();
denir, ama işte devlet makineyle ilgili güzel şey,LootState.Walk()
Hiçbir şey yapmayan veya bir özellik olarak yaptığım gibi boş bir yöntemimiz var - oyuncular şöyle diyor: "Hey dostum, bunu henüz yağmalamadım, bekleyebilir misin?" Yağmalamayı bitirdiğinde, değişiyoruz IDLEState
.
Ayrıca, class BaseState : IState
tüm bu varsayılan yöntemler davranışının uygulandığı, ancak bunları sınıf türlerinde virtual
yapabilmeniz override
için çağrılan başka bir komut dosyası da yapabilirsiniz class LootState : BaseState
.
Bileşen tabanlı sistem harika, beni rahatsız eden tek şey, birçoğu Örnekler. Ve daha fazla hafıza alır ve çöp toplayıcı için çalışır. Örneğin, 1000 düşman örneğiniz varsa. Hepsinde 4 bileşen var. 1000 yerine 1000 nesne. Mb, birlik gameobject'in tüm bileşenlerini göz önünde bulundurursak, çok büyük bir şey değil (performans testleri yapmadım).
2) Kalıtım temelli mimari. Her ne kadar bileşenlerden tamamen kurtulamayacağımızı fark edeceksin - temiz ve çalışma koduna sahip olmak istiyorsak bu imkansız. Ayrıca, uygun durumlarda kullanılması şiddetle tavsiye edilen Tasarım Kalıplarını kullanmak istiyorsak (bunları fazla kullanmayın, buna aşırı lisans denir).
Bir oyunda çıkması gereken tüm özelliklere sahip bir Player sınıfımız olduğunu hayal edin. Sağlığı, manası veya enerjisi vardır, yeteneklerini taşıyabilir, çalıştırabilir ve kullanabilir, bir envantere sahip olabilir, eşya üretebilir, eşyaları yağmalayabilir, hatta bazı barikatlar veya taretler inşa edebilir.
Öncelikle, Envanter, Zanaat, Hareket, Yapı bileşenlerine dayalı olması gerektiğini söyleyeceğim, çünkü böyle AddItemToInventoryArray()
bir yönteme sahip olmak oyuncunun sorumluluğu değil - oyuncu PutItemToInventory()
daha önce tarif edilen yöntemi çağıracak bir yönteme sahip olsa da (2 kat - farklı katmanlara bağlı olarak bazı koşullar ekleyin).
Bina ile başka bir örnek. Oyuncu gibi bir şey arayabilir OpenBuildingWindow()
, ancak Building
geri kalan her şeyle ilgilenir ve kullanıcı belirli bir bina oluşturmaya karar verdiğinde, oyuncuya gerekli tüm bilgileri iletir Build(BuildingInfo someBuildingInfo)
ve oyuncu gerekli tüm animasyonlarla oluşturmaya başlar.
KATI - OOP prensipleri. S - tek sorumluluk: önceki örneklerde gördüklerimiz. Evet, tamam ama Miras nerede?
Burada: oyuncunun sağlığı ve diğer özellikleri başka bir işletme tarafından ele alınmalı mı? Bence değil. Sağlığı olmayan bir oyuncu olamaz, eğer bir tane varsa, kalıtımsal değiliz. Örneğin, elimizdeki IDamagable
, LivingEntity
, IGameActor
, GameActor
. IDamagable
Tabii ki TakeDamage()
.
class LivinEntity : IDamagable {
private float _health; // For fields that are the same between Instances I would use Flyweight Pattern.
public void TakeDamage() {
....
}
}
class GameActor : LivingEntity, IGameActor {
// Here goes state machine and other attached components needed.
}
class Player : GameActor {
// Inventory, Building, Crafting.... components.
}
Yani burada bileşenleri mirastan ayırmadım ama gördüğünüz gibi onları karıştırabiliriz. Ayrıca, örneğin farklı sistemlerimiz varsa ve gerekenden daha fazla kod yazmak istemiyorsak, örneğin Bina sistemi için bazı temel sınıflar oluşturabiliriz. Aslında, farklı tipte binalara da sahip olabiliriz ve aslında bu yapıyı temel alarak yapmanın iyi bir yolu yok!
OrganicBuilding : Building
, TechBuilding : Building
. Genel işlemler veya bina özellikleri için 2 bileşen oluşturup oraya iki kez kod yazmanıza gerek yoktur. Ve sonra onları farklı bir şekilde ekleyin, kalıtım gücünü ve daha sonra polimorfizm ve kapsülleme gücünü kullanabilirsiniz.
Arada bir şeyler kullanmanızı öneririm. Ve bileşenlerin fazla kullanılmaması.
Oyun Programlama Kalıpları hakkında bu kitabı okumanı şiddetle tavsiye ediyorum - WEB'de ücretsiz.