设计思路
使用Canvas绘制游戏场景

实现玩家控制、敌人自动生成和移动

添加射击功能和碰撞检测

设计游戏状态管理(开始、进行中、结束)

// GamePage.ets
@Entry
@Component
struct GamePage {
  @State gameStatus: string = 'ready' // ready, playing, gameover
  @State score: number = 0
  @State lives: number = 3
  private context: CanvasRenderingContext2D | null = null
  private player: Player = new Player()
  private enemies: Enemy[] = []
  private bullets: Bullet[] = []
  private gameWidth: number = 0
  private gameHeight: number = 0
  private lastTime: number = 0
  private enemySpawnCooldown: number = 500 // 敌人生成间隔(ms)
  private lastSpawnTime: number = 0
  private backgroundY: number = 0 // 背景星空的Y位置

  // 控制方向
  moveLeft: boolean = false
  moveRight: boolean = false
  shoot: boolean = false
  private lastShootTime: number = 0
  private shootCooldown: number = 300 // 射击冷却时间

  // 初始化Canvas
  initCanvas(context: CanvasRenderingContext2D, width: number, height: number) {
    this.context = context
    this.gameWidth = width
    this.gameHeight = height
    this.player.x = width / 2
    this.player.y = height - 100
// 开始游戏

  startGame() {
    this.gameStatus = 'playing'
    this.score = 0
    this.lives = 3
    this.enemies = []
    this.bullets = []
    this.player.x = this.gameWidth / 2
    this.player.y = this.gameHeight - 100
    this.gameLoop()
// 游戏主循环

  gameLoop() {
    if (this.gameStatus !== 'playing') return
    
    const currentTime = new Date().getTime()
    const elapsed = this.lastTime ? currentTime - this.lastTime : 16
    this.lastTime = currentTime
    
    this.update(elapsed)
    this.draw()
    
    requestAnimationFrame(() => this.gameLoop())
// 更新游戏状态

  update(elapsed: number) {
    // 更新背景位置(产生移动效果)
    this.backgroundY += 0.2 * elapsed
    
    // 玩家移动
    if (this.moveLeft) this.player.x -= this.player.speed * elapsed / 16
    if (this.moveRight) this.player.x += this.player.speed * elapsed / 16
    
    // 边界限制
    if (this.player.x < 30) this.player.x = 30
    if (this.player.x > this.gameWidth - 30) this.player.x = this.gameWidth - 30
    
    // 处理射击
    if (this.shoot && (currentTime - this.lastShootTime) > this.shootCooldown) {
      this.bullets.push(new Bullet(
        this.player.x,
        this.player.y,
        4,
        10,
        '#FFFF00'
      ))
      this.lastShootTime = currentTime
// 生成敌人

    if ((currentTime - this.lastSpawnTime) > this.enemySpawnCooldown) {
      this.spawnEnemy()
      this.lastSpawnTime = currentTime
// 更新敌人位置

    this.enemies.forEach((enemy, index) => {
      enemy.y += enemy.speed * elapsed / 16
      // 超出屏幕移除
      if (enemy.y > this.gameHeight) {
        this.enemies.splice(index, 1)
})

    
    // 更新子弹位置
    this.bullets.forEach((bullet, index) => {
      bullet.y -= bullet.speed * elapsed / 16
      // 超出屏幕移除
      if (bullet.y < 0) {
        this.bullets.splice(index, 1)
})

    
    // 碰撞检测 - 子弹与敌人
    for (let i = this.bullets.length - 1; i >= 0; i--) {
      for (let j = this.enemies.length - 1; j >= 0; j--) {
        const bullet = this.bullets[i]
        const enemy = this.enemies[j]
        
        if (this.checkCollision(bullet, enemy)) {
          // 移除碰撞的子弹和敌人
          this.bullets.splice(i, 1)
          this.enemies.splice(j, 1)
          this.score += 100
          
          // 增加游戏难度
          if (this.score > 0 && this.score % 500 === 0) {
            this.enemySpawnCooldown = Math.max(100, this.enemySpawnCooldown - 50)
break

}

// 碰撞检测 - 玩家与敌人

    for (let i = this.enemies.length - 1; i >= 0; i--) {
      const enemy = this.enemies[i]
      
      if (this.checkCollision(this.player, enemy)) {
        this.enemies.splice(i, 1)
        this.lives -= 1
        
        if (this.lives <= 0) {
          this.gameStatus = 'gameover'
break

}

// 碰撞检测算法

  checkCollision(obj1: GameObject, obj2: GameObject): boolean {
    const dx = obj1.x - obj2.x
    const dy = obj1.y - obj2.y
    const distance = Math.sqrt(dx  dx + dy  dy)
    return distance < (obj1.radius + obj2.radius)
// 生成敌人

  spawnEnemy() {
    const types = [
speed: 0.2, radius: 15, color: '#FF5252', score: 100 }, // 红色敌人

speed: 0.15, radius: 20, color: '#7C4DFF', score: 200 }, // 紫色敌人

speed: 0.3, radius: 10, color: '#00E676', score: 150 }, // 绿色敌人

const type = types[Math.floor(Math.random() * types.length)]

    
    const enemy = new Enemy(
      Math.random() * (this.gameWidth - 40) + 20,
      -20,
      type.speed,
      type.radius,
      type.color
    )
    
    this.enemies.push(enemy)
// 绘制游戏场景

  draw() {
    if (!this.context) return
    
    const ctx = this.context
    const width = this.gameWidth
    const height = this.gameHeight
    
    // 绘制背景
    ctx.fillStyle = '#000020'
    ctx.fillRect(0, 0, width, height)
    
    // 绘制星空背景
    ctx.fillStyle = '#FFFFFF'
    for (let i = 0; i < 100; i++) {
      const starX = Math.random() * width
      const starY = (Math.random() * height + this.backgroundY) % height
      ctx.fillRect(starX, starY, 1.5, 1.5)
// 绘制玩家

    ctx.fillStyle = this.player.color
    ctx.beginPath()
    ctx.moveTo(this.player.x, this.player.y - 20)
    ctx.lineTo(this.player.x - 15, this.player.y + 15)
    ctx.lineTo(this.player.x + 15, this.player.y + 15)
    ctx.closePath()
    ctx.fill()
    
    // 绘制敌人
    this.enemies.forEach(enemy => {
      ctx.fillStyle = enemy.color
      ctx.beginPath()
      ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI * 2)
      ctx.fill()
      
      // 敌人的"眼睛"
      ctx.fillStyle = '#000'
      ctx.beginPath()
      ctx.arc(enemy.x - 5, enemy.y - 5, 4, 0, Math.PI * 2)
      ctx.arc(enemy.x + 5, enemy.y - 5, 4, 0, Math.PI * 2)
      ctx.fill()
    })
    
    // 绘制子弹
    this.bullets.forEach(bullet => {
      ctx.fillStyle = bullet.color
      ctx.beginPath()
      ctx.arc(bullet.x, bullet.y, bullet.radius, 0, Math.PI * 2)
      ctx.fill()
    })
    
    // 绘制UI
    ctx.fillStyle = '#FFFFFF'
    ctx.font = '20px sans-serif'
    ctx.fillText(分数: ${this.score}, 20, 40)
    ctx.fillText(生命: ${this.lives}, 20, 70)
    
    // 游戏结束界面
    if (this.gameStatus === 'gameover') {
      ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'
      ctx.fillRect(0, 0, width, height)
      
      ctx.fillStyle = '#FFFFFF'
      ctx.font = 'bold 36px sans-serif'
      ctx.textAlign = 'center'
      ctx.fillText('游戏结束', width / 2, height / 2 - 30)
      ctx.font = '30px sans-serif'
      ctx.fillText(最终分数: ${this.score}, width / 2, height / 2 + 30)
      ctx.textAlign = 'start'
else if (this.gameStatus === 'ready') {

      ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'
      ctx.fillRect(0, 0, width, height)
      
      ctx.fillStyle = '#FFFFFF'
      ctx.font = 'bold 36px sans-serif'
      ctx.textAlign = 'center'
      ctx.fillText('太空射击游戏', width / 2, height / 2 - 60)
      ctx.font = '24px sans-serif'
      ctx.fillText('控制:左下按钮移动,右下按钮射击', width / 2, height / 2)
      ctx.font = '30px sans-serif'
      ctx.fillText('点击开始游戏', width / 2, height / 2 + 60)
      ctx.textAlign = 'start'
}

  build() {
    Column() {
      // 游戏画布区域
      Stack({ alignContent: Alignment.Bottom }) {
        // 游戏画布
        Canvas(this.context)
          .width('100%')
          .height('100%')
          .backgroundColor('#000000')
          .onReady(() => {
            this.context = this.context.getContext('2d') as CanvasRenderingContext2D
            const width = px2vp(this.context.width)
            const height = px2vp(this.context.height)
            this.initCanvas(this.context, width, height)
          })
          .onTouch((e) => {
            if (e.type = TouchType.Down && this.gameStatus = 'ready') {
              this.startGame()
})

        
        // 控制按钮区域
        Row() {
          // 左移按钮
          Button('←')
            .onClick(() => this.moveLeft = true)
            .onTouch((e) => {
              if (e.type === TouchType.Down) this.moveLeft = true
              else if (e.type === TouchType.Up) this.moveLeft = false
            })
            .width('33%')
            .fontSize(30)
            .backgroundColor('#555555')
          
          // 右移按钮
          Button('→')
            .onClick(() => this.moveRight = true)
            .onTouch((e) => {
              if (e.type === TouchType.Down) this.moveRight = true
              else if (e.type === TouchType.Up) this.moveRight = false
            })
            .width('33%')
            .fontSize(30)
            .backgroundColor('#555555')
          
          // 射击按钮
          Button('射击')
            .onClick(() => this.shoot = true)
            .onTouch((e) => {
              if (e.type === TouchType.Down) this.shoot = true
              else if (e.type === TouchType.Up) this.shoot = false
            })
            .width('34%')
            .fontSize(30)
            .backgroundColor('#FF0000')
.width('100%')

        .height(60)
        .backgroundColor('#333333')
.width('100%')

      .height('100%')
.width('100%')

    .height('100%')
    .onKeyEvent((e) => {
      // 键盘控制,方便模拟器使用
      if (e.keyCode === 1004) { // 左箭头
        if (e.action === 0) this.moveLeft = true
        else if (e.action === 1) this.moveLeft = false
else if (e.keyCode === 1003) { // 右箭头

        if (e.action === 0) this.moveRight = true
        else if (e.action === 1) this.moveRight = false
else if (e.keyCode === 1000) { // 空格

        if (e.action === 0) this.shoot = true
        else if (e.action === 1) this.shoot = false
})

}

// 游戏对象基类
class GameObject {
  x: number
  y: number
  speed: number
  radius: number
  color: string
  
  constructor(x: number, y: number, speed: number, radius: number, color: string) {
    this.x = x
    this.y = y
    this.speed = speed
    this.radius = radius
    this.color = color
}

// 玩家类
class Player extends GameObject {
  constructor() {
    super(0, 0, 0.35, 15, '#4FC3F7')
}

// 敌人类
class Enemy extends GameObject {
// 子弹类

class Bullet extends GameObject {

代码解析

游戏结构
GamePage组件:游戏的入口组件,包含所有游戏逻辑和渲染

Canvas画布:负责绘制游戏场景中的所有元素

控制按钮:实现玩家飞船的左右移动和射击功能

核心类
GameObject:所有游戏对象的基类

Player:玩家飞船类

Enemy:敌人类

Bullet:子弹类

关键功能实现
游戏循环:使用requestAnimationFrame实现流畅的游戏循环

碰撞检测:采用圆形边界检测算法,高效准确

背景绘制:动态星空背景增加场景感

难度递增:随着得分增加,敌人生成频率提高

游戏状态管理:开始、进行中、游戏结束三种状态

控制方式
触摸控制:左下按钮移动,右下按钮射击

键盘支持:模拟器可使用方向键和空格键(开发阶段实用)

触摸事件响应:区分按下和抬起动作实现更自然控制

游戏特点
三种不同属性的敌人:不同颜色、大小和移动速度

生命值系统:玩家有3条生命

动态难度:随着分数提高,敌人生成速度增加

美观的UI:包括分数显示、生命指示器等

使用说明
在开发环境中创建一个ArkUI项目

将代码复制到GamePage.ets文件中

添加必要的样式和配置

在模拟器或设备上运行

这个简单的射击游戏展示了鸿蒙游戏开发的基本思路,包括图形渲染、状态管理、用户交互等核心功能。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