当前位置: 首页 > news >正文

HarmonyOS 诗词填空游戏开发实战教程(非AI生成 提供源代码和演示视频)

HarmonyOS 诗词填空游戏开发实战教程

效果视频

鸿蒙版古诗词填空游戏

前言

本教程将手把手教你开发一个古诗词填空小游戏,适合 HarmonyOS 初学者跟随学习。完成本教程后,你将学会:

  • 如何组织 HarmonyOS 项目结构
  • 如何管理游戏数据
  • 如何实现游戏界面和交互逻辑
  • 如何配置页面路由

开发环境要求:

  • DevEco Studio(推荐最新版本)
  • HarmonyOS SDK 6.0.0 或以上
  • 基础的 TypeScript 知识

预计用时: 30-45 分钟

让我们开始吧!


第一步:创建项目和目录结构

1.1 打开你的 HarmonyOS 项目

假设你已经有一个名为 tiankong 的 HarmonyOS 项目。如果没有,请先用 DevEco Studio 创建一个新项目。

1.2 创建所需目录

我们需要创建两个新文件夹来组织代码:

方法一:使用 DevEco Studio

  1. 在项目视图中,找到 entry/src/main/ets/ 目录
  2. 右键点击 ets 文件夹 → NewDirectory
  3. 输入 data,回车创建
  4. 再次右键点击 ets 文件夹 → NewDirectory
  5. 输入 pages/game(会自动创建两级目录),回车创建

方法二:使用命令行

打开终端(Terminal),执行:

cd 你的项目路径/tiankong
mkdir -p entry/src/main/ets/data
mkdir -p entry/src/main/ets/pages/game

验证结果:

你的目录结构应该如下:

tiankong/
└── entry/└── src/└── main/└── ets/├── data/           ← 新建(存放数据文件)├── pages/│   ├── game/      ← 新建(存放游戏页面)│   └── Index.ets├── entryability/└── entrybackupability/

第二步:创建诗词数据库

现在我们来创建第一个文件,存放所有的诗词数据。

2.1 创建文件

  1. 在 DevEco Studio 中,右键点击 entry/src/main/ets/data 文件夹
  2. 选择 NewArkTS File
  3. 输入文件名:CultureData(不需要 .ets 后缀)
  4. 点击确定

2.2 编写数据结构

打开刚创建的 CultureData.ets 文件,先定义数据类型:

/** 诗词园地数据*/// 定义文化类别:诗词、歇后语、故事
export type CultureCategory = 'poetry' | 'idiom' | 'story'// 定义动物类型:鸡、鸭、鹅
export type AnimalType = 'chicken' | 'duck' | 'goose'// 定义文化数据项的结构
export interface CultureItem {id: string              // 唯一标识category: CultureCategory  // 类别animalType: AnimalType     // 动物类型title: string           // 标题(诗词名)content: string         // 内容(诗词正文)author?: string         // 作者dynasty?: string        // 朝代explanation?: string    // 解释说明tags: string[]          // 标签isFavorite: boolean     // 是否收藏
}

💡 知识点:

  • export 表示导出,其他文件可以导入使用
  • type 定义类型别名
  • interface 定义对象的结构
  • ? 表示可选属性

2.3 创建数据库类

继续在同一个文件中添加数据库类:

export class CultureDatabase {/*** 获取所有诗词数据*/public static getPoetryData(): CultureItem[] {return [// 第一首诗:咏鹅{id: 'poetry_goose_001',category: 'poetry',animalType: 'goose',title: '咏鹅',author: '骆宾王',dynasty: '唐',content: '鹅,鹅,鹅,曲项向天歌。\n白毛浮绿水,红掌拨清波。',explanation: '这首诗是骆宾王七岁时所作,通过对鹅的形态、颜色、动作的描写,生动形象地展现了白鹅的美丽和可爱。',tags: ['骆宾王', '咏物诗', '儿童诗', '经典'],isFavorite: false},// 第二首诗:画鸡{id: 'poetry_chicken_001',category: 'poetry',animalType: 'chicken',title: '画鸡',author: '唐寅',dynasty: '明',content: '头上红冠不用裁,满身雪白走将来。\n平生不敢轻言语,一叫千门万户开。',explanation: '这首诗通过描绘大公鸡的形象,赞美了它的美丽和报晓的功劳。"一叫千门万户开"突出了公鸡报晓的重要作用。',tags: ['唐寅', '咏物诗', '寓意深刻'],isFavorite: false},// 第三首诗:惠崇春江晚景{id: 'poetry_duck_001',category: 'poetry',animalType: 'duck',title: '惠崇春江晚景',author: '苏轼',dynasty: '宋',content: '竹外桃花三两枝,春江水暖鸭先知。\n蒌蒿满地芦芽短,正是河豚欲上时。',explanation: '"春江水暖鸭先知"成为千古名句,生动描绘了鸭子在春江中嬉戏的情景。',tags: ['苏轼', '题画诗', '春景', '名句'],isFavorite: false}// 💡 这里可以继续添加更多诗词// 完整版包含17首诗词,为了简洁,这里只展示3首// 你可以从文末的完整代码中复制其余诗词]}/*** 获取所有歇后语数据(暂时返回空数组)*/public static getIdiomData(): CultureItem[] {return []}/*** 获取所有成语故事数据(暂时返回空数组)*/public static getStoryData(): CultureItem[] {return []}/*** 获取所有文化数据*/public static getAllCultureData(): CultureItem[] {let result: CultureItem[] = []result = result.concat(CultureDatabase.getPoetryData())result = result.concat(CultureDatabase.getIdiomData())result = result.concat(CultureDatabase.getStoryData())return result}/*** 根据分类获取数据*/public static getDataByCategory(category: CultureCategory): CultureItem[] {const allData = CultureDatabase.getAllCultureData()return allData.filter(item => item.category === category)}/*** 根据动物类型获取数据*/public static getDataByAnimalType(animalType: AnimalType): CultureItem[] {const allData = CultureDatabase.getAllCultureData()return allData.filter(item => item.animalType === animalType)}
}

💡 知识点:

