Commit 7b63c40f authored by Tt's avatar Tt

feat(游戏逻辑): 实现分阶段游戏流程和鱼点击验证机制

parent f21d4bcf
......@@ -157,10 +157,10 @@
<div style="margin-top: 5px;">
<nz-radio-group [(ngModel)]="question.imgSize" (ngModelChange)="ngChange()">
<label nz-radio nzValue="big">
<span [style]="{color:question.imgSize=='big' ? '#169BD5':'#000'}">大图片</span>
<span [style]="{color:question.imgSize=='big' ? '#169BD5':'#000'}">大图片(一屏3个图)</span>
</label>
<label nz-radio nzValue="small">
<span [style]="{color:question.imgSize=='small' ? '#169BD5':'#000'}">小图片</span>
<span [style]="{color:question.imgSize=='small' ? '#169BD5':'#000'}">小图片(一屏4个图)</span>
</label>
</nz-radio-group>
</div>
......
......@@ -74,3 +74,75 @@ export interface PositionMap {
/** 键为问题数量,值为位置数组 */
[key: string]: Position[];
}
/**
* 游戏阶段类
*/
export class Stage {
/** 正确的问题 */
correctQuestion: Question;
/** 问题列表 */
questionList: Question[];
/** 随机问题列表 */
questionRandomList: Question[];
/**
* 构造函数
* @param correctQuestion 正确的问题
* @param questionList 问题列表
*/
constructor(correctQuestion: Question, questionList: Question[]) {
this.correctQuestion = correctQuestion;
this.questionList = questionList;
this.questionRandomList = [];
// 初始化时自动生成随机列表
this.randomList();
}
/**
* 将问题列表打乱并放置到随机问题列表中
* 1. 从数组中移除正确的答案
* 2. 将打乱的数组的长度限制为不超过4
* 3. 将正确的答案添加两次到列表中
* 4. 使用深拷贝处理数据
*/
randomList(): void {
// 使用深拷贝复制问题列表,避免直接修改原数组
const tempList = JSON.parse(JSON.stringify(this.questionList));
// 从列表中移除正确答案
const correctQuestionIndex = tempList.findIndex(q =>
q.audio === this.correctQuestion.audio &&
q.image === this.correctQuestion.image);
if (correctQuestionIndex !== -1) {
tempList.splice(correctQuestionIndex, 1);
}
// 使用Fisher-Yates洗牌算法打乱数组
for (let i = tempList.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[tempList[i], tempList[j]] = [tempList[j], tempList[i]];
}
// 限制打乱后的数组长度不超过4(不包括正确答案)
const maxLength = 4;
const limitedList = tempList.slice(0, maxLength);
// 将正确答案添加两次到列表中
const correctQuestionCopy = JSON.parse(JSON.stringify(this.correctQuestion));
limitedList.push(correctQuestionCopy);
limitedList.push(JSON.parse(JSON.stringify(this.correctQuestion)));
// 再次打乱数组,确保正确答案的位置随机
for (let i = limitedList.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[limitedList[i], limitedList[j]] = [limitedList[j], limitedList[i]];
}
// 将最终打乱后的列表赋值给随机问题列表
this.questionRandomList = limitedList;
}
}
......@@ -44,7 +44,7 @@ import TWEEN from "@tweenjs/tween.js";
// ]
// }
import { FormData, Position, PositionMap } from './interfaces';
import { FormData, Position, PositionMap, Stage, Question } from './interfaces';
let defaultFormData: FormData = {
"score": 0,
......@@ -145,6 +145,7 @@ export class PlayComponent implements OnInit, OnDestroy {
m_gameOver = false; // 游戏结束标志
selectList = []; // 选中的项目列表
checkBtnElement = null; // 检查按钮元素引用
gameStages: Stage[] = []; // 游戏阶段数组,存储所有创建的Stage对象
// ------------------------------------
......@@ -704,6 +705,24 @@ export class PlayComponent implements OnInit, OnDestroy {
private cat_right: any;
private cat_center: any;
initFishGame() {
const targetList = [0, 1, 2, 3, 4, 5, 6];
const questions = this.g_formData.questions;
// 1. 梳理出选中的数据数组
const selectedQuestions = targetList.map(index => questions[index]).filter(q => q); // 过滤掉可能的undefined值
// 2. 根据梳理好的数组创建多个Stage
this.gameStages = []; // 清空之前的游戏阶段
selectedQuestions.forEach(currentQuestion => {
// 为每个选中的值创建一个Stage对象
// 正确值为当前值,列表值为整个梳理后的列表
const stage = new Stage(currentQuestion, selectedQuestions);
this.gameStages.push(stage);
});
// 打印创建的Stage对象,用于调试
console.log('Created Stages:', this.gameStages);
this.layoutPage.ref.visible = false;
this.checkBtnElement.ref.visible = false;
this.listItem.ref.visible = false;
......@@ -715,10 +734,45 @@ export class PlayComponent implements OnInit, OnDestroy {
this.cat_center = this.createCat('center'); // 创建中间猫
}
// const type = 'big'
// const data = this.g_formData.questions[0];
// const fish = this.createFish(type, data);
// const paopao = this.brokePaopao(fish);
this.initNextStage();
}
// 当前游戏阶段索引
private currentStageIndex = 0;
// 存储用户点击的鱼的数据
private clickedFishData: Question[] = [];
/**
* 初始化下一个游戏阶段
* 1. 将当前Stage中的数组随机一次
* 2. 后续出鱼按照随机后的数组出现
* 3. 用户点击鱼后存储对应数据,如果数据中有两个数据为当前Stage的正确数据则清空画面,等待1秒后进行下一轮游戏
*/
initNextStage() {
// 清空之前点击的鱼的数据
this.clickedFishData = [];
// 检查是否还有游戏阶段
if (this.currentStageIndex >= this.gameStages.length) {
// 所有阶段都完成了,结束游戏
this.endGame();
return;
}
// 获取当前游戏阶段
const currentStage = this.gameStages[this.currentStageIndex];
// 将当前Stage中的数组随机一次
currentStage.randomList();
console.log('Current Stage:', currentStage);
console.log('Random List:', currentStage.questionRandomList);
// 清空现有的鱼群
this.clearFishes();
// 修改鱼的生成逻辑,使用当前阶段的随机问题列表
this.m_fishInterval = 3000; // 设置鱼的生成间隔为3秒
this.m_lastFishTime = 0; // 重置上次生成鱼的时间,使下一帧立即生成鱼
}
initVsView() {
......@@ -997,13 +1051,62 @@ export class PlayComponent implements OnInit, OnDestroy {
}
private touchFish = null;
/**
* 处理鱼泡泡破裂
* 隐藏泡泡背景、小鱼和图片,显示泡泡破裂动画
* 存储用户点击的鱼的数据,检查是否有两个正确数据
* @param fish 被点击的鱼对象
*/
brokePaopao(fish) {
// 隐藏泡泡背景、小鱼和图片
fish['child_paopao_bg'].ref.visible = false;
fish['child_xiao_yu'].ref.visible = false;
fish['child_image'].ref.visible = false;
fish['child_paopao_ani'].ref.visible = true;
// 创建飞向水桶的小鱼动画
this.createFlyingFish(fish);
// 获取当前游戏阶段
const currentStage = this.gameStages[this.currentStageIndex];
// 存储用户点击的鱼的数据
if (fish.questionData) {
this.clickedFishData.push(fish.questionData);
// 检查是否点击了正确的鱼
const isCorrect = fish.questionData.audio === currentStage.correctQuestion.audio &&
fish.questionData.image === currentStage.correctQuestion.image;
console.log('Clicked fish data:', fish.questionData);
console.log('Is correct:', isCorrect);
console.log('Clicked fish count:', this.clickedFishData.length);
// 计算正确点击的次数
const correctClicks = this.clickedFishData.filter(data =>
data.audio === currentStage.correctQuestion.audio &&
data.image === currentStage.correctQuestion.image
).length;
console.log('Correct clicks:', correctClicks);
// 如果有两个数据为当前Stage的正确数据,则清空画面,等待1秒后进入下一轮游戏
if (correctClicks >= 2) {
console.log('Stage completed! Moving to next stage...');
// 清空画面(清除所有鱼)
this.clearFishes();
// 增加当前阶段索引
this.currentStageIndex++;
// 等待1秒后进入下一轮游戏
setTimeout(() => {
this.initNextStage();
}, 1000);
}
}
// 按顺序切换序列帧 pao_pao_xu_lie_zhen_1 到 pao_pao_xu_lie_zhen_9
let currentFrame = 1;
const maxFrame = 9;
......@@ -1513,29 +1616,62 @@ export class PlayComponent implements OnInit, OnDestroy {
this.updateFish();
}
/**
* 清空所有鱼
* 移除所有当前屏幕上的鱼元素
*/
clearFishes() {
// 从渲染对象数组中移除所有鱼
for (let i = this.m_fishArray.length - 1; i >= 0; i--) {
const fish = this.m_fishArray[i];
// 从渲染对象数组中移除
this.m_renderObject = this.m_renderObject.filter(item => item.element !== fish.ref);
}
// 重新构建渲染数组
this.m_renderArr = [];
this.m_renderObject.forEach(item => {
this.m_renderArr.push(item.element);
});
// 清空鱼群数组
this.m_fishArray = [];
}
/**
* 更新鱼的运动
* 管理鱼从右侧游到左侧的动画效果
*/
updateFish() {
// 如果没有游戏阶段或当前阶段索引超出范围,不生成鱼
if (!this.gameStages || this.currentStageIndex >= this.gameStages.length) {
return;
}
// 获取当前游戏阶段
const currentStage = this.gameStages[this.currentStageIndex];
// 检查是否需要创建新的鱼
const currentTime = Date.now();
if (currentTime - this.m_lastFishTime > this.m_fishInterval) {
// 创建新的鱼
if (this.g_formData && this.g_formData.questions && this.g_formData.questions.length > 0) {
// 随机选择一个问题数据
const randomIndex = Math.floor(Math.random() * this.g_formData.questions.length);
const fishData = this.g_formData.questions[randomIndex];
if (currentStage && currentStage.questionRandomList && currentStage.questionRandomList.length > 0) {
// 从当前阶段的随机问题列表中选择一个问题数据
const randomIndex = Math.floor(Math.random() * currentStage.questionRandomList.length);
const fishData = currentStage.questionRandomList[randomIndex];
// 创建小型鱼
const fish = this.createFish('small', fishData);
// 存储问题数据到鱼对象中,以便点击时可以获取
fish.questionData = fishData;
// 设置鱼的初始位置在屏幕右侧外部
fish.ref.x = this.g_canvasWidth + fish.ref.width * fish.ref.scaleX / 2;
fish.ref.y = Math.random() * (this.g_canvasHeight * 0.6) + this.g_canvasHeight * 0.2; // 在屏幕中间区域随机高度
// 设置鱼的游动速度
fish.speed = 0.3; // 0.5-1.5的随机速度,降低游动速度
fish.speed = 0.3; // 降低游动速度
// 确保鱼已添加到渲染队列中
// createFish 方法中已经调用了 render 方法,但为了确保,这里再次检查
......
1. 显示大图片表示:在前端显示“3个图”这种 显示小图片表示:在前端显示“4个图”这种
2. 选择的图片组成题数。
3. 鱼出现的数组在本局开始的时候就生成好, 后续的出现鱼的顺序按照这个流程来。【鱼数量上限为5,主角鱼+1】
4. 点击鱼 正确 播放音频
5. 本局开始 播放音频
6.
1. 本局开始 播放音频
2. 点击鱼 正确 播放音频
3. 获取星星表示当前进度
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment