构建带对象识别功能的游戏及个人作品集网站
游戏开发流程
1. 游戏结束界面与基本流程搭建
首先,有一个简单的游戏结束界面代码:
<template>
<v-card class="pa-4">
<v-card-title>
<h1 class="text-h3 text-md-h2 text-wrap">It's over!</h1>
</v-card-title>
<v-card-text>
<p>Let's see how you did!</p>
</v-card-text>
<ScoreCard />
<v-card-actions class="justify-center">
<StartGame>Play Again?</StartGame>
</v-card-actions>
</v-card>
</template>
<script lang="ts" setup>
import ScoreCard from "@/components/ScoreCard.vue";
import StartGame from "@/components/StartGame.vue";
</script>
这里主要复用了
<StartGame />
组件来触发新游戏。接下来要在
./views/Find.vue
文件中实现基本游戏流程。在该文件的脚本标签里,有以下操作:
- 从存储中加载属性和方法(第 3 - 15 行),使用
appStore
导航不同页面,
gameStore
包含当前游戏进度信息。
- 有一些计算属性:
-
currentRound
(第 17 - 19 行)显示游戏进度。
-
isPlaying
(第 21 - 23 行)确定回合边界与最大回合数。
- 从配置文件加载随机激励语录(第 25 - 29 行)。
- 组件中有两个方法:
-
skip
方法(第 31 - 39 行):跟踪跳过回合数,修改玩家分数(确保分数不低于 0),然后调用
newRound
方法。
-
newRound
方法(第 41 - 47 行):判断回合数是否达到最大,达到则导航到结束状态,否则从存储中使用
getCategory
函数加载新类别。在
onMounted
钩子中调用
newRound
函数确保进入
Find
状态时开始游戏。
Find.vue
文件的模板部分,使用
<SkipRound />
组件和
@skipped
事件,确保无论是否使用对象识别都能推进回合。此时运行应用,可通过跳过所有回合完成整个流程。
2. 移动设备测试
为了在移动设备上测试应用,需要进行以下操作:
-
更新
package.json
文件
:
{
"scripts": {
"dev": "vite --host",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"lint": "eslint . --fix --ignore-path .gitignore"
},
"dependencies": {
// ...abbreviated
},
"devDependencies": {
// ...abbreviated
}
}
这样可通过本地网络自动提供内容,只要移动设备和开发服务器在同一网络,就可通过网络地址访问应用。
-
安装 SSL 插件
:由于媒体流只能通过安全连接访问,按照 Vite 官方文档建议,在终端安装插件:
npm install --save-dev @vitejs/plugin-basic-ssl
-
更新
vite.confis.ts文件 :
// Plugins
import vue from '@vitejs/plugin-vue'
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
import basicSsl from '@vitejs/plugin-basic-ssl'
// Utilities
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
basicSsl(),
vue({
template: { transformAssetUrls }
}),
// ...abbreviated
],
// ...abbreviated
})
保存后重启开发服务器,内容将通过 HTTPS 协议提供,首次进入浏览器可能会有警告。
3. 相机对象识别
3.1 创建
CameraDetect.vue
组件
在
./components
文件夹中创建
CameraDetect.vue
组件,代码使用
@vueuse
包的组合式函数与浏览器的
Devices
和
userMedia
API 交互:
- 使用
useDevicesList
列出可用相机(第 33 - 40 行),填充
<v-select />
组件(第 4 - 14 行),允许用户切换相机。
- 用户需手动激活相机,组件中的按钮切换相机流(第 44 - 46 行)。
- 使用
watchEffect
将流导入视频引用(第 48 - 50 行),在
<video />
HTML 组件中引用流显示相机画面(第 20 行)。
3.2 检测和识别流中的对象
-
修改
object.ts文件中的detect函数 :
const detect = async (img: any, className?: string) => {
try {
detected.value = []
const result = await cocoSsdModel.detect(img)
const filter = className ? (item: DetectedObject) => (item.score >= config.DETECTION_ACCURACY_THRESHOLD && item.class === className) : () => true
detected.value = result.map((item: DetectedObject) => item).filter(filter).sort((a: DetectedObject, b: DetectedObject) => b.score - a.score)
} catch (e) {
// handle error if model is not loaded
}
};
添加可选参数
className
,根据其值定义过滤函数,若未提供则不过滤对象,以提供
<ImageDetect />
组件的向后兼容性。
-
连接流到检测函数
:在
CameraDetect.vue
文件中添加以下代码:
<script lang="ts" setup>
import { ref, watchEffect, watch } from "vue";
// ...abbreviated
import { storeToRefs } from "pinia";
import { useObjectStore } from "@/store/object";
const objectStore = useObjectStore();
const { detected } = storeToRefs(objectStore);
const { detect } = objectStore;
import { useGameStore } from "@/store/game";
const gameStore = useGameStore();
const { currentCategory } = storeToRefs(gameStore);
// ...abbreviated
const detectObject = async (): Promise<void> => {
if (!props.disabled) {
await detect(video.value, currentCategory.value);
}
window.requestAnimationFrame(detectObject);
};
watch(enabled, () => {
if (enabled.value && video.value) {
video.value.addEventListener("loadeddata", detectObject);
}
});
const emit = defineEmits(["found"]);
watch(detected, () => {
if (detected.value?.length > 0) {
emit("found", detected.value[0]);
}
});
</script>
创建递归函数
detectObject
,使用
window.requestAnimationFrame
节流调用
detect
方法。使用
watch
钩子监测
enabled
变量,视频启用时调用
detectObject
函数。检测到匹配项时,触发
found
事件并返回最佳匹配项到父组件。
4. 连接检测事件
在
Find.vue
文件中进行以下修改:
-
模板部分
:
<CameraDetect @found="found" :disabled="detectionDisabled" />
- 脚本部分 :
<script lang="ts" setup>
// ...abbreviated
const detectionDisabled = ref(false);
// ...abbreviated
const skip = () => {
// ...abbreviated
detectionDisabled.value = false;
// ...abbreviated
};
const found = (e: { class: string; score: number }) => {
detectionDisabled.value = true;
objectsFound.value++;
const newScore = Math.round(
config.SCORE_FOUND * (e.score + 1) * config.SCORE_ACCURACY_MULTIPLIER
);
score.value += newScore;
newRound();
// ...abbreviated
};
</script>
添加
detectionDisabled
响应式变量,在
skip
函数中设置其值为
false
,在
found
函数中更新分数,调用
newRound
函数,然后设置
detectionDisabled
为
true
继续检测。此时可再次测试应用,若识别不可靠,可降低
./config.ts
文件中的
DETECTION_ACCURACY_THRESHOLD
。
5. 完善游戏流程
为了给用户足够反馈,使用对话框功能:
-
更新
CameraDetect.vue
文件
:
<script lang="ts" setup>
// ...abbreviated
import { useAppStore } from "@/store/app";
const appStore = useAppStore();
const { dialogVisible } = storeToRefs(appStore);
// ...abbreviated
const detectObject = async (): Promise<void> => {
if (!props.disabled && !dialogVisible.value) {
await detect(video.value, currentCategory.value);
}
window.requestAnimationFrame(detectObject);
};
// ...abbreviated
</script>
-
更新
Find.vue文件 :
<script lang="ts" setup>
// ...abbreviated
const dialogEndLine = computed(() =>
objectsFound.value + skips.value >= objectsLimit.value
? "You're done!"
: "Get ready for the next round!"
);
// ...abbreviated
const found = (e: { class: string; score: number }) => {
detectionDisabled.value = true;
objectsFound.value++;
const newScore = Math.round(
config.SCORE_FOUND * (e.score + 1) * config.SCORE_ACCURACY_MULTIPLIER
);
score.value += newScore;
newRound();
appStore.showDialog(
"Congratulations! 🥳",
`<p>You've scored ${newScore} points by finding <strong>${e.class}</strong>!</p>
<p>${dialogEndLine.value}</p>`
);
};
const skipped = () => {
detectionDisabled.value = true;
skips.value++;
if ((score.value + config.SCORE_SKIP) <= 0) {
score.value = 0;
} else {
score.value += config.SCORE_SKIP;
}
newRound();
appStore.showDialog(
"Oh no! 🙀",
`<p>Skipping cost you ${-config.SCORE_SKIP} points!</p><p>${
dialogEndLine.value
}</p>`
);
};
</script>
使用
appStore
的
showDialog
方法显示对话框,告知用户得分情况和下一回合提示。
游戏开发流程总结
| 步骤 | 操作 |
|---|---|
| 基本流程搭建 |
在
Find.vue
文件中实现游戏基本流程,包括方法和计算属性
|
| 移动设备测试 |
更新
package.json
和
vite.confis.ts
文件,安装 SSL 插件
|
| 相机对象识别 |
创建
CameraDetect.vue
组件,修改
object.ts
文件中的
detect
函数,连接流到检测函数
|
| 连接检测事件 |
在
Find.vue
文件中添加事件处理和
detectionDisabled
变量
|
| 完善游戏流程 |
更新
CameraDetect.vue
和
Find.vue
文件,使用对话框提供反馈
|
游戏开发流程图
graph TD;
A[游戏结束界面] --> B[基本流程搭建];
B --> C[移动设备测试];
C --> D[相机对象识别];
D --> E[连接检测事件];
E --> F[完善游戏流程];
个人作品集网站搭建
1. 项目概述
我们将使用 Nuxt 来构建个人作品集网站,内容存储在 Storyblok 空间,通过 Netlify 进行发布。在这个过程中,我们会涉及到使用 Nuxt 作为静态站点渲染器、应用无头 CMS 管理内容、连接 Nuxt 和 Storyblok、优化网站以及自动化部署等操作。
2. 技术要求
- 框架 :使用 Nuxt(https://nuxt.com/)作为构建网站的框架。
- UI 库 :采用 Nuxt UI(https://ui.nuxt.com/)进行网站的样式和交互设计。
- SSL 证书 :使用 mkcert(https://github.com/FiloSottile/mkcert)生成本地受信任的开发证书,以在 SSL 模式下运行本地开发。
3. 具体操作步骤
3.1 刷新对 Nuxt 的使用及作为静态站点渲染器
Nuxt 可以大大加速开发过程,作为静态站点渲染器,它能生成静态 HTML 文件,提升网站性能。具体操作可参考 Nuxt 官方文档进行配置。
3.2 学习应用无头 CMS 管理内容
无头 CMS(如 Storyblok)可以帮助我们更好地组织和管理网站内容。具体步骤如下:
-
注册 Storyblok 账号
:访问 https://www.storyblok.com/ 注册账号并创建一个新的空间。
-
创建内容模型
:在 Storyblok 中定义内容模型,例如项目介绍、个人信息等。
-
添加内容
:根据定义的内容模型添加具体的网站内容。
3.3 连接 Nuxt 和 Storyblok
利用现有的集成来连接 Nuxt 和 Storyblok,步骤如下:
-
安装 Storyblok 插件
:在 Nuxt 项目中安装 Storyblok 插件。
npm install @storyblok/nuxt
-
配置插件
:在
nuxt.config.ts文件中进行配置。
export default defineNuxtConfig({
modules: [
'@storyblok/nuxt',
],
storyblok: {
accessToken: 'YOUR_ACCESS_TOKEN',
apiOptions: {
region: 'us' // or 'eu'
}
}
})
- 获取内容 :在 Nuxt 组件中使用 Storyblok SDK 获取内容。
<template>
<div>
<h1>{{ storyblokContent.name }}</h1>
<p>{{ storyblokContent.description }}</p>
</div>
</template>
<script lang="ts" setup>
import { useStoryblok } from '@storyblok/nuxt'
const { story } = useStoryblok('home', {
version: 'draft'
})
const storyblokContent = computed(() => story.value?.content)
</script>
3.4 应用优化模式
为了优化网站性能,可采用以下方法:
-
代码分割
:Nuxt 会自动进行代码分割,将不同页面的代码分开加载,减少初始加载时间。
-
图片优化
:使用压缩工具对图片进行压缩,同时采用响应式图片技术,根据设备屏幕大小加载合适的图片。
-
缓存策略
:设置合理的缓存策略,减少服务器请求。
3.5 自动化部署到公共主机
使用 Netlify 进行自动化部署,步骤如下:
-
连接 Netlify 和代码仓库
:在 Netlify 中连接你的代码仓库(如 GitHub、GitLab 等)。
-
配置部署设置
:在 Netlify 中设置构建命令和发布目录。
Build command: npm run build
Publish directory: .output/public
- 触发部署 :每次代码更新时,Netlify 会自动触发部署流程。
个人作品集网站搭建总结
| 步骤 | 操作 |
|---|---|
| 刷新 Nuxt 使用 | 参考官方文档配置 Nuxt 作为静态站点渲染器 |
| 应用无头 CMS | 注册 Storyblok 账号,创建内容模型并添加内容 |
| 连接 Nuxt 和 Storyblok | 安装插件,配置插件,在组件中获取内容 |
| 优化网站 | 采用代码分割、图片优化、缓存策略等方法 |
| 自动化部署 | 连接 Netlify 和代码仓库,配置部署设置,触发自动部署 |
个人作品集网站搭建流程图
graph TD;
A[项目概述] --> B[技术要求];
B --> C[刷新 Nuxt 使用];
C --> D[应用无头 CMS];
D --> E[连接 Nuxt 和 Storyblok];
E --> F[优化网站];
F --> G[自动化部署];
通过以上步骤,我们完成了带对象识别功能的游戏开发和个人作品集网站的搭建。在开发过程中,我们学习了如何利用各种技术和工具,将不同的功能集成到项目中。希望这些内容能帮助你更好地掌握相关技术,打造出属于自己的项目。
超级会员免费看
2108

被折叠的 条评论
为什么被折叠?