  • static 表示静态方法,可以直接通过类名调用,无需创建实例
  • filter() 是数组方法,用于筛选符合条件的元素
  • concat() 用于合并数组

2.4 保存文件

Ctrl + S(Windows)或 Cmd + S(Mac)保存文件。

📝 小贴士: 完整的诗词数据(17首)请参考文末附录,现在继续下一步。


第三步:创建游戏数据管理器

这个文件负责把诗词转换成游戏题目。

3.1 创建文件

  1. 右键点击 entry/src/main/ets/data 文件夹
  2. 选择 NewArkTS File
  3. 输入文件名:GameDataManager
  4. 点击确定

3.2 定义题目数据结构

打开 GameDataManager.ets,先导入数据库和定义题目结构:

/** 游戏数据管理器*/import { CultureDatabase, CultureItem } from './CultureData'/*** 游戏题目接口*/
export interface GameQuestion {id: string              // 题目IDtype: 'idiom' | 'proverb' | 'poetry'  // 题目类型question: string        // 题目内容(带填空的诗句)answer: string          // 正确答案options?: string[]      // 四个选项hint: string            // 提示信息animalType: string      // 动物类型
}

3.3 创建游戏数据管理器类

继续添加核心逻辑:

/*** 游戏数据管理器*/
export class GameDataManager {/*** 获取古诗词填空游戏题目*/static getPoetryQuestions(): GameQuestion[] {// 1. 获取所有诗词数据const poetry = CultureDatabase.getPoetryData()const questions: GameQuestion[] = []// 2. 遍历每首诗poetry.forEach(poem => {// 定义要查找的家禽名称(按长度从长到短,避免误匹配)const animalNames = ['公鸡', '母鸡', '雏鸡', '小鸡', '鹅儿', '鸭儿', '鸡', '鸭', '鹅']// 3. 查找诗句中是否包含这些名称for (const animalName of animalNames) {if (poem.content.includes(animalName)) {// 4. 创建填空题:将家禽名称替换为 ____const index = poem.content.indexOf(animalName)const blankContent = poem.content.substring(0, index) +'____' +poem.content.substring(index + animalName.length)// 5. 生成答案和选项let correctOption = animalNamelet options: string[] = []// 将复合词简化为基础词作为答案if (animalName.includes('鸡')) {correctOption = '鸡'options = ['鸡', '鸭', '鹅', '雀']} else if (animalName.includes('鸭')) {correctOption = '鸭'options = ['鸭', '鸡', '鹅', '雁']} else if (animalName.includes('鹅')) {correctOption = '鹅'options = ['鹅', '鸡', '鸭', '雁']} else {correctOption = animalNameoptions = [animalName, '鸡', '鸭', '鹅']}// 6. 打乱选项顺序options = GameDataManager.shuffleArray(options)// 7. 确保正确答案在选项中if (!options.includes(correctOption)) {options[options.length - 1] = correctOption}// 8. 创建题目对象questions.push({id: `${poem.id}_${animalName}`,type: 'poetry',question: `${poem.title}》\n${poem.author ? `[${poem.dynasty}] ${poem.author}` : ''}\n\n${blankContent}`,answer: correctOption,options: options,hint: poem.explanation ? poem.explanation.substring(0, 50) + '...' : '这是一首咏物诗',animalType: poem.animalType})// 每首诗只生成一个题目break}}})// 9. 打乱题目顺序,并限制为8道题return GameDataManager.shuffleArray(questions).slice(0, 8)}/*** 打乱数组(Fisher-Yates 洗牌算法)*/private static shuffleArray<T>(arr: T[]): T[] {const result: T[] = []// 复制数组for (let i = 0; i < arr.length; i++) {result.push(arr[i])}// 打乱顺序for (let i = result.length - 1; i > 0; i--) {const j = Math.floor(Math.random() * (i + 1))const temp = result[i]result[i] = result[j]result[j] = temp}return result}
}

💡 知识点:

