Pacote de unidade que representa a biblioteca reativa C# e o conjunto de ferramentas para o desenvolvimento da unidade.
Este conjunto de ferramentas consiste em várias partes:
ZerGrush.Reative é uma biblioteca reativa projetada para ser melhor alternativa ao UNIRX. O objetivo principal é gerenciar dependências lógicas complexas do estado de jogo/UI e todos os tipos de "observadores de estado". Consiste nas seguintes peças:
ZerGrush.CodeGen é uma ferramenta de geração de código que permite adicionar uma grande variedade de várias funcionalidades para seus modelos de dados, por exemplo, pode adicionar:
Coleções ZerGrush.utils das ferramentas Variuos para tarefas e extensões comuns de gamedev para bibliotecas acima.
Por enquanto, o processo de instalação é o seguinte:
Use o gerenciador de pacotes> + (botão)> Adicionar pacote do URL Git ...
Se você não possui newtonsoft.json-for-Unity em seu projeto, primeiro instale isso:
https://github.com/jilleJr/Newtonsoft.Json-for-Unity.git#upm
Em seguida, instale este pacote com URL:
https://github.com/CeleriedAway/ZergRush.git
A primeira parte do ZerGrush.Retive é o EventStream
public interface IEventStream < out T > : IEventStream
{
IDisposable Subscribe ( Action < T > callback ) ;
}
Como você vê, representa um evento simples que você pode se inscrever para obter suas atualizações. Normalmente, sua ação não seria chamada durante o processo de subestrições. Os principais eventos de implementação permitem os eventos "send ()".
Inscreva -se método Retorno objeto de "conexão" idisposável. Se disposto, essas conexões não são mais dignas e o retorno de chamada inicial não será chamado depois disso. Uma das melhores USECASE para isso é uma coleta de objetos de conexão para disposição automaticamente, para que você não esqueça de "cancelar a inscrição"
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 ) { .. . }
}
Os eventos podem ser filtrados, transformados e mesclados. Assim..
// 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 ) ;
Mas a parte mais útil da biblioteca no desenvolvimento de jogos é a célula
public interface ICell < out T >
{
IDisposable ListenUpdates ( Action < T > reaction ) ;
T value { get ; }
}
API básica para células é:
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 ) ;
Representa um valor que está mudando no tempo. E um ajuste perfeito para representar dados do jogo.
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 ) ;
}
Espero que este código seja claro o suficiente
/*
* 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 ;
}