Nethereum.Flappy
Flappy Bird example
Install / Use
/learn @Nethereum/Nethereum.FlappyREADME
Nethereum.Flappy
This is the csharp source code for the Flappy Bird example on how to integrate Unity3d and Ethereum. It is assumed some familiarity with Nethereum as the Unity3d support extends the current implementation.
If you are not familiar with Unity3d, this sample is based on the Unity3d tutorial on how to build a flappy bird style game, full tutorial here: https://unity3d.com/learn/tutorials/topics/2d-game-creation/project-goals
And full game here: https://assetstore.unity.com/packages/templates/flappy-bird-style-example-game-80330
Note: For up to date how to get started templates using Nethereum, use the following:
Unity3dSimpleSampleNet461: A solution for all platforms using the latest version of Nethereum and integrating Unity and Nethereum check also this tutorial. https://github.com/Nethereum/Unity3dSimpleSampleNet461
Nethereum Unity Webgl: A solution example on how to integrate with Nethereum/Metamask and NFTs https://github.com/Nethereum/Nethereum.Unity.Webgl
Working with Unity3d
To enable cross platform compatibility and the threading mechanism using coroutines for Unity3d, Nethereum uses a new type of RPC Client, the UnityRpcClient.
All the Unity RPCRequests inherit now from this client, for example to retrieve the current blocknumber you will need to use: EthBlockNumberUnityRequest
The UnityRpcClient similarly to other RPC providers accepts an RPCRequest, but internally uses UnityWebRequest which is compatible with Webgl and Il2Cpp.
Coroutines do not support any return values, to overcome this, the client inherits from UnityRequest, which provide the following properties:
public class UnityRequest<TResult> {
public TResult Result { get; set; }
public Exception Exception { get; set; }
}
All this UnityRPC requests, internally wrap the core RPC requests, decoupling the RPC clients but maintaining the integrity with the core requests.
Simple RPC calls
So how does this work? Let’s start with simple RPC calls, like the one to retrieve the current block number on the Ethereum Flappy Unicorn game.
Note: in this sample, a special INFURA API key is used: 7238211010344719ad14a89db874158c. If you wish to use this sample in your own project you’ll need to sign up on INFURA and use your own key.
var blockNumberRequest = new EthBlockNumberUnityRequest("https://rinkeby.infura.io/v3/7238211010344719ad14a89db874158c");
Normally we would use web3 to manage all the RPC requests, but for Unity3d we use a specific request per each type of RPC call, including information about the RPC client.
In this scenario the request is using as the RPC provider the Rinkbey public node provided by Infura.
When requesting a block number we don’t need any parameters, so we can simply just send the request and “yield” until it is complete.
yield return blockNumberRequest.SendRequest();
Once is completed, we can check for any errors and parse the result
if(blockNumberRequest.Exception == null) {
var blockNumber = blockNumberRequest.Result.Value;
blockNumberText.text = "Block: " + blockNumber.ToString();
}
Full source code:
using System;
using System.Collections;
using Nethereum.Hex.HexTypes;
using Nethereum.JsonRpc.UnityClient;
using Nethereum.RPC.Eth.Blocks;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class BlockNumber : MonoBehaviour {
public Text blockNumberText;
private float blockCheckRate = 3f;
private float lastBlockCheckTime;
void Start () {
lastBlockCheckTime = 0f;
StartCoroutine (CheckBlockNumber ());
}
public IEnumerator CheckBlockNumber () {
var wait = 1;
while (true) {
yield return new WaitForSeconds (wait);
wait = 10;
var blockNumberRequest = new EthBlockNumberUnityRequest ("https://rinkeby.infura.io/v3/7238211010344719ad14a89db874158c");
yield return blockNumberRequest.SendRequest ();
if (blockNumberRequest.Exception == null) {
var blockNumber = blockNumberRequest.Result.Value;
blockNumberText.text = "Block: " + blockNumber.ToString ();
}
}
}
}
Contract calls
Contract calls are made in a similar way to any other Unity RPC call, but they need the request to be encoded.
For example to retrieve the user top score we will create first a EthCallUnityRequest, responsible to make an eth_call rpc request.
var userTopScoreRequest = new EthCallUnityRequest("https://rinkeby.infura.io/v3/7238211010344719ad14a89db874158c");
Using the contract ABI and contract address we can then create a new contract and retrieve the function.
var contract = new Contract(null, ABI, contractAddress);
var function = new contract.GetFunction("userTopScores");
Note The contract does not have now the generic RPC client as a constructor parameter.
The contract function can build the call input to retrieve the user top score.
var callInput = function.CreateCallInput(userAddress);
var blockParameter = Nethereum.RPC.Eth.DTOs.BlockParameter.CreateLatest();
yield return userTopScoreRequest.SendRequest(callInput, blockParameter);
Once we have retrieved the result successfully we use the function to decode the output.
var topScore = function.DecodeSimpleTypeOutput<int>(userTopScoreRequest.Result);
Full source code:
//Check if the user top score has changed on the contract chain every 2 seconds
public IEnumerator CheckTopScore () {
var wait = 0;
while (true) {
yield return new WaitForSeconds (wait);
wait = 2;
//Create a unity call request (we have a request for each type of rpc operation)
var userTopScoreRequest = new EthCallUnityRequest (_url);
if (_userAddress != null) {
//Use the service to create a call input which includes the encoded
var userTopScoreCallInput = _scoreContractService.CreateUserTopScoreCallInput (_userAddress);
//Call request sends and yield for response
yield return userTopScoreRequest.SendRequest (userTopScoreCallInput, Nethereum.RPC.Eth.DTOs.BlockParameter.CreateLatest ());
//Each request has a exception and a result. The exception is set when an error occurs.
//Follows a similar patter to the www and unitywebrequest
if (userTopScoreRequest.Exception == null) {
//decode the top score using the service
var topScoreUser = _scoreContractService.DecodeUserTopScoreOutput (userTopScoreRequest.Result);
//and set it to the text box
topScoreText.text = "Your top: " + topScoreUser.ToString ();
//set the value on the global worl
GameControl.instance.TopScoreRecorded = topScoreUser;
wait = 3;
} else {
Debug.Log (userTopScoreRequest.Exception.ToString ());
}
}
}
}
Submitting Transactions
Before we submit the transaction in a similar way when we make a call we need to build the transaction input. For example to submit the user top score.
function.CreateTransactionInput(addressFrom, gas, valueAmount, score, v, r, s);
In this scenario the input parameters are the score, and the signature values for v, r, s.
Signing the transactions
There are many way that you can sign the transactions, as usual this depends on where the user stores their private keys and / or what is the best user experience.
An option, could be, that for Desktop and Mobile applications the user can open the web3 secret storage definition file (account file in geth / parity) and sign the transaction with their private key.
var transactionSignedRequest = new TransactionSignedUnityRequest(_url, key, _userAddress);
yield return transactionSignedRequest.SignAndSendTransaction(transactionInput);
WebGL and Metamask
Decrypting using WebGL / JavaScript the account file is extremely slow, so when deploying a game to the browser, as per the Unicorn Flappy sample, it makes more sense to delegate the signing to Metamask.
To achieve this you can create your own external library to interact with the injected web3 library in the browser. For more information on External libraries check the Unity documentation https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html
NOTE Check for an updated version on metamask integration https://github.com/Nethereum/Nethereum.Unity.Webgl
[DllImport("__Internal")]
private static extern string SendTransaction(string to, string data);
Full Source code sample
//Game control sets a signal
if (GameControl.instance.SubmitTopScore && !submitting) {
if (_userAddress != null) {
submitting = true;
Debug.Log ("Submitting tx");
//Create the transaction input with encoded values for the function
var transactionInput = _scoreContractService.CreateSetTopScoreTransactionInput (_userAddress, _addressOwner, _privateKey,
GameControl.instance.TopScore,
new HexBigInteger (4712388));
if (ExternalProvider) {
Debug.Log ("Submitting tx to score using external: " + transactionInput.Data);
SendTransaction (transactionInput.To, transactionInput.Data);
} else {
//Create Unity Request with the private key, url and user address
//(the address could be recovered from private key as in normal Nethereum, could put this an overload)
Related Skills
node-connect
340.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
340.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.2kCommit, push, and open a PR