  • substring() 截取字符串
  • includes() 检查字符串是否包含某个子串
  • indexOf() 查找子串位置
  • slice() 截取数组
  • Fisher-Yates 算法是经典的随机打乱算法

3.4 保存文件

Ctrl + S 保存。


第四步:创建游戏界面

这是最重要的一步,我们将创建游戏的用户界面。

4.1 创建文件

  1. 右键点击 entry/src/main/ets/pages/game 文件夹
  2. 选择 NewArkTS File
  3. 输入文件名:PoetryFillGame
  4. 点击确定

4.2 导入必要的模块

打开 PoetryFillGame.ets,首先导入需要的模块:

/** 古诗词填空游戏*/import { router } from '@kit.ArkUI'
import { GameDataManager, GameQuestion } from '../../data/GameDataManager'
import { promptAction } from '@kit.ArkUI'

4.3 创建组件和状态

添加组件定义和状态管理:

@Entry
@Component
struct PoetryFillGame {// 状态变量@StorageProp('app_is_dark_mode') isDarkMode: boolean = false  // 是否深色模式@State questions: GameQuestion[] = []      // 题目列表@State currentIndex: number = 0            // 当前题目索引@State selectedAnswer: string = ''         // 用户选择的答案@State showHint: boolean = false           // 是否显示提示@State score: number = 0                   // 得分@State answered: boolean = false           // 是否已答题@State isCorrect: boolean = false          // 答案是否正确// 组件即将出现时调用aboutToAppear() {this.loadQuestions()}// 加载题目private loadQuestions() {this.questions = GameDataManager.getPoetryQuestions()console.info('PoetryFillGame', `加载了 ${this.questions.length} 道题目`)}// 获取当前题目private getCurrentQuestion(): GameQuestion | null {if (this.currentIndex < this.questions.length) {return this.questions[this.currentIndex]}return null}// 检查答案private checkAnswer(option: string) {if (this.answered) return  // 已经答过题了,不能再答this.selectedAnswer = optionthis.answered = trueconst question = this.getCurrentQuestion()if (question) {this.isCorrect = option === question.answerif (this.isCorrect) {this.score++  // 答对了,加分}}}// 下一题private nextQuestion() {if (this.currentIndex < this.questions.length - 1) {// 还有下一题this.currentIndex++this.selectedAnswer = ''this.answered = falsethis.showHint = false} else {// 已经是最后一题,显示结果this.showGameOver()}}// 显示游戏结束对话框private async showGameOver() {const totalQuestions = this.questions.lengthconst percentage = Math.round((this.score / totalQuestions) * 100)promptAction.showDialog({title: '游戏结束',message: `你答对了 ${this.score}/${totalQuestions} 题\n正确率:${percentage}%`,buttons: [{ text: '再玩一次', color: '#4CAF50' },{ text: '返回', color: '#666666' }]}).then((data) => {if (data.index === 0) {// 再玩一次this.currentIndex = 0this.score = 0this.selectedAnswer = ''this.answered = falsethis.showHint = falsethis.loadQuestions()} else {// 返回首页router.back()}})}// 构建界面(稍后填充)build() {Column() {Text('游戏界面')}.width('100%').height('100%')}
}

💡 知识点:

