I Built A Web3 Clone Of The Hill Climb Game Using Unity, ThirdWeb GamingKit, and ContractKit.
Tags: solidity, web3, ethereum, webdev
Published: 2023-02-14 00:35:05

---
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxg5cjgwclasbyakb4je.gif
---

{% embed https://youtu.be/dSx8h3uIiQE %}

Over the past month, I participated in the ThirdWeb hackathon in which I set myself the challenge of building a web3 clone of The Hill Climb game using both ThirdWeb GamingKit and ContractKit. I was so excited about the opportunity to explore the different ways in which the Ethereum blockchain can be utilized to create games with unique features.

In this post, I will analyze the different parts of the game I built over the past month. I'll explain how I used ThirdWeb GamingKit and ContractKit to customize different game parts. Finally, I'll show you how you can customize this game to build your own decentralized version of The Hill Climb.

## Model Analysis <a name="introduction"></a>   

Model Analysis is a method that allows us to comprehend the prerequisites of specific software, in this case, a game, and how they interact with one another. This technique gives us a high-level overview of what must be done to build the game and a plan for developing it.

### Use Case Diagram

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hvesi8mo6n4l7felhqvd.png">
  <figcaption>Use-case diagram of the game (Image by author).</figcaption>
</figure>

The Use Case Diagram outlines the interactions between a user and the software. It illustrates the user's activities and how they are interconnected. A player progresses through the game by completing a series of activities represented by modules/panels. Each panel signifies a different game stage, and the player must complete all tasks within each panel to move on to the next one.

This use case diagram begins when a user playing the game triggers the server to run and the game has loaded. A user connects their wallet, selects a car and level, and then begins playing the game. As they play, they may upgrade their vehicle.

- Steps:

1. Go to the dashboard.
2. Claim an NFT to grant access to the game.
1. Go to the main menu of the game.
2. Connect the wallet.
3. Select a level.
4. Select a car.
5. Upgrade your car.
6. The game is loaded.

### Activity Diagram

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0g8sk3t57jn5xc87hao.png">
  <figcaption>Activity diagram of the game (Image by author).</figcaption>
</figure>

An activity Diagram is a great tool to understand better the order of activities that need to be performed to complete a task and the relationships between them. It is mainly used to identify potential areas for improvement.

You can comprehensively understand the project by reading the above activity diagram from the top right corner.

### Architecture

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vn70fvt0r0b14lxwm7jn.png">
  <figcaption>Game architecture (Image by author).</figcaption>
</figure>

## ThirdWeb Integration

This section describes how ThirdWeb was integrated into this game. 

### Smart Contract

Using the ThirdWeb ContractKit CLI, we can quickly write and deploy secure contracts by only focusing on the smart contract itself. The code snippet below represents a custom ERC721 Drop contract.

```cs
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "@thirdweb-dev/contracts/base/ERC721Drop.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";

contract Contract is ERC721Drop, Permissions {
    // An array of notes.
    mapping(uint256 => string) public notes;

    constructor(
        string memory _name,
        string memory _symbol,
        address _royaltyRecipient,
        uint128 _royaltyBps,
        address _primarySaleRecipient
    )
        ERC721Drop(
            _name,
            _symbol,
            _royaltyRecipient,
            _royaltyBps,
            _primarySaleRecipient
        )
    {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    /**
     *  A function that allows writing notes to the owner of the NFT.
     */
    function writeNote(uint256 _tokenId, string memory _msg) public {
        require(
            msg.sender == ownerOf(_tokenId),
            "You are not the token owner."
        );
        notes[_tokenId] = _msg;
    }

    /**
     *  A function that allows notes update only to holders of the NFT that has the `DEFAULT_ADMIN_ROLE` permission.
     */
    function updateNote(
        uint256 _tokenId,
        string memory _msg
    ) public onlyRole(DEFAULT_ADMIN_ROLE) {
        notes[_tokenId] = _msg;
    }
}
```

The code shows that only the owner of a given NFT has permission to write, update, and read notes for a given token.

The reason why I chose the ERC721 Drop contract is because of the following features:

- Batch uploading because it saves time and gas fees on the upload process.
- Set up robust claiming options for claiming these tokens from users, like setting up an allow list, etc.
- Great for PFP projects like our game in this case.

For more info on how to build and deploy this contract, you can refer to the following repo:

{% embed https://github.com/hill-climb/smart-contract %}

### The Game

The game utilizes Unity and the ThirdWeb GamingKit to create a powerful unique web3 gaming experience. This game was developed in Unity version `2021.15.1f` because it is the only existing version on my PC. So, make sure to have this version to build and deploy the game. For more info on how to build and deploy the game, please refer to the following repo:

{% embed https://github.com/hill-climb/game %}

## Unity

## Scenes

At the moment of writing this guide, there are four playable levels and menu scenes. Double-click on a scene to start your edit.

### Levels

When developing a game, you must consider the different levels a player will progress through. Each level can have similar game objects and components to maintain continuity throughout the game. However, you will also need to customize the terrain heights for each level; this is where the 2D Terrain Editor comes in handy. 

To create a new level, you only need to customize terrain heights for each level. You can use the 2D Terrain Editor for this purpose. This tool can spawn coins, fuels, and other items randomly. 

The Input component is attached to the Canvas and automatically switches between the keyboard and touch buttons on each platform.

### Menus

The main menu system is a crucial part of the game, providing access to all necessary features and allowing us to navigate easily through the game. This system is based on activating and deactivating menu panels, which are stored under empty game objects.

As you can notice, the main menu consists of five different panels. If you want to switch to a particular menu panel, it can be activated or deactivated accordingly by right-clicking on the intended panel and clicking `Toggle Active State`.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6dllgiljldpz0ev5tx41.png">
  <figcaption>Toggle active state for a given component (Image by author).</figcaption>
</figure>

Similarly, you can click on a particular panel and check the box that is left aligned with the panel's name in the inspector.

The components of each panel are fully customizable; you can customize different properties of objects (e.g., color, width, height, script, etc.) under the currently activated panel.

### 1. Main Menu

The Main Menu is the first thing you see when you open the MainMenu game scene. It's activated by default, so you can connect your wallet and start playing. The Main Menu is where you'll find all the options and settings for the game.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hkn3i371odbrkoban1uw.PNG">
  <figcaption>Main menu panels (Image by author).</figcaption>
</figure>

This panel has two primary components: `Connected` and `Disconnected`. These components are programmatically activated and deactivated based on the wallet connection status. On a successful attempt, the `Connected` component will be activated, and the `Disconnected` component will be deactivated. This provides us with an easy way to check the wallet connection status.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5tixpwo7tf7c3ifnxt9w.PNG">
  <figcaption>The `Connected` component is activated, and the `Disconnected` component is deactivated (Image by author).</figcaption>
</figure>

Creating a new input component is pretty straightforward. To create a new input component, you can copy, paste, and customize it to your liking.

### 2. Settings Menu

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7269rffo0ojp2ew5fux.PNG">
  <figcaption>Settings Menu (Image by author).</figcaption>
</figure>

The settings menu is where you gain complete control over the gaming experience. It provides access to various features and settings so that you can tailor their game as much as possible.

For example, if you want to use a particular feature in the game, you can find it in the settings menu and activate it with just a few clicks. Similarly, you can customize your experience by enabling or disabling certain graphics options from here too. Using this menu system, you can control your gaming experience and customize it according to your needs and preferences.

Another example is changing the background music's volume by sliding the indicator left or right like a sliding window. You can turn off the music and sound effects to enjoy a more peaceful atmosphere while playing. You also have the opportunity to adjust the quality of each pixel displayed on the screen.

Creating a new component is pretty straightforward. To create a new component, you can copy, paste, and customize it.

### 3. Level Select Menu

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ft77fjfkt4c9r9u0yeun.PNG">
  <figcaption>Levels select Menu (Image by author).</figcaption>
</figure>

The level select menu is easy to use and navigate. You click the LevelSelect `GameObject` icon on your screen and scroll down to the item icons in the inspector, where you can choose from four available levels. These levels are ordered from left to right according to their length, price, and terrain difficulty. 

If you want to add a new level, you can click the `plus` icon in the inspector and drag and drop a level sprite onto your screen. After that, you can change its animation, sound, and icons as needed. 

### 4. Car Select Menu

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdestst3qndl10rpaaww.PNG">
  <figcaption>Cars select Menu (Image by author).</figcaption>
</figure>

This panel is a powerful tool that allows you to add new cars, control animation, and more. This Menu is similar to the previous one.

### 5. Upgrade Menu

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3797ehvmagk9ysrljuh2.PNG">
  <figcaption>Cars select Menu (Image by author).</figcaption>
</figure>

The upgrade menu is where you can customize the recently selected car. You can adjust the engine, fuel, wheels, and speed levels.

### Creating A New Menu Panel

You can also create a new panel using the built-in Empty GameObject in Unity. To do so, select  `GameObject > Create Empty` under the canvas.

This will create a new empty GameObject that you can rename to whatever you like, `YourCustomPanelName`.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7etafwtam7y5a114rk55.PNG">
  <figcaption>Custom Component (Image by author).</figcaption>
</figure>

As the object is invisible, you will need to add a Canvas Renderer to it so that we can see something in the scene view that lives inside the canvas. This is not, however, related to the animation states. It simply gives us an on-screen representation of the panel.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6920wvg741dhr443wpvy.png">
  <figcaption>Create a canvas renderer (Image by author).</figcaption>
</figure>

After adding the Canvas Renderer component, you can add an Image component. The Image component provides many options for customizing the look and feel of your image. You can choose from a variety of presets or customize the settings yourself.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/est2bf7vzihut1tll23v.png">
  <figcaption>Image Component (Image by author).</figcaption>
</figure>

The image component is now visible in the scene. To attach a sprite to that component, drag the image from the Project window into the inspector.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnv7y73zyir0delv6e4a.PNG">
  <figcaption>Attach an image to the image component (Image by author).</figcaption>
</figure>

If you want to take your panel user experience to the next level, you can even attach a script to the component that controls its behavior based on custom logic. This means you have complete control over how your panel looks and behaves.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqe9pldbhisr335o1yyu.png">
  <figcaption>A script component is attached (Image by author).</figcaption>
</figure>

Now, you can start adding your custom panel components children. You can take inspiration from existing panels.

### Switch between panels

You can attach a button to the current panel to switch between panels. To make that button switch to the next one, you can add a runtime event under the `OnClick` header by clicking on the plus icon. Now, you must drag and drop the canvas into the `GameObject` placeholder.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwa94jzok9aaxystesm0.png">
  <figcaption>Add a button to a component (Image by author).</figcaption>
</figure>

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/at0ya7zthvisticslcgj.png">
  <figcaption>Add a canvas to a function (Image by author).</figcaption>
</figure>

Now you can attach a custom function available in the game, which is `true` in this case, that accepts a GameObject.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5ixnp8ppzm9l73f8zdw.png">
  <figcaption>Choose a button function (Image by author).</figcaption>
</figure>

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p85ane6zt35n55603x7z.PNG">
  <figcaption>Add hide/show panels functions to a button (Image by author).</figcaption>
</figure>

The game object represents the next panel. This function will display the next panel. Similarly, to hide the current panel, drag and drop the current panel as an argument to `setFalse`. 

## Initial State

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvaffbw0g7bfkmzkkbvw.PNG">
  <figcaption>Canvas Initial State (Image by author).</figcaption>
</figure>

When you first open the canvas, all of the values for the game's initial states are present there. You can control the game variables' initial values under the inspector's canvas script. The initial states are important because they set the starting point for the player's actions. If you want to change how the game plays, you can do so by altering the initial states.

## Create A New Car

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y0ri05xlgviislspiugb.PNG">
  <figcaption>Create a new car (Image by author).</figcaption>
</figure>

Creating a new car is relatively easy. To create a new one, copy and paste a car prefab, and change its objects under the Hierarchy tab. To enhance your car's performance, open the `Body` object in the Hierarchy tab. Then select any of these components from the list to customize them to your car body. 

To change the car body image, Click the `VehicleSprite` object in the Hierarchy tab. Now you need to specify a car image.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iy7qoiyiiai19axot0a2.png">
  <figcaption>Change car sprite (Image by author).</figcaption>
</figure>

Now, you need to change the bounding volume of the car body collider. To do so, click the `Collider` object in the Hierarchy tab. Now you need to click edit collider in the inspector to match the car sprite recently added. 

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0s9p5bd4ab80dngxinvp.png">
  <figcaption>Change car bounding volume (Image by author).</figcaption>
</figure>

The Dead trigger component controls when the lost state is set. If the top of the car touches the ground, 
you will lose the game.

Similarly, you can customize the remaining components of the car. After changing all these properties to your liking, your new vehicle is ready to be used in your game!

## Resources

{% embed https://github.com/hill-climb %}