8 changed files with 428 additions and 5 deletions
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 934 B |
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 6.4 KiB |
@ -0,0 +1,318 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { ref, computed, onMounted } from 'vue' |
||||
|
import { useRouter } from 'vue-router' |
||||
|
import { useCharacterStore } from '@/stores/character' |
||||
|
import Particles from '@/components/Particles/Particles.vue' |
||||
|
import TextType from '@/components/TextType/TextType.vue' |
||||
|
import StarBorder from '@/components/StarBorder/StarBorder.vue' |
||||
|
|
||||
|
const router = useRouter() |
||||
|
const characterStore = useCharacterStore() |
||||
|
|
||||
|
const currentPage = ref(0) |
||||
|
|
||||
|
interface TutorialPage { |
||||
|
title: string |
||||
|
content: string[] |
||||
|
} |
||||
|
|
||||
|
const tutorialPages: TutorialPage[] = [ |
||||
|
{ |
||||
|
title: '世界观', |
||||
|
content: [ |
||||
|
'天地未开,混沌一片。', |
||||
|
'亿万年前,域外天魔入侵,引发了远古神战。', |
||||
|
'战神以身化道,封印天魔于无尽虚空。', |
||||
|
'天地灵气开始复苏,修炼之道由此大兴。', |
||||
|
'玩家降临此界,将踏上修炼之路,寻求成仙之道。' |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
title: '装备系统', |
||||
|
content: [ |
||||
|
'装备分为三大类:武器、防具、饰品。', |
||||
|
'武器增加攻击,如飞剑、长刀、长枪等。', |
||||
|
'防具增加防御和生命,如铠甲、法袍等。', |
||||
|
'饰品附加特殊属性,如戒指、项链、玉佩等。', |
||||
|
'装备品质分为:凡品、上品、精品、极品、仙器、神器。', |
||||
|
'高品质装备可通过锻造、掉落或交易获得。' |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
title: '丹药系统', |
||||
|
content: [ |
||||
|
'丹药是修炼者不可或缺的资源。', |
||||
|
'修炼类:聚气丹、破境丹,帮助快速提升修为。', |
||||
|
'战斗类:回血丹、爆发丹,战斗时使用。', |
||||
|
'辅助类:隐身丹、传送符,特殊场合使用。', |
||||
|
'丹药可通过炼丹炉炼制,或击杀怪物掉落。', |
||||
|
'高品质丹药需要珍稀药材才能炼制。' |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
title: '境界体系', |
||||
|
content: [ |
||||
|
'修炼境界分为:炼气、筑基、金丹、元婴、化神、大乘、飞升。', |
||||
|
'每境界有九层,需积累足够经验才能突破。', |
||||
|
'突破需要满足境界条件,如修为、丹药等。', |
||||
|
'境界越高,属性加成越高,能力越强。', |
||||
|
'达到飞升境界,即可飞升仙界,寻求更高的道。' |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
const totalPages = computed(() => tutorialPages.length) |
||||
|
const currentPageData = computed(() => tutorialPages[currentPage.value]) |
||||
|
const isLastPage = computed(() => currentPage.value === tutorialPages.length - 1) |
||||
|
|
||||
|
const goToNextPage = () => { |
||||
|
if (currentPage.value < tutorialPages.length - 1) { |
||||
|
currentPage.value++ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const goToPrevPage = () => { |
||||
|
if (currentPage.value > 0) { |
||||
|
currentPage.value-- |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const skipTutorial = () => { |
||||
|
localStorage.setItem('has_seen_tutorial', 'true') |
||||
|
router.push('/game') |
||||
|
} |
||||
|
|
||||
|
const startGame = () => { |
||||
|
localStorage.setItem('has_seen_tutorial', 'true') |
||||
|
router.push('/game') |
||||
|
} |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
await characterStore.fetchCharacters() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div class="tutorial-page"> |
||||
|
<Particles :particle-count="50" :particle-colors="['#ffffff', '#cccccc']" class="particles-bg" /> |
||||
|
|
||||
|
<div class="tutorial-container"> |
||||
|
<div class="page-header"> |
||||
|
<TextType |
||||
|
:key="`title-${currentPage}`" |
||||
|
:text="[currentPageData.title]" |
||||
|
:typingSpeed="80" |
||||
|
:pauseDuration="1500" |
||||
|
:showCursor="false" |
||||
|
class="page-title" |
||||
|
/> |
||||
|
<span class="page-indicator">{{ currentPage + 1 }} / {{ totalPages }}</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="content-area"> |
||||
|
<div v-for="(line, lineIndex) in currentPageData.content" :key="`${currentPage}-${lineIndex}`" class="content-line"> |
||||
|
<TextType |
||||
|
:text="[line]" |
||||
|
:typingSpeed="30" |
||||
|
:pauseDuration="1000" |
||||
|
:initialDelay="lineIndex * 300" |
||||
|
:showCursor="false" |
||||
|
class="content-text" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="pagination"> |
||||
|
<div class="page-dots"> |
||||
|
<span |
||||
|
v-for="(_, index) in tutorialPages" |
||||
|
:key="index" |
||||
|
class="dot" |
||||
|
:class="{ active: index === currentPage }" |
||||
|
@click="currentPage = index" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="page-actions"> |
||||
|
<button |
||||
|
class="nav-btn prev-btn" |
||||
|
:disabled="currentPage === 0" |
||||
|
@click="goToPrevPage" |
||||
|
> |
||||
|
上一页 |
||||
|
</button> |
||||
|
|
||||
|
<StarBorder as="div" color="#22c55e" speed="5s" :thickness="2"> |
||||
|
<button |
||||
|
v-if="isLastPage" |
||||
|
class="start-btn" |
||||
|
@click="startGame" |
||||
|
> |
||||
|
开始游戏 |
||||
|
</button> |
||||
|
<button |
||||
|
v-else |
||||
|
class="skip-btn" |
||||
|
@click="skipTutorial" |
||||
|
> |
||||
|
跳过 |
||||
|
</button> |
||||
|
</StarBorder> |
||||
|
|
||||
|
<button |
||||
|
class="nav-btn next-btn" |
||||
|
:disabled="currentPage === totalPages - 1" |
||||
|
@click="goToNextPage" |
||||
|
> |
||||
|
下一页 |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
.tutorial-page { |
||||
|
min-height: 100vh; |
||||
|
background: #000000; |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.particles-bg { |
||||
|
position: fixed !important; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100% !important; |
||||
|
height: 100% !important; |
||||
|
} |
||||
|
|
||||
|
.tutorial-container { |
||||
|
position: relative; |
||||
|
z-index: 10; |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
padding: 40px 20px; |
||||
|
min-height: 100vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.page-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 32px; |
||||
|
} |
||||
|
|
||||
|
.page-title { |
||||
|
font-size: 1.5rem; |
||||
|
font-weight: bold; |
||||
|
color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.page-indicator { |
||||
|
color: #888888; |
||||
|
font-size: 0.875rem; |
||||
|
} |
||||
|
|
||||
|
.content-area { |
||||
|
flex: 1; |
||||
|
padding: 20px 0; |
||||
|
} |
||||
|
|
||||
|
.content-line { |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.content-text { |
||||
|
font-size: 1rem; |
||||
|
line-height: 1.8; |
||||
|
color: #cccccc; |
||||
|
} |
||||
|
|
||||
|
.pagination { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
margin: 24px 0; |
||||
|
} |
||||
|
|
||||
|
.page-dots { |
||||
|
display: flex; |
||||
|
gap: 12px; |
||||
|
} |
||||
|
|
||||
|
.dot { |
||||
|
width: 10px; |
||||
|
height: 10px; |
||||
|
border-radius: 50%; |
||||
|
background: rgba(255, 255, 255, 0.2); |
||||
|
cursor: pointer; |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.dot:hover { |
||||
|
background: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
.dot.active { |
||||
|
background: #22c55e; |
||||
|
transform: scale(1.2); |
||||
|
} |
||||
|
|
||||
|
.page-actions { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
gap: 16px; |
||||
|
margin-top: 24px; |
||||
|
} |
||||
|
|
||||
|
.nav-btn { |
||||
|
padding: 12px 20px; |
||||
|
background: rgba(255, 255, 255, 0.05); |
||||
|
border: 1px solid rgba(255, 255, 255, 0.1); |
||||
|
border-radius: 10px; |
||||
|
color: #888888; |
||||
|
font-size: 0.9rem; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.2s ease; |
||||
|
min-width: 80px; |
||||
|
} |
||||
|
|
||||
|
.nav-btn:hover:not(:disabled) { |
||||
|
background: rgba(255, 255, 255, 0.1); |
||||
|
border-color: rgba(255, 255, 255, 0.2); |
||||
|
color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.nav-btn:disabled { |
||||
|
opacity: 0.3; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
.start-btn, |
||||
|
.skip-btn { |
||||
|
padding: 12px 32px; |
||||
|
background: transparent; |
||||
|
border: none; |
||||
|
border-radius: 10px; |
||||
|
font-size: 1rem; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
.start-btn { |
||||
|
color: #22c55e; |
||||
|
} |
||||
|
|
||||
|
.skip-btn { |
||||
|
color: #888888; |
||||
|
} |
||||
|
|
||||
|
.start-btn:hover, |
||||
|
.skip-btn:hover { |
||||
|
transform: translateY(-2px); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,74 @@ |
|||||
|
# 游戏教程功能设计 |
||||
|
|
||||
|
## 概述 |
||||
|
|
||||
|
创建一个游戏教程/介绍界面,在玩家创建角色后自动弹出,展示游戏世界观、装备系统、丹药系统、境界等级等信息,采用分页展示,使用 TextType 组件逐字显示文字。 |
||||
|
|
||||
|
## 功能需求 |
||||
|
|
||||
|
1. **自动触发**:玩家创建角色成功后自动弹出教程 |
||||
|
2. **手动触发**:游戏主界面有"游戏指南"按钮可再次查看 |
||||
|
3. **全屏弹窗**:覆盖整个屏幕的模态窗口 |
||||
|
4. **分页展示**:多个页面分别为世界观、装备、丹药、境界等 |
||||
|
5. **退出方式**:底部"跳过"按钮关闭 |
||||
|
|
||||
|
## UI/UX 设计 |
||||
|
|
||||
|
### 布局 |
||||
|
|
||||
|
- 全屏覆盖弹窗(`position: fixed`) |
||||
|
- 顶部:页面标题 + 页码指示器(如 1/4) |
||||
|
- 中间:内容区域,支持滚动 |
||||
|
- 底部:"跳过"按钮 |
||||
|
|
||||
|
### 分页结构 |
||||
|
|
||||
|
1. **第一页:世界观** |
||||
|
- 游戏背景介绍 |
||||
|
- 修炼体系说明 |
||||
|
|
||||
|
2. **第二页:装备系统** |
||||
|
- 装备分类(武器、防具、饰品等) |
||||
|
- 装备品质说明 |
||||
|
|
||||
|
3. **第三页:丹药系统** |
||||
|
- 各类丹药介绍 |
||||
|
- 丹药用途 |
||||
|
|
||||
|
4. **第四页:境界等级** |
||||
|
- 境界划分 |
||||
|
- 修炼路线 |
||||
|
|
||||
|
### 交互 |
||||
|
|
||||
|
- 左右滑动或按钮切换页面 |
||||
|
- TextType 组件逐字显示文字 |
||||
|
- 最后一页显示"开始游戏"按钮 |
||||
|
|
||||
|
## 技术实现 |
||||
|
|
||||
|
### 文件结构 |
||||
|
|
||||
|
``` |
||||
|
Build_God_Game/src/views/TutorialView.vue # 新建 |
||||
|
Build_God_Game/src/router/index.ts # 修改 |
||||
|
``` |
||||
|
|
||||
|
### 路由 |
||||
|
|
||||
|
- 路径:`/tutorial` |
||||
|
- 需要认证:`requiresAuth: true` |
||||
|
|
||||
|
### 组件设计 |
||||
|
|
||||
|
- 使用 TextType 组件显示文字 |
||||
|
- 使用分页组件管理多页内容 |
||||
|
- 支持键盘/按钮导航 |
||||
|
|
||||
|
## 验收标准 |
||||
|
|
||||
|
1. 创建角色后自动跳转到教程页面 |
||||
|
2. 教程使用 TextType 动画显示文字 |
||||
|
3. 可通过分页按钮切换页面 |
||||
|
4. 点击"跳过"或"开始游戏"进入游戏主界面 |
||||
|
5. GameView 有"游戏指南"按钮可重新查看 |
||||
Loading…
Reference in new issue