  • @Entry 表示这是一个页面入口
  • @Component 表示这是一个组件
  • @State 表示状态变量,改变时会触发 UI 更新
  • aboutToAppear() 是生命周期函数,页面显示前调用

4.4 构建主界面

现在替换 build() 方法,构建完整界面:

build() {Column() {// 1. 导航栏this.buildNavigationBar()// 2. 游戏内容if (this.questions.length > 0 && this.currentIndex < this.questions.length) {Scroll() {Column() {this.buildProgressIndicator()  // 进度指示器this.buildQuestionContent()    // 题目内容this.buildOptions()            // 选项按钮this.buildHintButton()         // 提示按钮if (this.showHint) {this.buildHintContent()      // 提示内容}if (this.answered) {this.buildNextButton()       // 下一题按钮}Row().height(40)  // 底部留白}.width('100%').padding({ left: 16, right: 16, top: 16 })}.layoutWeight(1).scrollBar(BarState.Auto)} else {// 加载中Column() {Text('加载题目中...').fontSize(16).fontColor(this.isDarkMode ? '#AAAAAA' : '#666666')}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}.width('100%').height('100%').backgroundColor(this.isDarkMode ? '#121212' : '#F5F5F5')
}

4.5 构建导航栏

build() 方法之后添加导航栏构建器:

@Builder
buildNavigationBar() {Row() {// 返回按钮Row() {Text('←').fontSize(24).fontColor(this.isDarkMode ? '#FFFFFF' : '#212121')}.width(40).height(40).justifyContent(FlexAlign.Center).onClick(() => {router.back()  // 返回上一页})// 标题Text('古诗词填空').fontSize(20).fontWeight(FontWeight.Bold).fontColor(this.isDarkMode ? '#FFFFFF' : '#212121').layoutWeight(1).textAlign(TextAlign.Center)// 得分Text(`${this.score}`).fontSize(16).fontWeight(FontWeight.Medium).fontColor('#95E1D3').padding({ left: 12, right: 12 })}.width('100%').height(56).padding({ left: 8, right: 8 }).backgroundColor(this.isDarkMode ? '#1E1E1E' : '#FFFFFF')
}

4.6 构建进度指示器

@Builder
buildProgressIndicator() {Column() {// 题目序号Row() {Text(`${this.currentIndex + 1} / ${this.questions.length}`).fontSize(14).fontColor(this.isDarkMode ? '#AAAAAA' : '#666666')}.width('100%').justifyContent(FlexAlign.Center).margin({ bottom: 8 })// 进度条Stack() {// 背景条Row().width('100%').height(6).backgroundColor(this.isDarkMode ? '#2D2D2D' : '#E0E0E0').borderRadius(3)// 进度条Row().width(`${((this.currentIndex + 1) / this.questions.length) * 100}%`).height(6).backgroundColor('#95E1D3').borderRadius(3)}.width('100%').height(6)}.width('100%').margin({ bottom: 24 })
}

4.7 构建题目内容

@Builder
buildQuestionContent() {if (this.currentIndex < this.questions.length) {Column() {// 提示文字Text('请填入合适的家禽名称').fontSize(14).fontColor('#95E1D3').fontWeight(FontWeight.Medium).margin({ bottom: 16 })// 题目内容Text(this.questions[this.currentIndex].question).fontSize(18).fontColor(this.isDarkMode ? '#FFFFFF' : '#212121').lineHeight(32).textAlign(TextAlign.Center)}.width('100%').padding(24).backgroundColor(this.isDarkMode ? '#1E1E1E' : '#FFFFFF').borderRadius(16).margin({ bottom: 24 })}
}

4.8 构建选项按钮(2x2 网格)

@Builder
buildOptions() {if (this.currentIndex < this.questions.length &&this.questions[this.currentIndex].options &&this.questions[this.currentIndex].options!.length >= 4) {// 2x2 网格布局Column({ space: 12 }) {// 第一行Row({ space: 12 }) {this.buildOptionButton(this.questions[this.currentIndex].options![0])this.buildOptionButton(this.questions[this.currentIndex].options![1])}// 第二行Row({ space: 12 }) {this.buildOptionButton(this.questions[this.currentIndex].options![2])this.buildOptionButton(this.questions[this.currentIndex].options![3])}}.width('100%').margin({ bottom: 16 })}
}@Builder
buildOptionButton(option: string) {Row() {Text(option).fontSize(24).fontColor(this.getOptionTextColor(option)).fontWeight(FontWeight.Bold)}.layoutWeight(1).height(80).justifyContent(FlexAlign.Center).backgroundColor(this.getOptionBackgroundColor(option)).borderRadius(12).border({width: 2,color: this.getOptionBorderColor(option),style: BorderStyle.Solid}).onClick(() => {this.checkAnswer(option)})
}// 获取选项文字颜色
private getOptionTextColor(option: string): string {const currentQuestion = this.getCurrentQuestion()const correctAnswer = currentQuestion?.answer || ''if (!this.answered) {// 未答题:默认颜色return this.isDarkMode ? '#FFFFFF' : '#212121'}if (option === correctAnswer) {// 正确答案:白色return '#FFFFFF'}if (option === this.selectedAnswer && !this.isCorrect) {// 错误选择:白色return '#FFFFFF'}// 其他选项:灰色return this.isDarkMode ? '#AAAAAA' : '#666666'
}// 获取选项背景颜色
private getOptionBackgroundColor(option: string): string {const currentQuestion = this.getCurrentQuestion()const correctAnswer = currentQuestion?.answer || ''if (!this.answered) {// 未答题:白色背景return this.isDarkMode ? '#1E1E1E' : '#FFFFFF'}if (option === correctAnswer) {// 正确答案:绿色return '#4CAF50'}if (option === this.selectedAnswer && !this.isCorrect) {// 错误选择:红色return '#F44336'}return this.isDarkMode ? '#1E1E1E' : '#FFFFFF'
}// 获取选项边框颜色
private getOptionBorderColor(option: string): string {const currentQuestion = this.getCurrentQuestion()const correctAnswer = currentQuestion?.answer || ''if (!this.answered) {return this.isDarkMode ? '#3D3D3D' : '#E0E0E0'}if (option === correctAnswer) {return '#4CAF50'}if (option === this.selectedAnswer && !this.isCorrect) {return '#F44336'}return this.isDarkMode ? '#3D3D3D' : '#E0E0E0'
}

4.9 构建提示按钮和内容

@Builder
buildHintButton() {if (!this.answered) {Button('💡 查看提示').width('100%').height(48).fontSize(16).fontColor('#FFA726').backgroundColor(this.isDarkMode ? '#2D2D2D' : '#FFF3E0').borderRadius(12).onClick(() => {this.showHint = !this.showHint  // 切换显示/隐藏}).margin({ bottom: 16 })}
}@Builder
buildHintContent() {if (this.currentIndex < this.questions.length) {Column() {Row() {Text('💡').fontSize(20).margin({ right: 8 })Text('提示').fontSize(16).fontWeight(FontWeight.Bold).fontColor(this.isDarkMode ? '#FFFFFF' : '#212121')}.margin({ bottom: 12 })Text(this.questions[this.currentIndex].hint).fontSize(14).fontColor(this.isDarkMode ? '#CCCCCC' : '#555555').lineHeight(22)}.width('100%').padding(16).backgroundColor(this.isDarkMode ? '#2D2D2D' : '#FFF3E0').borderRadius(12).margin({ bottom: 16 })}
}

4.10 构建下一题按钮

@Builder
buildNextButton() {Button(this.currentIndex < this.questions.length - 1 ? '下一题 →' : '查看结果').width('100%').height(56).fontSize(18).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').backgroundColor('#95E1D3').borderRadius(12).onClick(() => {this.nextQuestion()})
}

4.11 保存文件

Ctrl + S 保存。至此,游戏页面已经完成!


第五步:配置页面路由

5.1 打开路由配置文件

在项目中找到并打开:

entry/src/main/resources/base/profile/main_pages.json

5.2 添加游戏页面路由

修改文件内容为:

{"src": ["pages/Index","pages/game/PoetryFillGame"]
}

💡 注意:

  • 第一个页面是应用首页
  • 路径不包含 .ets 后缀
  • 路径相对于 entry/src/main/ets/

5.3 保存文件

Ctrl + S 保存。


第六步:修改首页,添加游戏入口

6.1 打开首页文件

找到并打开:

entry/src/main/ets/pages/Index.ets

6.2 替换全部内容

用以下代码替换文件的全部内容:

import { router } from '@kit.ArkUI'@Entry
@Component
struct Index {@State message: string = '古诗词填空游戏';build() {Column() {// 标题Text(this.message).fontSize(28).fontWeight(FontWeight.Bold).margin({ bottom: 40 })// 开始游戏按钮Button('开始游戏').fontSize(20).fontWeight(FontWeight.Medium).width(200).height(56).backgroundColor('#95E1D3').borderRadius(12).onClick(() => {router.pushUrl({url: 'pages/game/PoetryFillGame'})})}.height('100%').width('100%').justifyContent(FlexAlign.Center).backgroundColor('#F5F5F5')}
}

6.3 保存文件

Ctrl + S 保存。


第七步:编译和运行

7.1 检查文件结构

确保你创建了以下文件:

tiankong/
├── entry/src/main/ets/
│   ├── data/
│   │   ├── CultureData.ets          ✅ 已创建
│   │   └── GameDataManager.ets      ✅ 已创建
│   ├── pages/
│   │   ├── game/
│   │   │   └── PoetryFillGame.ets  ✅ 已创建
│   │   └── Index.ets                ✅ 已修改
│   └── ...
└── entry/src/main/resources/base/profile/└── main_pages.json               ✅ 已修改

7.2 编译项目

  1. 在 DevEco Studio 菜单栏,点击 BuildBuild Hap(s) / APP(s)Build Hap(s)
  2. 等待编译完成,查看底部的 Build 窗口

编译成功标志:

BUILD SUCCESSFUL in 30s

常见错误:

错误原因解决方法
Cannot find module导入路径错误检查相对路径 ../../
Property does not exist类型不匹配检查接口定义
Page not found路由未注册检查 main_pages.json

7.3 运行应用

  1. 连接 HarmonyOS 设备或启动模拟器
  2. 点击 DevEco Studio 顶部的绿色运行按钮 ▶️
  3. 等待应用安装并启动

第八步:测试游戏

测试清单

✅ 测试 1:启动应用

  • 应显示首页,标题"古诗词填空游戏"
  • 应显示"开始游戏"按钮(青绿色)

✅ 测试 2:进入游戏

  • 点击"开始游戏"
  • 应跳转到游戏页面
  • 应显示第一道题目

✅ 测试 3:答题

  • 应显示诗词标题、作者、朝代
  • 应显示带填空的诗句(____
  • 应显示 4 个选项(2x2 网格)
  • 点击选项后:
    • 正确答案变绿色
    • 错误选择变红色,正确答案也变绿色
    • 显示"下一题"按钮

✅ 测试 4:提示功能

  • 答题前点击"💡 查看提示"
  • 应展开诗词解释
  • 再次点击可收起

✅ 测试 5:进度显示

  • 顶部应显示"第 X / 8 题"
  • 进度条应随题目推进
  • 右上角应显示当前得分

✅ 测试 6:游戏结束

  • 答完 8 道题后应弹出对话框
  • 显示得分和正确率
  • 点击"再玩一次"应重新开始
  • 点击"返回"应回到首页

第九步:添加更多诗词(可选)

目前我们只添加了 3 首诗词,你可以添加更多。

如何添加诗词

打开 CultureData.ets,在 getPoetryData() 方法的数组中添加:

{id: 'poetry_goose_002',category: 'poetry',animalType: 'goose',title: '题鹅',author: '李商隐',dynasty: '唐',content: '眠沙卧水自成群,曲岸残阳极浦云。\n那解将心怜孔翠,羁雌长共故雄分。',explanation: '诗人借鹅的成群而居、夫妻相守来表达人间的情感和世态。',tags: ['李商隐', '咏物', '抒情'],isFavorite: false
},

添加规则:

  1. id 必须唯一
  2. content 中必须包含"鸡"、“鸭"或"鹅”
  3. \n 表示换行
  4. 多首诗之间用逗号分隔

完整代码参考

CultureData.ets 完整版(17首诗词)

由于篇幅限制,完整的 CultureData.ets(包含17首诗词)可以从以下位置获取:

方法一: 从源项目复制

MyApplication8/entry/src/main/ets/data/CultureData.ets

方法二: 手动添加以下诗词到数组中

点击展开完整诗词列表(14首)
// 第4首:题鹅
{id: 'poetry_goose_002',category: 'poetry',animalType: 'goose',title: '题鹅',author: '李商隐',dynasty: '唐',content: '眠沙卧水自成群,曲岸残阳极浦云。\n那解将心怜孔翠,羁雌长共故雄分。',explanation: '诗人借鹅的成群而居、夫妻相守来表达人间的情感和世态。',tags: ['李商隐', '咏物', '抒情'],isFavorite: false
},// 第5首:舟前小鹅儿
{id: 'poetry_goose_003',category: 'poetry',animalType: 'goose',title: '舟前小鹅儿',author: '杜甫',dynasty: '唐',content: '鹅儿黄似酒,对酒爱新鹅。\n引颈嗔船逼,无行乱眼多。',explanation: '杜甫描写小鹅的可爱形象,充满童趣。',tags: ['杜甫', '咏物', '生动'],isFavorite: false
},// 第6首:鹅赠鹤
{id: 'poetry_goose_004',category: 'poetry',animalType: 'goose',title: '鹅赠鹤',author: '白居易',dynasty: '唐',content: '君因风送入青云,我被人驱向鸭群。\n雪颈霜毛红网掌,请看何处不如君?',explanation: '白居易借鹅鹤对比,讽刺了重名位轻实际的社会现象。',tags: ['白居易', '寓言', '社会讽刺'],isFavorite: false
},// 第7首:鸡
{id: 'poetry_chicken_002',category: 'poetry',animalType: 'chicken',title: '鸡',author: '崔道融',dynasty: '唐',content: '买得晨鸡共鸡语,常时不用等闲鸣。\n深山月黑风雨夜,欲近晓天啼一声。',explanation: '诗人描写了公鸡司晨的职责,表现了公鸡的尽职尽责。',tags: ['崔道融', '咏物', '职责'],isFavorite: false
},// 第8首:春晓
{id: 'poetry_chicken_003',category: 'poetry',animalType: 'chicken',title: '春晓',author: '孟浩然',dynasty: '唐',content: '春眠不觉晓,处处闻啼鸟。\n夜来风雨声,花落知多少。',explanation: '这首诗虽未直接写鸡,但"春眠不觉晓"的"晓"正是由鸡鸣报晓而来。',tags: ['孟浩然', '春景', '经典名篇'],isFavorite: false
},// 第9首:鸡鸣
{id: 'poetry_chicken_004',category: 'poetry',animalType: 'chicken',title: '鸡鸣',author: '诗经',dynasty: '先秦',content: '鸡既鸣矣,朝既盈矣。\n匪鸡则鸣,苍蝇之声。',explanation: '这是《诗经》中描写鸡鸣的诗句,通过鸡鸣来表现时间的推移。',tags: ['诗经', '古老', '朝政'],isFavorite: false
},// 第10首:风入松·寄柯敬仲
{id: 'poetry_chicken_005',category: 'poetry',animalType: 'chicken',title: '风入松·寄柯敬仲',author: '虞集',dynasty: '元',content: '画堂红袖倚清酣,华发不胜簪。\n几回晚直金銮殿,东风软、花里停骖。\n书诏许传宫烛,香罗初试朝衫。\n御沟冰泮水挼蓝,飞燕语呢喃。\n重重帘幕卷轻寒,凭寄与、旧时相识。\n莫向春风笑我,花前须插红杉。',explanation: '这首词虽未直接写鸡,但"晓"指鸡鸣时分,充满了对往昔的怀念。',tags: ['虞集', '怀旧', '朝堂'],isFavorite: false
},// 第11首:春江晚景
{id: 'poetry_duck_002',category: 'poetry',animalType: 'duck',title: '春江晚景',author: '张舜民',dynasty: '宋',content: '花映新林岸,云迎曲岛隈。\n江流添夜色,鸭宿鹭归来。',explanation: '诗人描绘春江晚景,一幅宁静和谐的春江晚景图。',tags: ['张舜民', '晚景', '和谐'],isFavorite: false
},// 第12首:江上
{id: 'poetry_duck_003',category: 'poetry',animalType: 'duck',title: '江上',author: '王安石',dynasty: '宋',content: '江北秋阴一半开,晚云含雨却低徊。\n青山缭绕疑无路,忽见千帆隐映来。',explanation: '虽然此诗未直接写鸭,但江上秋景常有群鸭嬉戏,富有诗意。',tags: ['王安石', '江景', '秋色'],isFavorite: false
},// 第13首:春日
{id: 'poetry_duck_004',category: 'poetry',animalType: 'duck',title: '春日',author: '朱熹',dynasty: '宋',content: '胜日寻芳泗水滨,无边光景一时新。\n等闲识得东风面,万紫千红总是春。',explanation: '诗人在春天的美好日子里到泗水边寻找春天的气息,春江水暖,鸭子嬉戏。',tags: ['朱熹', '春景', '哲理'],isFavorite: false
},// 第14首:游园不值
{id: 'poetry_duck_005',category: 'poetry',animalType: 'duck',title: '游园不值',author: '叶绍翁',dynasty: '宋',content: '应怜屐齿印苍苔,小扣柴扉久不开。\n春色满园关不住,一枝红杏出墙来。',explanation: '"春色满园关不住,一枝红杏出墙来"表达了春天的勃勃生机,园中池塘里必有鸭子嬉戏。',tags: ['叶绍翁', '春游', '名句'],isFavorite: false
}

常见问题解答

Q1:编译时提示"找不到模块"?

A: 检查导入路径:

// 正确:相对路径
import { CultureDatabase } from './CultureData'
import { GameDataManager } from '../../data/GameDataManager'// 错误:绝对路径(不要用)
import { CultureDatabase } from 'entry/src/main/ets/data/CultureData'

Q2:运行时提示"页面未找到"?

A: 检查 main_pages.json 是否正确添加了路由:

{"src": ["pages/Index","pages/game/PoetryFillGame"  // 确保这行存在]
}

Q3:点击按钮没有反应?

A: 检查 onClick 事件是否正确绑定:

.onClick(() => {this.checkAnswer(option)  // 确保方法名正确
})

Q4:题目没有显示?

A:

  1. 检查 CultureData.ets 中是否添加了诗词数据
  2. 打开 DevTools 查看控制台,看是否有错误信息
  3. 确认 aboutToAppear() 方法被调用

Q5:如何调试?

A: 使用 console.info() 输出调试信息:

console.info('PoetryFillGame', `题目数量: ${this.questions.length}`)
console.info('PoetryFillGame', `当前答案: ${question.answer}`)

然后在 DevEco Studio 的 DevTools 中查看 Console 面板。


进阶扩展

扩展 1:添加音效

在答题正确/错误时播放音效:

import { media } from '@kit.MediaKit'// 答题时
if (this.isCorrect) {// 播放正确音效media.createSoundPool().play(correctSoundId)
} else {// 播放错误音效media.createSoundPool().play(wrongSoundId)
}

扩展 2:添加动画效果

为按钮添加点击动画:

Button('开始游戏').animation({duration: 300,curve: Curve.EaseInOut}).scale(this.buttonPressed ? 0.95 : 1.0)

扩展 3:保存最高分

使用 Preferences 保存历史最高分:

import { preferences } from '@kit.ArkData'// 保存最高分
const prefs = await preferences.getPreferences(getContext(), 'game_data')
await prefs.put('high_score', this.score)// 读取最高分
const highScore = await prefs.get('high_score', 0)

扩展 4:添加难度选择

提供简单、中等、困难三种模式:

@State difficulty: 'easy' | 'medium' | 'hard' = 'medium'// 根据难度调整题目数量
private getQuestionCount(): number {switch (this.difficulty) {case 'easy': return 5case 'medium': return 8case 'hard': return 12}
}

总结

恭喜你!你已经完成了一个完整的诗词填空游戏。通过本教程,你学会了:

数据管理

  • 定义数据结构(interface、type)
  • 创建数据库类
  • 使用静态方法提供数据

游戏逻辑

  • 题目生成算法
  • 答案验证
  • 分数统计
  • 随机打乱算法

UI 开发

  • ArkUI 组件使用
  • 状态管理(@State)
  • 条件渲染
  • 事件处理

页面导航

  • 路由配置
  • 页面跳转
  • 页面返回

下一步学习

  • 学习更多 ArkUI 组件
  • 探索动画和过渡效果
  • 学习数据持久化(Preferences、数据库)
  • 尝试网络请求获取诗词数据

项目文件总结

文件行数作用
CultureData.ets~500诗词数据库
GameDataManager.ets~140游戏数据管理
PoetryFillGame.ets~380游戏界面
Index.ets~30首页
main_pages.json~6路由配置

总计: 约 1050+ 行代码


附录

颜色参考

用途颜色代码示例
主题色#95E1D3青绿色
正确#4CAF50绿色
错误#F44336红色
提示#FFA726橙色
背景#F5F5F5浅灰

快速命令

# 创建目录
mkdir -p entry/src/main/ets/data
mkdir -p entry/src/main/ets/pages/game# 查看文件
find entry/src/main/ets -name "*.ets"# 查看行数
wc -l entry/src/main/ets/data/CultureData.ets

参考文档

  • HarmonyOS 开发者文档
  • ArkTS 语言
  • ArkUI 组件

本教程完成!

如有问题,欢迎查阅 HarmonyOS 官方文档或在社区提问。

祝你开发愉快!🎉
https://developer.huawei.com/consumer/cn/training/classDetail/fd34ff9286174e848d34cde7f512ce22?type=1%3Fha_source%3Dhmosclass&ha_sourceId=89000248

http://www.dtcms.com/a/585173.html

相关文章:

  • 【期末网页设计作业】HTML+CSS+JavaScript 蜡笔小新 动漫主题网站设计与实现(附源码)
  • 柳州建站衣联网和一起做网站。哪家强
  • 深入解析CFS虚拟运行时间:Linux公平调度的核心引擎
  • cdr做网站流程哪家公司做网站结算好
  • 专业课复习计划
  • SQL50+Hot100系列(11.8)
  • 猫狗识别数据集:34,441张高质量标注图像,深度学习二分类任务训练数据集,计算机视觉算法研发,CNN模型训练,图像识别分类,机器学习实践项目完整数据资
  • DOM NodeList 简介
  • 【数据结构】unordered 系列容器底层结构和封装
  • 昆明做网站要多少钱京津冀协同发展交通一体化规划
  • Rust编程学习 - 问号运算符会return一个Result 类型,但是如何使用main函数中使用问号运算符
  • 『 数据库 』MySQL索引深度解析:从数据结构到B+树的完整指南
  • Spring JDBC源码解析:模板方法模式的优雅实践
  • 19-Node.js 操作 Redis 实战指南:ioredis 客户端全解析与异步场景落地
  • linux服务-iptables 原理及示例详解
  • Firebase 架构原理与实战入门:从零搭建你的第一个云端应用
  • 精品在线试题库系统|基于SpringBoot和Vue的精品在线试题库系统(源码+数据库+文档)
  • AI时代职场反脆弱性:杠铃策略平衡稳定工作与高风险创新
  • 网站搭建的步骤wordpress 添加评论
  • SLAM中的非线性优-3D图优化之轴角在Opencv-PNP中的应用(一)
  • Rust 练习册 :Poker与扑克牌游戏
  • 【python】基础案例分析
  • LeetCode(python)——15.三数之和
  • Java基础——集合进阶用到的数据结构知识点1
  • 无线交换机(AC)核心技术详解:构建集中式Wi-Fi网络的基石
  • DNS的正向、反向解析的服务配置知识点及实验
  • 庖丁解牛:深度剖析 Ascend C 算子开发流程与核心概念
  • 《Learn Python Programming(4th)》读后感
  • 网站开发毕业生报告网页设计与制作项目教程陈义文
  • SSM基于JAVA的物流管理系统ztwfg(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。