Commit e3bc91dc authored by Tt's avatar Tt

feat(form): 添加全局图片大小设置并优化鱼生成逻辑

parent 2b477b30
......@@ -115,6 +115,23 @@
</nz-radio-group>
</div>
</div>
<!-- 图片大小设置区 -->
<div class="border-dashed" style="margin: 20px;width: 1000px;">
<div class="word-input-title">
<span style="font-size: 20px;">图片大小设置: </span>
</div>
<div style="margin-top: 10px;">
<nz-radio-group [(ngModel)]="item.imgSize" (ngModelChange)="ngChange()">
<label nz-radio nzValue="big">
<span [style]="{color:item.imgSize=='big' ? '#169BD5':'#000'}">大图片(一屏3个图)</span>
</label>
<label nz-radio nzValue="small">
<span [style]="{color:item.imgSize=='small' ? '#169BD5':'#000'}">小图片(一屏4个图)</span>
</label>
</nz-radio-group>
</div>
</div>
</div>
<div style="padding: 10px;background-color: #fff;display:none">
<!-- 标题区 -->
......@@ -154,16 +171,6 @@
</div>
<div>
<span style="font-size: 20px;">图片: </span>
<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'}">大图片(一屏3个图)</span>
</label>
<label nz-radio nzValue="small">
<span [style]="{color:question.imgSize=='small' ? '#169BD5':'#000'}">小图片(一屏4个图)</span>
</label>
</nz-radio-group>
</div>
<div style="width:300px; margin-top: 10px;">
<app-upload-image-with-preview [picUrl]="question.image"
(imageUploaded)="onImageUploadSuccess($event,'image',question)"></app-upload-image-with-preview>
......
......@@ -21,6 +21,7 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
questionTextAudio: "",
bgAudio: "",
doubleModal: false, // 游戏模式:false为单人模式,true为双人模式
imgSize: 'big', // 统一设置图片大小
questions: [],
};
......@@ -50,7 +51,6 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
addQuestion() {
this.item.questions.push({
type: 'img',
imgSize: 'big', // 默认大图片
audio: '',
image: '',
time: null,
......
......@@ -43,6 +43,9 @@ export interface FormData {
bgAudio: string;
/** 背景音频名称 */
audioBgName?: string;
/** 图片尺寸 */
imgSize: 'big' | 'small';
/** 问题列表 */
questions: Question[];
/** 类型 */
......
......@@ -53,6 +53,7 @@ let defaultFormData: FormData = {
"doubleModal": false,
"questionText": "", "questionType": "read", "questionTextAudio": "",
"bgAudio": "",
imgSize: 'big',
"questions": [
{
"type": "txt", "audio": "http://staging-teach.cdn.ireadabc.com/736c616e8b66a3ae03c8e8636ca46f0f.mp3", "text": "", "image": "http://staging-teach.cdn.ireadabc.com/bde25b1b2a7fdf976f17ae80b6a20a25.png", "time": null, "duration": "", "audioName": "", "imgSize": "big"
......@@ -348,6 +349,7 @@ export class PlayComponent implements OnInit, OnDestroy {
this.clearFishes(); // 清空所有鱼元素
this.m_fishArray = []; // 清空鱼群管理数组
this.m_lastFishTime = 0; // 重置上次生成鱼的时间
this.m_fishTimeAccumulator = 0; // 重置鱼生成的累积时间
this.currentFishIndex = 0; // 重置当前鱼索引
this.clickedFishData = []; // 清空用户点击的鱼的数据
this.touchFish = null; // 清空当前触摸的鱼
......@@ -987,9 +989,26 @@ export class PlayComponent implements OnInit, OnDestroy {
// 修改鱼的生成逻辑,使用当前阶段的随机问题列表
const type = currentQuestion.questionRandomList.length > 3 ? 'small' : 'big';
this.m_fishInterval = type === 'big' ? 4700 : 3500;
this.m_lastFishTime = 0; // 重置上次生成鱼的时间,使下一帧立即生成鱼
const type = this.g_formData.imgSize === 'small' ? 'small' : 'big';
// 计算鱼从右侧游到左侧所需的时间(毫秒)
// 假设鱼的速度为1像素/帧,帧率约为60fps,则速度约为60像素/秒
// 屏幕宽度加上鱼的宽度,再除以速度,得到鱼游过整个屏幕的时间
const fishSpeed = 1; // 鱼的移动速度
const fishWidth = 374 * this.g_mapScale * (type === 'small' ? 0.7 : 1); // 估计的鱼宽度
const screenWidth = this.g_canvasWidth + fishWidth * 0; // 屏幕宽度加上鱼的宽度
const fishCrossingTime = (screenWidth / fishSpeed) * (1000 / 60); // 鱼游过屏幕所需的时间(毫秒)
// 根据imgSize类型确定最大鱼数量
const maxFishCount = type === 'small' ? 4 : 3;
// 计算合适的鱼生成间隔时间
// 鱼游过屏幕的时间除以最大鱼数量,确保屏幕上最多同时有maxFishCount条鱼
this.m_fishInterval = Math.floor(fishCrossingTime / maxFishCount);
console.log(`鱼类型: ${type}, 最大鱼数: ${maxFishCount}, 鱼生成间隔: ${this.m_fishInterval}ms`);
this.m_fishTimeAccumulator = this.m_fishInterval; // 重置累积时间,使下一帧立即生成鱼
// 每一个stage开始的时候播放一次正确问题的音频
if (currentQuestion.correctQuestion && currentQuestion.correctQuestion.audio) {
......@@ -1213,6 +1232,7 @@ export class PlayComponent implements OnInit, OnDestroy {
}
createFish(type, data) {
console.log('createFish', type)
const mapScale = this.g_mapScale > 1 ? 1 : this.g_mapScale;
// 创建鱼元素
const fishId = `fish_${Math.floor(Math.random() * 1000)}_${Date.now()}`
......@@ -2810,14 +2830,45 @@ export class PlayComponent implements OnInit, OnDestroy {
private m_lastFishTime = 0;
private m_fishInterval = 3000; // 每隔10秒生成一条新鱼
// 添加帧率控制相关属性
private m_targetFPS = 60; // 目标帧率:60帧/秒
private m_frameInterval = 1000 / 60; // 每帧间隔时间(毫秒)
private m_lastFrameTime = 0; // 上一帧的时间戳
/**
* 更新游戏画面
* 负责清除画布、更新动画和渲染所有元素
* 控制帧率为60帧/秒,避免因显示频率144Hz导致刷新过快
*/
update() {
this.g_animationId && window.cancelAnimationFrame(this.g_animationId);
// 请求下一帧动画,形成动画循环
this.g_animationId = window.requestAnimationFrame(this.update.bind(this));
// 帧率控制:计算当前时间与上一帧时间的差值
const currentTime = Date.now();
const elapsed = currentTime - this.m_lastFrameTime;
// 计算时间增量(秒)
const dt = elapsed / 1000;
// 如果经过的时间小于帧间隔,则跳过本次更新
if (elapsed < this.m_frameInterval) {
return;
}
// 如果时间间隔过大(大于0.1秒),说明可能是后台运行或性能问题,限制最大时间增量
// 避免因为大的时间间隔导致渲染异常
if (dt > 0.1) {
console.log('时间间隔过大,限制更新:', dt, '秒');
// 更新时间戳但不执行渲染
this.m_lastFrameTime = currentTime;
return;
}
// 更新上一帧时间(考虑实际执行时间的偏差)
this.m_lastFrameTime = currentTime - (elapsed % this.m_frameInterval);
// 清除画布内容,准备绘制新的一帧
this.g_ctx.clearRect(0, 0, this.g_canvasWidth, this.g_canvasHeight);
// 更新TWEEN动画系统
......@@ -2828,8 +2879,8 @@ export class PlayComponent implements OnInit, OnDestroy {
this.updateArr(this.m_endPageArr);
// 更新元素花瓣效果数组中的所有元素
this.updateArr(this.m_elementPetalArr);
// 更新鱼的运动
this.updateFish();
// 更新鱼的运动,传入时间增量dt
this.updateFish(dt);
// 如果游戏已开始,更新时间显示
if (this.isGameStart && this.g_formData.doubleModal) {
......@@ -2880,11 +2931,14 @@ export class PlayComponent implements OnInit, OnDestroy {
/**
* 更新鱼的运动
* 管理鱼从右侧游到左侧的动画效果
* @param dt 时间增量(秒),用于计算鱼的移动距离和生成间隔
*/
private isGameStart = false;
private currentFishIndex = 0;
updateFish() {
private m_fishTimeAccumulator = 0; // 累积的时间,用于鱼的生成
updateFish(dt: number = 0) {
// 如果没有游戏阶段或当前阶段索引超出范围,不生成鱼
if (!this.gameStages || this.currentStageIndex >= this.gameStages.length) {
return;
......@@ -2894,11 +2948,12 @@ export class PlayComponent implements OnInit, OnDestroy {
// 获取当前游戏阶段
const currentStage = this.gameStages[this.currentStageIndex];
// 检查是否需要创建新的鱼
const currentTime = Date.now();
const mapScale = this.g_mapScale < 1 ? 1 : this.g_mapScale;
if (currentTime - this.m_lastFishTime > this.m_fishInterval) {
// 创建新的鱼
// 累积时间,用于鱼的生成
this.m_fishTimeAccumulator += dt * 1000; // 转换为毫秒
// 使用累积的时间来控制鱼的生成频率,从而间接控制屏幕上的鱼的数量
if (this.m_fishTimeAccumulator >= this.m_fishInterval) {
// 创建新的鱼
if (currentStage && currentStage.questionRandomList && currentStage.questionRandomList.length > 0) {
// 从当前阶段的随机问题列表中选择一个问题数据
if (!this.currentFishIndex) this.currentFishIndex = 0;
......@@ -2906,7 +2961,8 @@ export class PlayComponent implements OnInit, OnDestroy {
const fishData = currentStage.questionRandomList[randomIndex];
this.currentFishIndex++;
const type = currentStage.questionRandomList.length > 3 ? 'small' : 'big';
const type = this.g_formData.imgSize == 'small' ? 'small' : 'big';
// 创建小型鱼
const fish = this.createFish(type, fishData);
......@@ -2918,7 +2974,7 @@ export class PlayComponent implements OnInit, OnDestroy {
fish.ref.y = (this.currentFishIndex % 2 == 0 ? 400 : 300) * this.g_mapScale; // Math.random() * (this.g_canvasHeight * 0.35) + this.g_canvasHeight * 0.25; // 在屏幕中间区域随机高度
// 设置鱼的游动速度
fish.speed = 0.45 * 2 * this.g_mapScale;
fish.speed = 1;
// 确保鱼已添加到渲染队列中
// createFish 方法中已经调用了 render 方法,但为了确保,这里再次检查
......@@ -2930,8 +2986,8 @@ export class PlayComponent implements OnInit, OnDestroy {
// 将鱼添加到管理数组
this.m_fishArray.push(fish);
// 更新最后创建鱼的时间
this.m_lastFishTime = currentTime;
// 重置累积时间(减去间隔时间,保留余数,避免时间损失)
this.m_fishTimeAccumulator -= this.m_fishInterval;
}
}
......@@ -2939,8 +2995,13 @@ export class PlayComponent implements OnInit, OnDestroy {
for (let i = this.m_fishArray.length - 1; i >= 0; i--) {
const fish = this.m_fishArray[i];
// 基于时间增量dt计算鱼的移动距离,确保在不同帧率下移动速度一致
// 鱼的基础速度是每秒60像素(1像素/帧 * 60帧/秒)
const baseSpeed = 60; // 每秒移动的像素数
const moveDistance = baseSpeed * dt * fish.speed;
// 向左移动鱼
fish.ref.x -= fish.speed;
fish.ref.x -= moveDistance;
// 如果鱼游出了屏幕左侧,从数组中移除
if (fish.ref.x < -fish.ref.width * fish.ref.scaleX / 2) {
......@@ -3343,6 +3404,10 @@ export class PlayComponent implements OnInit, OnDestroy {
// 初始化舞台
this.initStage();
// 初始化帧率控制的时间戳
this.m_lastFrameTime = Date.now();
// 更新画面渲染
this.update();
}
......
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