Jelly Forest es un juego runner 2D habilitado para blockchain. El juego incluye inicio de sesión con redes sociales, mejoras multinivel (donde los niveles superiores requieren mejoras de nivel inferior como insumos para construir/mintear) y mejoras cosméticas, todo lo cual se almacena en un smart contract wallet no custodial integrado. No hay ventanas emergentes para firmar transacciones ni requisitos de pago de gas para los jugadores.

¡Descárguelo en Google Play aquí!

Conozca por qué usar smart contract wallets aquí

Conozca qué es un embedded wallet aquí

Esta guía muestra cómo construimos Jelly Forest y cómo también puedes crear tu propio juego web3 usando el SDK de Unity de Sequence!

1. Cree el ciclo principal del juego

El primer paso es construir el ciclo básico del juego. ¡No olvides pensar primero en tu estrategia de monetización y cómo usarás los elementos web3!

Para nuestro ciclo de juego, compramos el Infinite Runner Engine en la Unity Asset Store. Dentro del asset, encontramos una escena demo JellyForest, que, con algunos ajustes, pudimos compilar y hacer funcionar en iOS y Android.

2. Integre inicio de sesión con redes sociales y la solución Embedded Wallet de Sequence

Configuración

  1. Instale el SDK de Unity de Sequence usando el Package Manager
  2. Inicie sesión en el Sequence Builder Console
  3. Cree un proyecto para su juego en el Builder Console
  4. Configure un Embedded Wallet en el Builder Console
  5. En su SequenceConfig scriptable object que importó desde el menú Samples en el Package Manager durante la etapa de instalación, agregue sus client ids de Google y Apple que también añadió en el Builder, así como su Configuration Key en WaaSConfigKey
    • ¡No olvide poner sus client ids de Android y iOS en sus respectivas plataformas!
  6. Agregue su Builder API Key desde el Builder Console en Settings > API Access Keys - necesita la clave prod

Inicio de sesión con redes sociales

  1. Cree una escena básica donde los jugadores puedan iniciar sesión.
  2. Cree un Canvas, agregue el componente Canvas Scaler y use el modo de escala de UI “Scale with Screen Size”. Esto hará que el LoginPanel (y cualquier otro elemento UI bajo este Canvas) se escale automáticamente al cambiar entre plataformas de compilación.
  3. Arrastre el prefab LoginPanel a la jerarquía de su escena bajo el Canvas. Puede encontrarlo en la ventana Project bajo Packages > Sequence Embedded Wallet SDK > SequenceFrontend > Prefabs.
  4. Cree un gestor de UI para llamar a Open en el LoginPanel. Vea nuestra implementación a continuación:
private void Start()

{

    LoginPanel loginPanel = GetComponentInChildren<LoginPanel>();

    if (loginPanel == null)

    {

        Debug.LogError("LoginPanel not found!");

    }

    loginPanel.Open();

}
  1. Rompa la referencia al prefab LoginPanel en la jerarquía para poder editarlo libremente en la vista de escena
    1. Seleccione el GameObject LoginPanel en la jerarquía
    2. Haga clic derecho en el GameObject LoginPanel en la jerarquía
    3. Prefab > Unpack Completely
  2. Personalice el LoginPanel para que se adapte al tema de su juego

El LoginPanel gestionará toda la lógica de inicio de sesión con redes sociales por usted. Si le interesa saber cómo está implementado, puede revisar las implementaciones de LoginPage y OpenIdAuthenticator. La autenticación funciona mediante el Open ID Connect Implicit Flow.

Registro de una sesión con la API de Sequence

Una vez que se complete el inicio de sesión con redes sociales, automáticamente se realizará una solicitud para registrar la sesión con las APIs de Sequence WaaS (Wallet as a Service). Así es como funciona:

Cuando finaliza el inicio de sesión con redes sociales, se dispara el evento OpenIdAuthenticator.SignedIn. Esto inicia el proceso de autorización en SequenceLogin.ConnectToWaaS.

Obtener el wallet del usuario

Para obtener el wallet, tendrá que suscribirse al evento SequenceWallet.OnWalletCreated.

SequenceWallet.OnWalletCreated += OnWalletCreatedHandler;

public void OnWalletCreatedHandler(SequenceWallet wallet) {

  // Do something

}

Le recomendamos importar SequenceConnector desde “Useful Scripts” en Samples en la página del Package Manager para el “Sequence Embedded Wallet SDK”. Por defecto, contiene mucho código útil para comenzar y sirve como una interfaz práctica para comunicarse con el SDK. Lo usamos bastante en nuestra integración con JellyForest.

En JellyForest, también creamos un LevelLoader MonoBehaviour que carga la siguiente escena cuando se dispara el evento SequenceWallet.OnWalletCreated.

private void Awake()

{

    SequenceWallet.OnWalletCreated += OnWalletCreated;

}



private void OnWalletCreated(SequenceWallet wallet)

{

    SceneManager.LoadScene("MenuScene");

}

Para obtener más información sobre cómo funciona la autenticación en la solución Embedded Wallet de Sequence, consulte nuestra documentación y entrada de blog.

3. Implemente un contrato de coleccionables

Ahora que nuestros jugadores pueden iniciar sesión y obtener un wallet, ¡agreguemos algunos coleccionables!

Le recomendamos ampliamente usar un contrato ERC1155. Es un estándar de tokens flexible y muy adecuado para juegos. Puede implementar fácilmente nuestra versión auditada de ERC1155 a través del Builder Console de la siguiente manera:

Esto es lo que hicimos para Jelly Forest.

Una vez que haya implementado su contrato inteligente, no olvide agregar la dirección de su contrato como Sponsored Address en la página de “Gas Sponsoring” del Builder Console. Así, los usuarios tendrán sus comisiones de gas patrocinadas automáticamente usando sus créditos de cómputo al interactuar con los contratos inteligentes de su juego.

4. Implemente un Remote Minter

Por defecto, los contratos ERC1155 implementados a través del Builder Console requieren que quienes llamen tengan los permisos adecuados para mintear un token. Aunque esto puede parecer una molestia a primera vista, ¡es algo bueno! Sin esto, cualquiera podría llamar al método de minteo en su contrato y darse a sí mismo una cantidad infinita de objetos dentro del juego.

Querrá implementar un servidor con un wallet de Sequence (u otro) y darle permisos de minteo en el Builder Console.

Cómo lo hicimos en Jelly Forest

En Jelly Forest, todas las monedas que recolecta durante el juego se mintean como tokens ERC1155. Así es como lo hicimos:

  1. Regístrese en Cloudflare – aquí es donde alojamos el código del servicio de minteo; si lo prefiere, puede usar cualquier otro método
  2. Abra la terminal u otra línea de comandos
  3. git clone https://github.com/0xsequence-demos/cloudflare-worker-sequence-relayer.git luego cd cloudflare-worker-sequence-relayer
  4. git checkout permissionedMinter
  5. pnpm install – para instalar las dependencias
  6. Instale wrangler
pnpm install wrangler --save-dev

alias wrangler='./node_modules/.bin/wrangler'

inicie sesión

wrangler login
  1. Abra wrangler.toml
    1. Asigne un nombre a su servidor cambiando el valor de name
    2. Cree un nuevo wallet EOA y exporte la clave privada. Cualquier wallet EOA funciona. Puede usar Metamask para configurar un wallet fácilmente y exportar la clave privada. ¡Tenga mucho cuidado con la clave privada y no la guarde en texto plano en su computadora ni la suba a control de versiones! Establezca esto en PKEY
    3. Establezca el CONTRACT_ADDRESS
    4. Establezca el PROJECT_ACCESS_KEY – esta es su clave API de producción del Builder Console que obtuvo antes al configurar el objeto scriptable SequenceConfig
    5. Establezca el CHAIN_HANDLE – si no está seguro de cuál es, puede ver el CHAIN_HANDLE de cada red respectiva en la página Node Gateway del Builder Console.
  2. pnpm dev – esto implementará el servidor localmente. Debería ver en la línea de comandos a qué localhost se implementó
  3. Abra otra ventana de línea de comandos
  4. curl http://localhost:8787 – sustituya el localhost que le hayan asignado. Esto enviará un ping al servidor.
  5. En la línea de comandos donde está corriendo el servidor localhost, debería ver que la dirección del wallet del minter ha sido registrada
  6. Otorgue permisos de minteo a esta dirección en el Builder Console
    1. Busque el contrato bajo Contracts y haga clic para abrirlo
    2. Haga clic en Write Contract
    3. Expanda grantRole
    4. En role ingrese 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6 – este es el hash Keccak-256 de MINTER_ROLE
    5. En account pegue la dirección del wallet del minter
  7. wrangler deploy – esto implementará el código en un Cloudflare Worker y le dará una URL de minteo

¡Listo! Ahora, cuando enviamos una solicitud POST a nuestro servidor con un cuerpo definido en C#, donde el proof es generado por el cliente que envía la solicitud de minteo. En el Unity SDK esto se implementa mediante el MintingRequestProver.

5. Mintee tokens dentro del juego al inventario del jugador

Ahora que tenemos nuestro servidor minter con permisos configurado, necesitamos conectar el lado del cliente (la app hecha con Unity) para que podamos empezar a dar tokens a nuestros jugadores a través del juego. Podemos enviar una solicitud al minter con permisos usando el Unity SDK llamando al método PermissionedMinter.MintToken.

En Jelly Forest, a medida que el jugador avanza por el nivel recolecta muchas monedas, todas son tokens ERC1155. Aún hay algunos retos que debemos resolver para brindar una excelente experiencia de usuario.

  1. ¿Cómo lee la cadena para saber qué tokens/derechos tiene el usuario en su inventario?
  2. Las transacciones en blockchain, aunque rápidas en algunas cadenas como Arbitrum, no son instantáneas. Recolectar una moneda (u otro objeto) y luego tener que esperar unos segundos antes de que aparezca en su inventario dentro del juego, en general, no es una buena experiencia para el usuario final.
  3. A primera vista, puede parecer tentador enviar una transacción cada vez que un usuario gana un token en su juego. Sin embargo, en la mayoría de los juegos, especialmente en juegos como Jelly Forest donde los jugadores recolectan muchas monedas (tokens), esto terminará enviando una gran cantidad de transacciones y ¡le costará una fortuna en comisiones de gas!

¡Veamos cómo resolvimos todos estos problemas en Jelly Forest usando el Unity SDK!

1. Lectura de la cadena

Leer los tokens en el wallet de un usuario es un proceso complicado que se facilita mucho usando el Indexer de Sequence, que implementa el Unity SDK.

Aquí hay un fragmento de código de Jelly Forest donde usamos el Indexer para leer todos los tokens en el wallet de un jugador desde el contrato ERC1155 de nuestro juego.

private Dictionary<BigInteger, TokenBalance> _tokenBalances = new Dictionary<BigInteger, TokenBalance>();

private async Task GetTokenBalances(Page page = null)

{

    if (page == null)

    {

        page = new Page();

    }

    GetTokenBalancesReturn balances = await _indexer.GetTokenBalances(new GetTokenBalancesArgs(_userAddress, SequenceConnector.ContractAddress, false, page));

    int uniqueTokens = balances.balances.Length;

    for (int i = 0; i < uniqueTokens; i++)

    {

        _tokenBalances[balances.balances[i].tokenID] = balances.balances[i];

    }

    if (balances.page.more)

    {

        await GetTokenBalances(balances.page);

    }

}

2. Construyendo un caché

Como las transacciones en blockchain no son instantáneas pero queremos dar retroalimentación inmediata al usuario, usaremos un caché simple en memoria.

Cuando recibimos nuestro SequenceWallet en Jelly Forest, SequenceConnector, que usamos como interfaz principal para comunicarnos con el SDK de Sequence en nuestro juego, crea un Inventory.

private void OnWalletCreated(SequenceWallet wallet)

{

    Wallet = wallet;

    Wallet.OnSendTransactionComplete += OnSendTransactionCompleteHandler;

    Wallet.OnSendTransactionFailed += OnSendTransactionFailedHandler;

    Wallet.OnSignMessageComplete += OnSignMessageCompleteHandler;

    Wallet.OnDeployContractComplete += OnDeployContractCompleteHandler;

    Wallet.OnDeployContractFailed += OnDeployContractFailedHandler;

    Wallet.OnDropSessionComplete += OnDropSessionCompleteHandler;

    Wallet.OnSessionsFound += OnSessionsFoundHandler;



    Inventory = new Inventory(Indexer, Wallet.GetWalletAddress(), ItemCatalogue); // [!code focus]



    _transactionQueuer.Setup(Wallet, Chain);

    _permissionedMinterTransactionQueuer.Setup(Wallet, Chain, "https://sequence-relayer-jelly-forest2.tpin.workers.dev/", ContractAddress);

}

