DEV Community

HarmonyOS
HarmonyOS

Posted on • Edited on

Building 2048 with ArkTS 1

Read the original article:Building 2048 with ArkTS 1

Let’s build with Arkts

Introduction

Hello, everyone! In this article, we’ll take an exciting journey to create the classic 2048 game using ArkTS and ArkUI. We’ll explore its core mechanics, design principles, and step-by-step implementation using these powerful technologies. Let’s dive in and bring 2048 to life!

Let’s create the UI

Defining Colors

To have the best game, we need colors and we need them now. Thus, we will use the “color.json” file to define them.

{
  "name": "color_background",
  "value": "#faf8ef"
},
{
  "name": "color_board",
  "value": "#776e65"
},
{
  "name": "color_empty",
  "value": "#ccc0b3"
},
{
  "name": "color_2",
  "value": "#eee4da"
},
{
  "name": "color_4",
  "value": "#ede0c8"
},
{
  "name": "color_8",
  "value": "#f2b179"
},
{
  "name": "color_16",
  "value": "#f59563"
},
{
  "name": "color_32",
  "value": "#f67c5f"
},
{
  "name": "color_64",
  "value": "#f65e3b"
},
{
  "name": "color_128",
  "value": "#edcf72"
},
{
  "name": "color_256",
  "value": "#edcc61"
},
{
  "name": "color_512",
  "value": "#edc850"
},
{
  "name": "color_1024",
  "value": "#edc53f"
},
{
  "name": "color_2048",
  "value": "#edc22e"
}
Enter fullscreen mode Exit fullscreen mode

Adding Game Board

We will use the Grid component with 4 rows and 4 columns to create the game board.

@Entry
@Component
struct Index {
  build() {
    RelativeContainer() {
      Grid() {
      }
      // 70% to fit the square board into circular watch
      .size({ width: '70%', height: '70%' })
      // center the board
      .alignRules({
        center: { anchor: '__container__', align: VerticalAlign.Center },
        middle: { anchor: '__container__', align: HorizontalAlign.Center }
      })
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr 1fr')
       // styles, change as you wish
      .columnsGap(4)
      .rowsGap(4)
      .padding(4)
      .backgroundColor($r('app.color.color_board'))
      .borderRadius(4)
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Empty Game Board

Adding Tile

We will use the power of ArkTS to create a custom component to define our tile.

  • But first, we will define game numbers to make our code more readable and place it under the model directory.
export enum GameNum {
  _0 = 0, // we will use 0 for empty cells
  _2 = 2,
  _4 = 4,
  _8 = 8,
  _16 = 16,
  _32 = 32,
  _64 = 64,
  _128 = 128,
  _256 = 256,
  _512 = 512,
  _1024 = 1024,
  _2048 = 2048
}

Enter fullscreen mode Exit fullscreen mode
  • Let’s define our tile: Create the components directory and place GameTile under it.
import { GameNum } from '../model/GameNum';

@Component
export default struct GameTile {
  @Require num: GameNum;

  build() {
    RelativeContainer() {
      if (this.num != GameNum._0) {
        Text(this.num.toString())
          .fontColor(this.textColor())
          .fontSize(14)
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
      }
    }
    .height('100%')
    .width('100%')
    .borderRadius(4)
    .backgroundColor(this.cellColor())
  }

  // set text color based on the GameNum
  textColor() {
    return this.num < GameNum._8 ? Color.Black : Color.White
  }

  // Set cell color based on the GameNum
  cellColor() {
    switch (this.num) {
      case GameNum._0:
        return $r('app.color.color_empty')
      case GameNum._2:
        return $r('app.color.color_2')
      case GameNum._4:
        return $r('app.color.color_4')
      case GameNum._8:
        return $r('app.color.color_8')
      case GameNum._16:
        return $r('app.color.color_16')
      case GameNum._32:
        return $r('app.color.color_32')
      case GameNum._64:
        return $r('app.color.color_64')
      case GameNum._128:
        return $r('app.color.color_128')
      case GameNum._256:
        return $r('app.color.color_256')
      case GameNum._512:
        return $r('app.color.color_512')
      case GameNum._1024:
        return $r('app.color.color_1024')
      case GameNum._2048:
        return $r('app.color.color_2048')
    }
  }
}

Enter fullscreen mode Exit fullscreen mode
Update Index.ets

Perfect tiles for the perfect game

Adding Gestures

So far, we have created the user interface. Now we will add gestures to move tiles. We will use the PanGesture to catch swipe actions.

RelativeContainer() {
   // ...
}
.height('100%')
.width('100%')
.gesture(PanGesture({ distance: 50 }).onActionEnd((event) => {
  const verticalDistance = event.offsetY;
  const horizontalDistance = event.offsetX;

  if (Math.abs(verticalDistance) > Math.abs(horizontalDistance)) { // vertical movement
    if (verticalDistance < 0) { // swipe up
      console.log('up');
    } else { // swipe down
      console.log('down')
    }
  } else { // horizontal movement
    if (horizontalDistance < 0) { // swipe left
      console.log('left')
    } else { // swipe right
      console.log('right')
    }
  }
}))
Enter fullscreen mode Exit fullscreen mode

Conlusion

Every magic starts with a simple idea and some courage. So far, we have created the user interface for our game. We will continue with the game logic in part 2. See you soon. :)

Written by Mr.Karaaslan

Top comments (0)