DEV Community

HarmonyOS
HarmonyOS

Posted on

Building Space Shooter with ArkTS-3

Read the original article:Building Space Shooter with ArkTS-3

Introduction

Hello, everybody. We have implemented most parts of our application in previous parts. In this part, we will learn how to move our ship. If you haven't seen previous parts, check them here. Part 1 - Part 2

Time to show our moves.

Move

We will move our ship in the horizontal direction.

  • Modify the ship.
moveRL(val: number) {
  this.leftTop.x = Math.max(
    0, Math.min(this.canvasWidth - this.pixelSize * 5, this.leftTop.x + val)
  )
}
Enter fullscreen mode Exit fullscreen mode
  • Now the Index.ets. We will add the crown gesture. This requires a real device, but don't worry. I got you.
@Entry
@Component
struct Index {
  @State game: Game = new Game()

  build() {
    Stack() {
      ...
    }
    .onDigitalCrown((event: CrownEvent) => {
      this.game.userShip?.moveRL(event.degree / 2)
    })
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Let's also add a touch gesture to move our ship. We will divide the page vertically into two parts, and each part will move the ship in its respective direction.
import Game from '../viewmodel/Game'

@Entry
@Component
struct Index {
  @State game: Game = new Game()

  build() {
    Stack() {
      ...

      Row() {
        Blank().layoutWeight(1).height('100%')
          .gesture(
            LongPressGesture({ repeat: true, duration: 5 })
              .onAction((event: GestureEvent) => {
                if (event && event.repeat) {
                  this.game.userShip?.moveRL(-1)
                }
              })
          )
        Blank().layoutWeight(1).height('100%')
          .gesture(
            LongPressGesture({ repeat: true, duration: 5 })
              .onAction((event: GestureEvent) => {
                if (event && event.repeat) {
                  this.game.userShip?.moveRL(1)
                }
              })
          )
      }
    }
    .onDigitalCrown((event: CrownEvent) => {
      this.game.userShip?.moveRL(event.degree / 2)
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Game Over

We do not have a way to restart the game yet. We will use a custom pop-up to show the final score and restart the game when it is closed.

Add GameOverPopup under the components directory.

@Component
export default struct WinLosePopup {
  @Link showPopup: boolean;
  @Require score: number;

  build() {
    Column() {
      Text('GAME OVER')
        .fontColor(Color.Red)
      Text(`Score: ${this.score}`)
        .fontColor(Color.Grey)

      Blank().size({ height: 8 })

      Button() {
        SymbolGlyph($r('sys.symbol.arrow_clockwise'))
          .fontColor([Color.White])
      }
      .size({
        width: '100%',
        height: 32
      })
      .onClick(() => {
        this.showPopup = false
      })
    }
    .width('70%')
    .backgroundColor(Color.White)
    .padding(16)
  }
}
Enter fullscreen mode Exit fullscreen mode

Modify Index and open a pop-up when he game ends.

Set parameters and pop-up builder.

@State @Watch('gameOver') game: Game = new Game()
@State pop: boolean = false;

gameOver() {
  this.pop = this.game.lives === 0
}

@Builder
gameOverPopupBuilder() {
  GameOverPopup({
    showPopup: this.pop,
    score: this.game.score
  })
}
Enter fullscreen mode Exit fullscreen mode

Bind it.

build() {
  Stack() {
    ...
  }
  .bindPopup(this.pop, {
    builder: this.gameOverPopupBuilder(),
    autoCancel: false,
    onStateChange: (e) => {
      if (!e.isVisible) {
        this.game.restart()
      }
    },
  })
}
Enter fullscreen mode Exit fullscreen mode

Let's add the restart function.

restart() {
  this.canvasContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
  this.enemies = []
  this.bullets = []
  this.score = 0
  this.lives = 3

  this.userShip = new Ship(
    this.canvasWidth, this.canvasHeight, this.PIXELSIZE,
    ShipType.USER, this.FRAMECOUNT
  )

  this.intervalId = setInterval(() => {
    this.iterateGame()
  }, 1000 / this.FRAMECOUNT) // 32 times per second
}
Enter fullscreen mode Exit fullscreen mode

Stars

The space is full of stars. Let's see them. We will use the Particle component.

struct Index {
  ...

  build() {
    Stack() {
      Particle({
        particles: [
          {
            emitter: {
              particle: {
                type: ParticleType.POINT,
                config: {
                  radius: 1,
                },
                count: -1,
                lifetime: 3000,
              },
              emitRate: 10,
              shape: ParticleEmitterShape.CIRCLE,
              position: [0, 0],
            },
            velocity: {
              speed: [30, 60],
              angle: [90, 90]
            }
          }
        ]
      }).size({ width: '100%', height: '100%' })

      ...
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

We did it, folks. We have created the Space Shooter game. Time to play and enjoy.

See you all in new adventures. :)

~ Fortuna Favet Fortibus

Written by Mehmet Karaaslan

Top comments (0)