Университетский пакет, который представляет C# реактивную библиотеку и набор инструментов для разработки единства.
Этот набор инструментов состоит из нескольких частей:
Zergrush.reactive - это реактивная библиотека, предназначенная для лучшей альтернативы Unirx. Основная цель-управлять сложными логическими зависимостью состояния игры/UI и всевозможными «государственными наблюдателями». Состоит из следующих частей:
Zergrush.codegen - это инструменты генерации кода, которые позволяют добавлять огромный диапазон различных функций для ваших моделей данных, например, он может добавить:
Zergrush.Utils Коллекции инструментов Variuos для общих задач Gamedev и расширений для вышеупомянутых библиотек.
На данный момент процесс установки является следующим:
Используйте диспетчер пакетов> + (кнопка)> Добавить пакет из url url ...
Если у вас нет Newtonsoft.json-for-unity в вашем проекте, сначала установите это:
https://github.com/jilleJr/Newtonsoft.Json-for-Unity.git#upm
Затем установите этот пакет с URL:
https://github.com/CeleriedAway/ZergRush.git
Первая часть Zergrush.reactive - это EventReam
public interface IEventStream < out T > : IEventStream
{
IDisposable Subscribe ( Action < T > callback ) ;
}
Как вы видите, он представляет простое событие, которое вы можете подписаться, чтобы получить его обновления. Обычно ваше действие не может быть вызвано во время процесса подчинений. Основная реализация EventReam позволяет «send ()» события.
Подписаться метод вернуть Idisposable «Connection» объект. Если они расположены, то эти соединения больше не оцениваются, и после этого не будет вызван первоначальный обратный вызов. Одним из лучших использования для этого является сбор объектов соединения для автоматической вытирной промышленности, поэтому вы не забудете «отписаться».
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 ) { .. . }
}
Eventtreams могут быть отфильтрованы, преобразованы и объединены. Так..
// 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 ) ;
Но самая полезная часть библиотеки в разработке игры - это Cell
public interface ICell < out T >
{
IDisposable ListenUpdates ( Action < T > reaction ) ;
T value { get ; }
}
Основной API для клеток:
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 ) ;
Это представляет собой значение, которое меняется во времени. И идеально подходит для представления игровых данных.
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 ) ;
}
Я надеюсь, что этот код достаточно ясен
/*
* 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 ;
}