Paket persatuan yang mewakili C# reaktif Perpustakaan dan set alat untuk pengembangan persatuan.
Toolset ini terdiri dari beberapa bagian:
ZerGrush.reaktif adalah perpustakaan reaktif yang dirancang untuk menjadi alternatif yang lebih baik untuk UNIRX. Tujuan utama adalah untuk mengelola ketergantungan logika yang kompleks dari keadaan game/UI dan segala macam "pengamat negara". Terdiri dari bagian -bagian berikut:
ZerGrush.codegen adalah alat pembuatan kode yang memungkinkan untuk menambahkan berbagai fungsi yang sangat besar untuk model data Anda, misalnya dapat menambahkan:
ZerGrush.utils Koleksi alat variuos untuk tugas dan ekstensi gamedev umum untuk perpustakaan di atas.
Untuk saat ini proses instalasi adalah sebagai berikut:
Gunakan Paket Manajer> + (tombol)> Tambahkan paket dari URL git ...
Jika Anda tidak memiliki newtonsoft.json-for-unity di proyek Anda terlebih dahulu instal ini:
https://github.com/jilleJr/Newtonsoft.Json-for-Unity.git#upm
Kemudian instal paket ini dengan URL:
https://github.com/CeleriedAway/ZergRush.git
Bagian pertama dari ZerGrush.reaktif adalah EventStream
public interface IEventStream < out T > : IEventStream
{
IDisposable Subscribe ( Action < T > callback ) ;
}
Seperti yang Anda lihat itu mewakili acara sederhana yang dapat Anda berlangganan untuk mendapatkan pembaruannya. Biasanya tindakan Anda tidak akan dipanggil selama proses subribti. Acara implementasi utama memungkinkan acara "mengirim ()".
Metode Berlangganan Mengembalikan objek "koneksi" idisposable. Jika dibuang maka koneksi ini tidak dihitung lagi dan panggilan balik awal tidak akan dipanggil setelah itu. Salah satu usecase terbaik untuk ini adalah pengumpulan objek koneksi untuk disposi secara otomatis, jadi Anda tidak akan lupa untuk "berhenti berlangganan"
class DogModel
{
EventStream injured ;
EventStream < string > spokeSomething ;
}
class MyView : ConnectableMonoBehaviour
{
void Start ( )
{
// those connections is just a list of IDisposables that will be auto-disposed in OnDestroy callback
connections += dogModel . injured . Subscribe ( ShowBloodSpashes ) ;
// Subscribe extension allowing pass connections as first argument
dogModel . spokeSomething . Subscribe ( connections , ShowTextBubble ) ;
}
void ShowBloodSpashes ( ) { .. . }
void ShowTetBubble ( string text ) { .. . }
}
Eventstreams dapat disaring, diubah, dan digabungkan. Seperti ini..
// The only real stream allowing to send values
EventStream < int > streamOfNumbers = new EventStream < int > ( ) ;
// Subscribe to this stream and you will receive only event number events sent with streamOfNumbers.
IEventStream < int > streamOfEvenNumbers = streamOfNumbers . Filter ( i => IsEven ( i ) ) ;
// Same with odd numbers.
IEventStream < int > streamOfOddNumbers = streamOfNumbers . Filter ( i => IsOdd ( i ) ) ;
// Here you will receive strings created from numbers
IEventStream < string > streamOfSomeStrings = streamOfEvenNumbers . Map ( i => i . ToString ( ) ) ;
// Here you will receive events from both streams
IEventStream < int > mergedStreamOfNumbers = streamOfEvenNumbers . MergeWith ( streamOfOddNumbers ) ;
Tapi bagian paling berguna dari perpustakaan dalam pengembangan game adalah sel
public interface ICell < out T >
{
IDisposable ListenUpdates ( Action < T > reaction ) ;
T value { get ; }
}
API Dasar untuk Sel adalah:
var moneyCount = new Cell < int > ( ) ;
moneyCount . ListenUpdates ( v => Debug . Log ( $" Money changed to { v } " ) ) ;
moneyCount . value = 10 ;
// It is important that all cell api guerantee that setting same value won't trigger update callback
moneyCount . value = 10 ; // Log won't be trigered
var imRich = moneyCount . Select ( m => m > 100 ) ;
Ini mewakili nilai yang berubah dalam waktu. Dan sangat cocok untuk mewakili data game.
class Item
{
public Cell < int > hpBonus ;
public void Upgrade ( ) => hpBonus . value ++ ;
}
class Weapon
{
public Cell < int > damage ;
}
partial class PlayerData
{
public Cell < int > money ;
public Cell < int > hp ;
public Cell < int > baseMaxHp ;
public void LevelUp ( )
{
baseMaxHp . value ++ ;
hp . value = maxHpTotal . value ;
}
public ReactiveCollection < Item > items ;
public Cell < Weapon > selectedWeapon ;
public void EquipWeapon ( Weapon w ) => selectedWeapon . value = w ;
// Join is the most important operator that transforms ICell<ICell<T>> to just ICell<T>
// That is the core mechanic of cell transformation and
// the one that allows to collapse all dependency layers into one
public ICell < int > damage => selectedWeapon . Map ( w => w . damage ) . Join ( ) ;
// you can use transform api to compose new properties without loosing of its dependancies
// Look how total maxHp is calculated
public ICell < int > maxHpFromItems => items . Map ( i => i . hpBonus ) . ToCellOfCollection ( ) . Map ( itemBuffs => itemBuffs . Sum ( ) ) ;
public ICell < int > maxHpTotal => baseMaxHp . Merge ( maxHpFromItems , ( hp1 , hp2 ) => hp1 + hp2 ) ;
public ICell < float > relativeHp => hp . Merge ( maxHpTotal , ( hp , maxHp ) => hp / ( float ) maxHp ) ;
public ICell < bool > isWounded => relativeHp . Select ( value => value < 0.5f ) ;
}
Saya harap kode ini cukup jelas
/*
* To generate code press Shift + Alt + C in unity, or "Code Gen" > "Run CodeGen" from menu
* Some time it is difficult to refactor code because of other generated code
* And new code can be generated only if program is fully compiled, that is IMPORTANT!!!
* Use "Code Gen" > "Generate Stubs" or Shift + Alt + S to generate stub code before or during your refactor
* And when you program is compilable generate code normal way again
*
* Versioning is not supported for BinarySerialization by now
* Json serialization is not very sensitive for versions
*
* Code generation starts with defining tag with task enum value describing which functionality we want to generate
* The simplest one is the following...
*/
[ GenTask (
GenTaskFlags . Serialization | // Fast binary serialize/deserialize methods
GenTaskFlags . JsonSerialization | // Json serialize/deserialize methods
GenTaskFlags . Hash | // Fast hash code calculation
GenTaskFlags . UpdateFrom | // Deep copy optimized for copying into other created similar model
GenTaskFlags . CompareChech | // Function that prints all differences between two models into error log
GenTaskFlags . DefaultConstructor | // Generate Constructor that constructs all class type fields with defaults
GenTaskFlags . PolymorphicConstruction // Allows to save ancestor as base class values as fields or in containers
) ]
// All generated code will be placed into "x_generated" folder
[ GenInLocalFolder ]
public partial class CodeGenSamples : ISerializable
{
// All fields are automatically included
int intField ;
// All properties are not included by default
string stringPropWithoutTagNotIncluded { get ; set ; }
// You need to specify which properties to include with GenInclude tag
[ GenInclude ] string stringProp { get ; set ; }
// You can ignore some fields with GenIgnoreTag
[ GenIgnore ] int someTempIgnoredField ;
// all ref type fields considered not null by default, if null expect exception during generated function calls
string stringFieldMustNotBeNull ;
// Use CanBeNull tag for fields that can be null so code for this case will be generated
[ CanBeNull ] string stringFieldThatCanBeNull ;
// Extension methods for external classes used in generated classes will be generated.
// But extension methods can't access private members, so be careful with that
ExternalClass externalClass ;
Vector3 vector ;
// Other generated objects can be included
[ CanBeNull ] OtherData otherData ;
// You can ignore specific parts of code generation, for example if you do not want default construction of this field
[ GenIgnore ( GenTaskFlags . DefaultConstructor ) ]
OtherData otherData2 ;
List < int > listsOfPrimitivesAreOk ;
List < OtherData > listsOfDataAreOk ;
int [ ] arraysAreOk ;
// Dictionaries are supported but not for deep copy (UpdateFrom) for now...
[ GenIgnore ( GenTaskFlags . UpdateFrom ) ] Dictionary < int , OtherData > dictsAreOk ;
[ GenIgnore ( GenTaskFlags . UpdateFrom ) ] Dictionary < int , List < List < string > > > complexStructuresAreAlsoOk ;
// NOT SUPPORTED
[ GenIgnore ] int [ , ] multyDimArraysAreNotSupported ;
// ZergRush.Reactive primitives are supported
Cell < OtherData > reactiveValue ;
ReactiveCollection < int > reactiveCollections ;
[ GenIgnore ( GenTaskFlags . DefaultConstructor ) ]
public List < CodeGenSamples > ancestorArray = new List < CodeGenSamples >
{
// because of PolymorphicConstruction, Ancestor class will be serialized in right way
new Ancestor ( )
} ;
static void HowToUse ( )
{
var data = new CodeGenSamples ( ) ;
// json serialize
string jsonData = data . SaveToJsonString ( ) ;
// binary serialize
byte [ ] binaryData = data . SaveToBinary ( ) ;
// json deserialize
data = jsonData . LoadFromJsonString < CodeGenSamples > ( ) ;
// binary deserialize
var data2 = binaryData . LoadFromBinary < CodeGenSamples > ( ) ;
// deep copy data2 into data
data . UpdateFrom ( data2 ) ;
// compare data hashes
if ( data . CalculateHash ( ) != data2 . CalculateHash ( ) )
{
// and check for differences if hashes are not equal
data . CompareCheck ( data2 , new Stack < string > ( ) ) ;
}
// polymorphic construction example
var ancestor = CreatePolymorphic ( ( ushort ) Types . Ancestor ) ;
}
}
// All class tags are inhereted, so its handy to create one base class for you model classes with all tags you want
[ GenInLocalFolder ]
public partial class Ancestor : CodeGenSamples
{
public int fields ;
}
[ GenTask ( GenTaskFlags . SimpleDataPack ) ]
[ GenInLocalFolder ]
public partial class OtherData
{
public int someData ;
}
[ GenInLocalFolder ]
public class ExternalClass
{
public int somePublicField ;
// private fields are not included in extension methods generation
int somePrivateField ;
}