Paquete de unidad que representa la biblioteca reactiva de C# y un conjunto de herramientas para el desarrollo de la unidad.
Este conjunto de herramientas consta de varias partes:
Zergrush.reactive es una biblioteca reactiva diseñada para ser una mejor alternativa a UNIRX. El objetivo principal es gestionar las dependencias lógicas complejas del estado de juego/UI y todo tipo de "observadores de estado". Consiste en las siguientes partes:
Zergrush.codegen es una herramientas de generación de código que permite agregar una amplia gama de diversas funcionalidades para sus modelos de datos, por ejemplo, puede agregar:
Zergrush.utils colecciones de herramientas de variuos para tareas y extensiones comunes para bibliotecas anteriores.
Por ahora el proceso de instalación es el siguiente:
Use el administrador de paquetes> + (botón)> Agregar paquete desde la URL Git ...
Si no tiene newtonsoft.json-for-unity en su proyecto, primero instale esto:
https://github.com/jilleJr/Newtonsoft.Json-for-Unity.git#upm
Luego instale este paquete con URL:
https://github.com/CeleriedAway/ZergRush.git
La primera parte de Zergrush.reactive es EventsTream
public interface IEventStream < out T > : IEventStream
{
IDisposable Subscribe ( Action < T > callback ) ;
}Como puede ver, representa un evento simple que puede suscribirse para obtener sus actualizaciones. Por lo general, su acción no se llamaría durante el proceso de susribtaciones. Los eventos de implementación principales de eventos de implementación permiten eventos "enviar ()".
Suscribir el método de retorno de retorno Idisposable "Conexión" objeto. Si se elimina, esta conexión ya no se valora y la devolución de llamada inicial no se llamará después de eso. Uno de los mejores USECase para esto es una recolección de objetos de conexión para una disposición automáticamente, por lo que no olvidará "cancelar la suscripción"
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 se puede filtrar, transformar y fusionarse. Como esto..
// 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 ) ;Pero la parte más útil de la biblioteca en el desarrollo del juego es la celda
public interface ICell < out T >
{
IDisposable ListenUpdates ( Action < T > reaction ) ;
T value { get ; }
} La API básica para las células es:
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 un valor que está cambiando en el tiempo. Y un ajuste perfecto para representar los datos del juego.
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 sea lo suficientemente claro
/*
* 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 ;
}