El Inventory se usa como un caché simple en nuestro juego. Cuando se crea por primera vez, y cuando se solicita, usamos el Indexer para obtener todos los tokens en el wallet del usuario. A partir de ahí, cada vez que el usuario gana un token, actualizamos nuestro caché (Inventory) y los datos en la cadena.

Lea la implementación completa de Inventory aquí

3. Uso de una cola de transacciones

El Unity SDK de Sequence ofrece un sistema de colas de transacciones muy flexible.

En Jelly Forest, adjuntamos un PermissionedMinterTransactionQueuer como MonoBehaviour a nuestro GameObject SequenceConnector y obtenemos una referencia en Awake.

Una vez que esto está configurado, todo lo que necesitamos hacer cuando se recolecta un token es llamar a “mint token”.

public class CollectibleToken : Coin

{

    protected override void ObjectPicked()

    {

        base.ObjectPicked();

        if (SequenceConnector.Instance == null || SequenceConnector.Instance.Wallet == null)

        {

            Debug.LogWarning("No minting will happen. Make sure SequenceConnector is in the scene and user is logged in.");

            return;

        }

        SequenceConnector.Instance.MintFungibleToken(); // [!code focus]

    }

}

Esto actualizará nuestro Inventory y añadirá una transacción de minteo a la cola del PermissionedMinterTransactionQueuer. El PermissionedMinterTransactionQueuer combinará automáticamente las transacciones cuando sea posible para que usted gaste la menor cantidad de dinero posible en comisiones de gas.

En Jelly Forest, hemos configurado nuestro gestor de transacciones para enviar transacciones cada vez que el jugador termina una partida, pero no antes de cada 30 segundos.

¿Cómo determinar la frecuencia con la que debe enviar sus transacciones?

Con nuestro Unity SDK, esto se convierte más en una cuestión de diseño de juego que en otra cosa.

Nuestros TransactionQueuers pueden configurarse para enviar transacciones automáticamente cada X segundos, cuando se solicite (mediante llamada de función) pero no antes de cada Y segundos, o cuando se solicite, ignorando cualquier umbral mínimo de tiempo configurado (Y segundos).

Aquí hay algunos aspectos a considerar al decidir cómo configurar sus gestores de transacciones:

  • Cuanto más seguido envíe transacciones, más comisiones de gas pagará. Por supuesto, la blockchain compatible con EVM que elija influirá mucho en la cantidad y complejidad de transacciones que puede enviar antes de que los costos sean prohibitivos.
  • Cuanto menos frecuente sea el envío de transacciones, más desincronizado quedará el estado de su juego (caché) con la información en la blockchain. Si una transacción falla, necesitará una forma de recuperarse de esto sin afectar la experiencia de juego de sus usuarios.

Como ejemplo de Jelly Forest: consideramos que las transacciones de la tienda eran de alta importancia para el usuario final. No queríamos arriesgarnos a que un usuario pensara que tenía una mejora o sombrero y luego la transacción fallara, teniendo que revocar la mejora/sombrero o mintear uno extra que el jugador quizá no haya ganado legítimamente. Por eso, hicimos que el usuario espere en las páginas de la tienda hasta que la transacción de compra (y todas las demás transacciones en los TransactionQueuers) hayan sido exitosas.

public async Task Buy()

