diff --git a/docs/smart-contracts/languages/smartpy.md b/docs/smart-contracts/languages/smartpy.md index 120656c0a..e3d67a426 100644 --- a/docs/smart-contracts/languages/smartpy.md +++ b/docs/smart-contracts/languages/smartpy.md @@ -35,17 +35,17 @@ import smartpy as sp @sp.module def main(): class StoreGreeting(sp.Contract): - def __init__(self, greeting): # Note the indentation + def __init__(self, greeting): # Note the indentation # Initialize the storage with a string passed at deployment time # Cast the greeting parameter to a string sp.cast(greeting, sp.string) self.data.greeting = greeting - @sp.entrypoint # Note the indentation + @sp.entrypoint # Note the indentation def replace(self, params): self.data.greeting = params.text - @sp.entrypoint # Note the indentation + @sp.entrypoint # Note the indentation def append(self, params): self.data.greeting += params.text diff --git a/docs/smart-contracts/views.md b/docs/smart-contracts/views.md index b58dbce06..0ff1d5cca 100644 --- a/docs/smart-contracts/views.md +++ b/docs/smart-contracts/views.md @@ -172,10 +172,10 @@ def callView(self, a, b): sp.cast(a, sp.int) sp.cast(b, sp.int) viewResponseOpt = sp.view( - "get_larger", # Name of the view - sp.address("KT1K6kivc91rZoDeCqEWjH8YqDn3iz6iEZkj"), # Address of the contract - sp.record(a=a, b=b), # Parameters to pass - sp.int # Return type of the view + "get_larger", # Name of the view + sp.address("KT1K6kivc91rZoDeCqEWjH8YqDn3iz6iEZkj"), # Address of the contract + sp.record(a=a, b=b), # Parameters to pass + sp.int # Return type of the view ) if viewResponseOpt.is_some(): self.data.myval = viewResponseOpt.unwrap_some() @@ -185,10 +185,10 @@ If the view takes no parameters, pass `()` for the parameter: ```python viewResponseOpt = sp.view( - "no_param_view", # Name of the view - sp.address("KT1K6kivc91rZoDeCqEWjH8YqDn3iz6iEZkj"), # Address of the contract - (), # No parameter - sp.int # Return type of the view + "no_param_view", # Name of the view + sp.address("KT1K6kivc91rZoDeCqEWjH8YqDn3iz6iEZkj"), # Address of the contract + (), # No parameter + sp.int # Return type of the view ) ``` diff --git a/docs/tutorials/smart-contract/smartpy.mdx b/docs/tutorials/smart-contract/smartpy.mdx index d9505ef97..eac8c85cc 100644 --- a/docs/tutorials/smart-contract/smartpy.mdx +++ b/docs/tutorials/smart-contract/smartpy.mdx @@ -114,17 +114,17 @@ You can work with SmartPy code in any IDE, but this online IDE keeps you from ha @sp.module def main(): class StoreGreeting(sp.Contract): - def __init__(self, greeting): # Note the indentation + def __init__(self, greeting): # Note the indentation # Initialize the storage with a string passed at deployment time # Cast the greeting parameter to a string sp.cast(greeting, sp.string) self.data.greeting = greeting - @sp.entrypoint # Note the indentation + @sp.entrypoint # Note the indentation def replace(self, params): self.data.greeting = params.text - @sp.entrypoint # Note the indentation + @sp.entrypoint # Note the indentation def append(self, params): self.data.greeting += params.text ``` @@ -178,17 +178,17 @@ import smartpy as sp @sp.module def main(): class StoreGreeting(sp.Contract): - def __init__(self, greeting): # Note the indentation + def __init__(self, greeting): # Note the indentation # Initialize the storage with a string passed at deployment time # Cast the greeting parameter to a string sp.cast(greeting, sp.string) self.data.greeting = greeting - @sp.entrypoint # Note the indentation + @sp.entrypoint # Note the indentation def replace(self, params): self.data.greeting = params.text - @sp.entrypoint # Note the indentation + @sp.entrypoint # Note the indentation def append(self, params): self.data.greeting += params.text diff --git a/docs/unity.md b/docs/unity.md index 3f924990b..737dafabd 100644 --- a/docs/unity.md +++ b/docs/unity.md @@ -23,6 +23,10 @@ For a walkthrough of installing and using the SDK in an existing Unity project, The SDK includes tutorial scenes that demonstrate how to use the SDK. For information about setting up and using the scenes, see [Tutorial scenes](./unity/scenes). +## Sample game + +For information about a complete sample game that you can load locally and explore, see [Sample game](./unity/sample-game). + ## SDK objects The SDK provides objects that you can use to interact with user wallets and with Tezos. diff --git a/docs/unity/connecting-accounts.md b/docs/unity/connecting-accounts.md index 20bc71c1d..3d4b292db 100644 --- a/docs/unity/connecting-accounts.md +++ b/docs/unity/connecting-accounts.md @@ -2,7 +2,7 @@ title: Connecting accounts authors: Tim McMackin last_update: - date: 11 January 2024 + date: 14 May 2024 --- Connecting to a user's wallet is a prerequisite to working with Tezos in any application. @@ -14,6 +14,8 @@ Game developers can also use the wallet and its account as a unique account iden For an example of connecting to wallets, see the [WalletConnection tutorial scene](./scenes#wallet-connection-scene). +For more information about Tezos wallets, see [Installing and funding a wallet](../developing/wallet-setup). + ## Best practices When working with wallets, be sure to follow the advice in [Best practices and avoiding flaws](../dApps/best-practices) for wallet connections. diff --git a/docs/unity/sample-game.md b/docs/unity/sample-game.md new file mode 100644 index 000000000..51d60cc85 --- /dev/null +++ b/docs/unity/sample-game.md @@ -0,0 +1,144 @@ +--- +title: Sample game +authors: Tim McMackin +last_update: + date: 22 May 2024 +--- + +The sample game for the Unity SDK is a single-player third-person shooter with survival elements. +Players receive tokens that represent in-game items and the game tracks and transfers them via the Tezos blockchain. +You can import this game into the Unity Editor and work with it yourself. + +The game shows how developers might structure a large-scale dApp by separating different features into different components, as described below in [Architecture](#architecture). +In particular, it handles some Tezos interaction from the Unity game itself, including connecting to the user's wallet and prompting them to sign a payload to authenticate. +The rest of the Tezos interaction happens in the backend application, including distributing tokens that represent in-game objects. +The Unity game also allows users to transfer those tokens to other accounts. + +The source code for the Unity frontend application is here: https://github.com/baking-bad/tezos-unity-game. +To open it locally, see [Opening the sample game](#opening-the-sample-game). +To play the game, go to https://game.baking-bad.org. + +The source code for the backend application is here: https://github.com/k-karuna/tezos_game_back. + +![A screenshot from within the game interface](/img/unity/sample-game-ui.png) + +## Architecture + +The sample game uses these main components: + +- **User wallets** as a source of user identity and authentication. +The user doesn't make any direct transactions from the wallet and pays no tez to the application or in fees. +For more information about Tezos wallets, see [Installing and funding a wallet](../developing/wallet-setup). + +- The **Unity WebGL application** is the front end of the application. +It connects to the user wallet, sends the sign request, runs the game interface, and sends requests to the backend. + +- The **backend application** hosts a REST API for the Unity application to call. +The Unity application calls it from the `Assets/Scripts/Api/GameApi.cs` file for tasks such as these: + + - Getting the signed payload from the wallet + - Verifying the signed payload + - Tracking events such as the beginning and end of a game session + - Sending tokens to players' accounts + + The backend application is responsible for most of the interaction with the smart contract, including transferring tokens to players. + It securely manages the private key for the administrator account, which is responsible for minting and transferring tokens. + This way, the game client itself has no access to the private key. + + For information about the REST API endpoints, see this page: https://game.baking-bad.org/back/swagger/. + +- The **backend database** stores persistent information about players, such as the number of games they have played. + +- The **smart contract** is a program that runs on the Tezos blockchain to manage tokens that represent in-game items. +It maintains a ledger of tokens and owners and allows the backend's administrator account to transfer them to players. +The sample game uses a custom contract, but you can use the SDK's built-in FA2-compliant contract; see [Managing contracts](./managing-contracts). +You can view and interact with the contract on a block explorer, such as tzkt.io: https://tzkt.io/KT1TSZfPJ5uZW1GjcnXmvt1npAQ2nh5S1FAj/operations. + +- The **Interplanetary File System (IPFS)** stores metadata for the tokens, including pictures and descriptions. + +This diagram shows the basic interaction between these components: + +![The architecture of the sample game, showing interaction between the user wallet, the Unity WebGL application, the backend, and the smart contract](/img/unity/sample-game-architecture.png) + +## Authentication + +The game uses the user's Tezos account as a source of authentication. +It prompts the user to connect their Tezos wallet so it can retrieve the user's account address. +For more information about connecting to user wallets, see [Connecting accounts](./connecting-accounts). + +When the wallet is connected, the game prompts the user to sign a payload to prove that they have the key for the account. +The process follows these general steps: + +1. The user loads the game client and clicks the button to connect. +1. The game client requests an authentication payload from the backend. +1. The backend [generates a random string](https://github.com/k-karuna/tezos_game_back/blob/e6bc9c021b86704ec1ce1b5e3fd799977d05034f/api/views.py#L20) and sends it to the Unity application. +1. The Unity application [sends the string as a signing request payload](https://github.com/baking-bad/tezos-unity-game/blob/7e3fb6454896896f7e0ac77f09d2b5f02e104aa7/Assets/Scripts/Managers/UserDataManager.cs#L108) to the wallet. +1. The user signs the payload in their wallet application. +1. The Unity application [receives the signed payload and sends it to the backend](https://github.com/baking-bad/tezos-unity-game/blob/9b71d3832dac076d74bd822c19b5f93909434190/Assets/Scripts/Managers/UserDataManager.cs#L78). +1. The backend [verifies that the payload is correctly signed](https://github.com/k-karuna/tezos_game_back/blob/e6bc9c021b86704ec1ce1b5e3fd799977d05034f/api/views.py#L50). +1. The game [allows the user to play if validation is successful](https://github.com/baking-bad/tezos-unity-game/blob/9b71d3832dac076d74bd822c19b5f93909434190/Assets/Scripts/Managers/UserDataManager.cs#L80). + +Here is a diagram of the process: + +![Authentication flow diagram](/img/unity/unity-sample-game-authentication.png) + +For more information about signing messages, see [Signing messages](./quickstart#signing-messages) in the Unity SDK quickstart. + +## Tokens + +The game uses Tezos tokens to represent in-game items, such as weapons, armor, and power-ups. +Because these tokens are compliant with the [FA2](../architecture/tokens/FA2) standard, players can see their tokens in their wallets and in applications such as block explorers. +They could also set up a third-party platform to show and trade their tokens. + +The smart contract that manages the tokens has one [token type](https://better-call.dev/mainnet/KT1TSZfPJ5uZW1GjcnXmvt1npAQ2nh5S1FAj/tokens) for each in-game item. +For example, tokens with the ID 1 represent armor: + +![Example of a token type](/img/unity/sample-game-token-types.png) + +The contract pre-mints a supply of 1000 of each token type so tokens are available when players claim them. +When a player claims a token with the Claim Reward button and solves a captcha, the game client calls the backend, which verifies the captcha and calls the contract's `transfer` entrypoint to send one of that token type to the player's account. +This diagram shows the interaction between the game and the player's wallet: + +![A diagram of the interaction between the player's wallet and the components of the application, showing how tokens are read from the wallet information and distributed from the smart contract to the wallet](/img/unity/sample-game-architecture-play.png) + +An account can have only one of each token type, which makes the tokens similar to NFTs, but they are not NFTs because any number of accounts can have one of each token. +Therefore, they are technically fungible tokens because tokens of the same type are interchangeable, but the backend sends only one token of each type to each account. + +To see the tokens that an account has, you can check the ledger in the contract's storage. +The ledger has entries that are indexed by the account address and the token type. +For example, this ledger entry shows that account `tz1eQQnDbkTpTnu3FXix28xKdaWYRqrsZcZv` has one token of type 16: + +![The contract ledger on Better Call Dev, showing one entry](/img/unity/sample-game-ledger-entry.png) + +The contract uses standard FA2 entrypoints including `transfer`, plus other custom entrypoints for this implementation. +You can see these entrypoints on block explorers: https://better-call.dev/mainnet/KT1TSZfPJ5uZW1GjcnXmvt1npAQ2nh5S1FAj/interact. + +## User data + +The game uses the Unity SDK to get player data from the backend and work with it locally. +The [`UserDataManager.cs`](https://github.com/baking-bad/tezos-unity-game/blob/master/Assets/Scripts/Managers/UserDataManager.cs) file manages this player data, which includes: + +- The tokens that the account owns +- The player's statistics +- Information about the active game session +- The player's currently equipped equipment +- Pending rewards, which represent in-game items that the user has earned but has not received a token for yet + +Some of this information (such as the tokens that the player owns) comes from Tezos and other information (such as the player's statistics) comes from the backend. +Information about the current game session and pending rewards are non-persistent data that are stored by the Unity application. + +The `UserDataManager` class responds to [events](./reference/EventManager) such as when the user connects their wallet and then loads information from the backend and from Tezos directly. + +## Opening the sample game + +Follow these steps to open the sample game in the Unity editor: + +1. Clone the sample game repository at https://github.com/baking-bad/tezos-unity-game. + +1. In Unity Hub, click **Add > Add project from disk**. + +1. Select the repository folder and click **Add Project**. + +1. If Unity Hub prompts you to install a specific version of Unity Editor, follow the prompts to install that version. + +1. Click the project in Unity Hub to open it in Unity Editor. diff --git a/sidebars.js b/sidebars.js index 7c1e10074..4e1fa6cb2 100644 --- a/sidebars.js +++ b/sidebars.js @@ -197,6 +197,7 @@ const sidebars = { items: [ 'unity/quickstart', 'unity/scenes', + 'unity/sample-game', 'unity/prefabs', 'unity/connecting-accounts', 'unity/managing-contracts', diff --git a/static/img/unity/sample-game-architecture-play.png b/static/img/unity/sample-game-architecture-play.png new file mode 100644 index 000000000..d0a76d969 Binary files /dev/null and b/static/img/unity/sample-game-architecture-play.png differ diff --git a/static/img/unity/sample-game-architecture.png b/static/img/unity/sample-game-architecture.png new file mode 100644 index 000000000..5413e55ed Binary files /dev/null and b/static/img/unity/sample-game-architecture.png differ diff --git a/static/img/unity/sample-game-ledger-entry.png b/static/img/unity/sample-game-ledger-entry.png new file mode 100644 index 000000000..02bc1de32 Binary files /dev/null and b/static/img/unity/sample-game-ledger-entry.png differ diff --git a/static/img/unity/sample-game-token-types.png b/static/img/unity/sample-game-token-types.png new file mode 100644 index 000000000..d3526eef4 Binary files /dev/null and b/static/img/unity/sample-game-token-types.png differ diff --git a/static/img/unity/sample-game-ui.png b/static/img/unity/sample-game-ui.png new file mode 100644 index 000000000..5fb7a088c Binary files /dev/null and b/static/img/unity/sample-game-ui.png differ diff --git a/static/img/unity/unity-sample-game-authentication.png b/static/img/unity/unity-sample-game-authentication.png new file mode 100644 index 000000000..fa83f3502 Binary files /dev/null and b/static/img/unity/unity-sample-game-authentication.png differ