{

    if (Status != ItemStatus.Available)

    {

        return;

    }



    if (SequenceConnector.Instance == null)

    {

        string error = "SequenceConnector not found. User has not logged in";

        Debug.LogError($"Failed to purchase shop item: {error}");

        OnFailedToPurchaseShopItem?.Invoke($"Failed to purchase shop item: {error}");

        return;



    SequenceConnector.Instance.AddToTransactionQueue(new PurchaseShopItemQueueableTransaction(this));

    TransactionReturn result = await SequenceConnector.Instance.SubmitQueuedTransactions(true, false); // [!code hl]

    if (result is SuccessfulTransactionReturn successfulTransactionReturn)

    {

        BurnTokensFromInventory();

        MintTokenInInventory()

        if (string.IsNullOrWhiteSpace(successfulTransactionReturn.txHash))

        {

            GetTransactionReceipt(successfulTransactionReturn);

        }

    }

    else if (result is FailedTransactionReturn failed)

    {

        string error = $"Transaction failed: {failed.error}";

        Debug.LogError(error);

        OnFailedToPurchaseShopItem?.Invoke($"Failed to purchase shop item: {error}");

    }

    else

    {

        throw new Exception("Unexpected transaction result type");

    }

}

6. Queme tokens del juego a cambio de otros

En Jelly Forest, puede comprar potenciadores y cosméticos quemando monedas y (a veces) potenciadores de menor nivel.

Para habilitar y hacer cumplir esta mecánica, desplegamos un contrato inteligente BurnToMint sencillo. Este contrato le permite especificar requisitos de minteo (ids de token requeridos y cantidades asociadas) para un id de token dado. Cuando recibe un lote de tokens ERC1155 y el remitente especifica el id de token que desea mintear en el parámetro data, el contrato verifica si recibió la cantidad requerida de cada id de token; si esto se cumple, el contrato quema los tokens y mintea el id de token solicitado al remitente (usuario); de lo contrario, la transacción falla y se revierte.

Le hemos otorgado a este contrato permisos de minteo para nuestro contrato del juego en el Builder Console:

  1. Busque el contrato bajo Contracts y haga clic para abrirlo
  2. Haga clic en Write Contract
  3. Expanda grantRole
  4. En role ingrese 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6 – este es el hash Keccak-256 de MINTER_ROLE
  5. En account pegue la dirección del wallet del minter

:::danger Advertencia: el contrato inteligente BurnToMint compartido arriba no ha sido auditado por terceros. ¡Reutilícelo con precaución! :::

Cuando un usuario compra una mejora o cosmético en la tienda, enviamos una transacción al contrato inteligente BurnToMint agregando una PurchaseShopItemQueueableTransaction a nuestro SequenceWalletTransactionQueuer en nuestro SequenceConnector.

SequenceConnector.Instance.AddToTransactionQueue(new PurchaseShopItemQueueableTransaction(this));

7. Construcción de las páginas de la tienda y configuración de los requisitos de minteo

Al construir las páginas de la tienda y definir los precios/requisitos de minteo para las diferentes mejoras y sombreros en Jelly Forest, optamos por definir los ShopItems usando Scriptable Objects porque son fáciles de ajustar y visualizar ya que pueden ser serializados en el Inspector. Estos scriptable objects también nos permiten definir qué es cada Item y asociarlos con un id de token.

Sin embargo, rápidamente se volvió tedioso (y una posible fuente de errores) mantener sincronizados los requisitos de minteo definidos en los Scriptable Objects y los definidos en nuestro contrato BurnToMint en la blockchain.

Creamos una extensión de editor para nuestros scriptable objects ShopItem agregando un botón que, al presionarlo, verifica si los requisitos de minteo definidos en la blockchain coinciden con los definidos en el scriptable object; si difieren, enviará una transacción para actualizar los requisitos de minteo en el contrato BurnToMint en la blockchain para que coincidan con el scriptable object. La transacción se envía mediante un wallet EOA creado a partir de una clave privada almacenada como variable de entorno en la máquina de uno de nuestros desarrolladores. Este wallet EOA es el propietario de este contrato.

De hecho, nuestras páginas de la tienda consultan el contrato inteligente cada 60 segundos (y cada vez que se abren) para detectar cambios en los requisitos de minteo, actualizando su interfaz en consecuencia. ¡Esto nos permite hacer ajustes en vivo a la economía del juego sin requerir una actualización!

Haga clic en el video a continuación

Vea la implementación de ShopItemEditorExtension aquí.

8. Aproveche los artículos comprados dentro del juego

¡Perfecto! Sus jugadores pueden iniciar sesión, obtener un wallet, ganar tokens y comprar cosas con sus tokens. Ahora solo falta darles una razón para querer comprar cosas en primer lugar. En otras palabras, es momento de aplicar su creatividad como desarrollador de juegos y crear potenciadores y cosméticos increíbles.

Todo lo que debe hacer para llevar sus tokens al juego es verificar si el usuario posee suficiente cantidad del id de token correspondiente y aplicar el efecto del token.

En Jelly Forest, definimos varios PowerUpTypes diferentes y asignamos a cada Item un PowerUpType y un rango. Luego, consultamos nuestro Inventory para encontrar el mejor potenciador de cada tipo que el jugador posee.