mirror of
http://junepve.top:3000/chicrobot/CINC.git
synced 2026-10-30 00:31:29 +08:00
747 lines
28 KiB
JavaScript
747 lines
28 KiB
JavaScript
import LanguageManager from '../lang/LanguageManager.js';
|
|
import translations from '../lang/translations.js';
|
|
|
|
export default class MenuScene extends Phaser.Scene {
|
|
constructor() {
|
|
super('MenuScene')
|
|
}
|
|
|
|
create() {
|
|
// 设置背景颜色
|
|
this.cameras.main.setBackgroundColor('#333333')
|
|
// 获取主相机对象
|
|
const cam = this.cameras.main;
|
|
|
|
// 获取屏幕尺寸,用于计算相对单位
|
|
this.screenWidth = this.sys.game.config.width;
|
|
this.screenHeight = this.sys.game.config.height;
|
|
|
|
// 计算相对单位:以屏幕高度为基准
|
|
this.unit = this.screenHeight / 100;
|
|
|
|
// 初始化雨天效果状态
|
|
this.isRainEnabled = true;
|
|
|
|
// 初始化setTimeout ID数组,用于管理所有与涟漪相关的延迟调用
|
|
this.rippleTimeouts = [];
|
|
|
|
// 创建文本container
|
|
this.textContainer = this.add.container(0, 0);
|
|
|
|
// 添加落雨落效果
|
|
this.createRainEffect();
|
|
|
|
// 初始化语言管理器 - 支持多语言
|
|
LanguageManager.loadTranslations(translations);
|
|
|
|
// 添加菜单文本 - 使用多语言
|
|
this.titleText = this.add.text(
|
|
cam.centerX, // 文本在x轴上的位置,相机中心
|
|
cam.centerY - this.unit * 12, // 文本在y轴上的位置,相机中心上方
|
|
LanguageManager.t('title'),
|
|
{ fontSize: this.unit * 2.4, color: '#ffffff', fontFamily: 'monospace'}
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 为标题文本添加倒影
|
|
this.createTextReflection(this.titleText);
|
|
|
|
// 添加菜单文本 - 使用多语言
|
|
this.startText = this.add.text(
|
|
cam.centerX, // 文本在x轴上的位置,相机中心
|
|
cam.centerY, // 文本在y轴上的位置,相机中心
|
|
LanguageManager.t('start'),
|
|
{ fontSize: this.unit * 3.2, color: '#ffffff', fontFamily: 'monospace'}
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 为开始文本添加倒影
|
|
this.createTextReflection(this.startText);
|
|
|
|
// 添加退出按钮文本 - 使用多语言
|
|
this.exitText = this.add.text(
|
|
cam.centerX, // 文本在x轴上的位置,相机中心
|
|
cam.centerY + this.unit * 10, // 文本在y轴上的位置,开始按钮下方
|
|
LanguageManager.t('exit'),
|
|
{ fontSize: this.unit * 2.4, color: '#ffffff', fontFamily: 'monospace'}
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 为退出文本添加倒影
|
|
this.createTextReflection(this.exitText);
|
|
|
|
// 为开始和退出按钮添加点击功能
|
|
this.addButtonClickHandlers();
|
|
|
|
// 添加语言切换按钮(仅用于测试)
|
|
this.createLanguageToggle();
|
|
|
|
// 添加雨天效果控制按钮
|
|
this.createRainControlButton();
|
|
|
|
// 添加页面可见性变化监听,处理页面切出和切回的情况
|
|
this.setupVisibilityChangeHandler();
|
|
}
|
|
|
|
/**
|
|
* 创建语言切换按钮(使用文本倒影效果)
|
|
*/
|
|
createLanguageToggle() {
|
|
const cam = this.cameras.main;
|
|
const languages = LanguageManager.getAvailableLanguages();
|
|
let currentLangIndex = languages.indexOf(LanguageManager.currentLanguage);
|
|
|
|
// 按钮基础位置(在退出按钮下方)
|
|
const baseY = cam.centerY + this.unit * 18;
|
|
|
|
// 创建主按钮文本
|
|
const toggleText = this.add.text(
|
|
cam.centerX,
|
|
baseY,
|
|
`${LanguageManager.currentLanguage}`,
|
|
{ fontSize: this.unit * 1.6, color: '#ffffff', fontFamily: 'monospace'}
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 使用createTextReflection方法创建倒影
|
|
const reflectionText = this.createTextReflection(toggleText);
|
|
|
|
// 创建透明的点击区域(与文本大小匹配)
|
|
const clickArea = this.add.rectangle(
|
|
cam.centerX,
|
|
baseY + this.unit * 0.5,
|
|
toggleText.width,
|
|
toggleText.height + this.unit,
|
|
0x000000,
|
|
0 // 完全透明
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 设置点击区域可交互
|
|
clickArea.setInteractive({ useHandCursor: true });
|
|
|
|
// 添加点击事件
|
|
clickArea.on('pointerdown', () => {
|
|
currentLangIndex = (currentLangIndex + 1) % languages.length;
|
|
const newLang = languages[currentLangIndex];
|
|
LanguageManager.setLanguage(newLang);
|
|
|
|
// 更新文本
|
|
const newText = `${newLang}`;
|
|
toggleText.setText(newText);
|
|
reflectionText.setText(newText);
|
|
|
|
// 重新计算点击区域大小
|
|
clickArea.width = toggleText.width + 20;
|
|
clickArea.height = toggleText.height + 20;
|
|
|
|
// 直接更新菜单文本以显示新语言
|
|
this.titleText.setText(LanguageManager.t('title'));
|
|
this.startText.setText(LanguageManager.t('start'));
|
|
this.exitText.setText(LanguageManager.t('exit'));
|
|
|
|
// 同步更新倒影文本
|
|
if (this.titleText.reflection) {
|
|
this.titleText.reflection.setText(LanguageManager.t('title'));
|
|
}
|
|
if (this.startText.reflection) {
|
|
this.startText.reflection.setText(LanguageManager.t('start'));
|
|
}
|
|
if (this.exitText.reflection) {
|
|
this.exitText.reflection.setText(LanguageManager.t('exit'));
|
|
}
|
|
|
|
// 更新雨天控制按钮文本
|
|
const rainText = LanguageManager.t('rain');
|
|
const stateText = this.isRainEnabled ? LanguageManager.t('on') : LanguageManager.t('off');
|
|
this.rainToggleText.setText(`${rainText} ${stateText}`);
|
|
|
|
// 同步更新雨天控制按钮的倒影文本
|
|
if (this.rainToggleText.reflection) {
|
|
this.rainToggleText.reflection.setText(`${rainText} ${stateText}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 创建雨天效果控制按钮
|
|
*/
|
|
createRainControlButton() {
|
|
const cam = this.cameras.main;
|
|
|
|
// 按钮基础位置(在语言切换按钮下方)
|
|
const baseY = cam.centerY + this.unit * 26;
|
|
|
|
// 创建主按钮文本
|
|
this.rainToggleText = this.add.text(
|
|
cam.centerX,
|
|
baseY,
|
|
LanguageManager.t('rain')+" "+LanguageManager.t('on'),
|
|
{ fontSize: this.unit * 1.6, color: '#ffffff', fontFamily: 'monospace'}
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 使用createTextReflection方法创建倒影
|
|
this.createTextReflection(this.rainToggleText);
|
|
|
|
// 创建透明的点击区域(与文本大小匹配)
|
|
this.rainClickArea = this.add.rectangle(
|
|
cam.centerX,
|
|
baseY + this.unit * 0.5,
|
|
this.rainToggleText.width + 40,
|
|
this.rainToggleText.height + 20,
|
|
0x000000,
|
|
0 // 完全透明
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 设置点击区域可交互
|
|
this.rainClickArea.setInteractive({ useHandCursor: true });
|
|
|
|
// 添加点击事件
|
|
this.rainClickArea.on('pointerdown', () => {
|
|
this.toggleRainEffect();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 切换雨天效果的开启和关闭
|
|
*/
|
|
toggleRainEffect() {
|
|
// 切换雨天效果状态
|
|
this.isRainEnabled = !this.isRainEnabled;
|
|
|
|
// 更新按钮文本
|
|
this.rainToggleText.setText(this.isRainEnabled ? `${LanguageManager.t('rain')} ${LanguageManager.t('on')}` : `${LanguageManager.t('rain')} ${LanguageManager.t('off')}`);
|
|
|
|
// 同步更新倒影文本
|
|
if (this.rainToggleText.reflection) {
|
|
this.rainToggleText.reflection.setText(this.isRainEnabled ? `${LanguageManager.t('rain')} ${LanguageManager.t('on')}` : `${LanguageManager.t('rain')} ${LanguageManager.t('off')}`);
|
|
}
|
|
|
|
// 控制雨滴和涟漪的显示/隐藏
|
|
this.updateRainEffectVisibility();
|
|
|
|
// 如果关闭雨天效果,清除所有与涟漪相关的延迟调用、Tween动画和涟漪对象
|
|
if (!this.isRainEnabled) {
|
|
// 清除所有涟漪相关的setTimeout
|
|
this.rippleTimeouts.forEach(timeoutId => {
|
|
clearTimeout(timeoutId);
|
|
});
|
|
// 清空timeout ID数组
|
|
this.rippleTimeouts = [];
|
|
|
|
// 清除所有与涟漪相关的Tween动画
|
|
this.tweens.killTweensOf(this.rippleGroup.getChildren());
|
|
|
|
// 彻底清除所有涟漪对象
|
|
this.rippleGroup.children.each(ripple => {
|
|
if (ripple && ripple.active) {
|
|
ripple.destroy();
|
|
}
|
|
});
|
|
|
|
// 清空涟漪组
|
|
this.rippleGroup.clear(true, true);
|
|
}
|
|
// 如果重新开启雨天效果,重新启动涟漪生成
|
|
else {
|
|
// 重新启动涟漪生成
|
|
this.createRainRipples(this.screenWidth, this.screenHeight);
|
|
|
|
// 检查并重新启动雨滴的Tween动画(如果需要)
|
|
this.rainGroup.children.each(raindrop => {
|
|
// 检查雨滴是否存在且激活
|
|
if (raindrop && raindrop.active) {
|
|
// 获取雨滴的当前Tween动画
|
|
const tweens = this.tweens.getTweensOf(raindrop);
|
|
|
|
// 如果没有运行中的Tween动画,重新创建雨滴动画
|
|
if (tweens.length === 0) {
|
|
// 水平方向的位移量(相对单位),固定向左下方倾斜
|
|
const horizontalOffset = -this.unit * Phaser.Math.Between(15, 17); // 固定向左方向,8-12单位
|
|
|
|
// 重新设置雨滴动画
|
|
this.tweens.add({
|
|
targets: raindrop,
|
|
x: raindrop.x + horizontalOffset, // 水平方向位移
|
|
y: this.screenHeight + this.unit * 2, // 垂直方向位移
|
|
duration: Phaser.Math.Between(1000, 1050), // 随机下落速度
|
|
ease: 'Linear',
|
|
repeat: -1, // 无限重复
|
|
delay: Phaser.Math.Between(0, 2000), // 随机延迟,使雨滴分布更均匀
|
|
onRepeat: (tween, targets) => {
|
|
// 检查目标是否存在,防止场景重启时的错误
|
|
if (targets[0] && targets[0].active) {
|
|
// 重置雨滴位置,考虑到斜向下落,初始x位置可以稍微超出屏幕边缘(相对单位)
|
|
const initialX = Phaser.Math.Between(-this.unit * 10, this.screenWidth + this.unit * 10);
|
|
targets[0].x = initialX;
|
|
targets[0].y = -this.unit * 2;
|
|
|
|
// 更新动画的x目标值
|
|
tween.data[0].value = initialX + horizontalOffset;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置页面可见性变化处理器
|
|
*/
|
|
setupVisibilityChangeHandler() {
|
|
// 保存当前场景的引用
|
|
const scene = this;
|
|
|
|
// 页面可见性变化事件处理函数
|
|
const handleVisibilityChange = () => {
|
|
if (document.hidden) {
|
|
// 页面变为不可见时
|
|
scene.onPageHidden();
|
|
} else {
|
|
// 页面变为可见时
|
|
scene.onPageVisible();
|
|
}
|
|
};
|
|
|
|
// 添加事件监听器
|
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
|
|
// 在场景销毁时移除事件监听器,防止内存泄漏
|
|
this.events.on('destroy', () => {
|
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 更新雨天效果的可见性
|
|
*/
|
|
updateRainEffectVisibility() {
|
|
// 控制雨滴container的显示/隐藏
|
|
this.rainContainer.visible = this.isRainEnabled;
|
|
}
|
|
|
|
/**
|
|
* 页面变为不可见时的处理逻辑
|
|
*/
|
|
onPageHidden() {
|
|
// 如果雨天效果是开启的,清除所有涟漪相关的延迟调用、Tween动画和涟漪对象
|
|
if (this.isRainEnabled) {
|
|
// 清除所有涟漪相关的setTimeout
|
|
this.rippleTimeouts.forEach(timeoutId => {
|
|
clearTimeout(timeoutId);
|
|
});
|
|
// 清空timeout ID数组
|
|
this.rippleTimeouts = [];
|
|
|
|
// 清除所有与涟漪相关的Tween动画
|
|
this.tweens.killTweensOf(this.rippleGroup.getChildren());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 页面重新变为可见时的处理逻辑
|
|
*/
|
|
onPageVisible() {
|
|
// 如果雨天效果是开启的,重新启动涟漪生成
|
|
if (this.isRainEnabled) {
|
|
// 清理所有残留的涟漪对象
|
|
this.rippleGroup.children.each(ripple => {
|
|
if (ripple && ripple.active) {
|
|
ripple.destroy();
|
|
}
|
|
});
|
|
|
|
// 清空涟漪组
|
|
this.rippleGroup.clear(true, true);
|
|
|
|
// 重新启动涟漪生成
|
|
this.createRainRipples(this.screenWidth, this.screenHeight);
|
|
|
|
// 检查并重新启动雨滴的Tween动画(如果需要)
|
|
this.rainGroup.children.each(raindrop => {
|
|
// 检查雨滴是否存在且激活
|
|
if (raindrop && raindrop.active) {
|
|
// 获取雨滴的当前Tween动画
|
|
const tweens = this.tweens.getTweensOf(raindrop);
|
|
|
|
// 如果没有运行中的Tween动画,重新创建雨滴动画
|
|
if (tweens.length === 0) {
|
|
// 水平方向的位移量(相对单位),固定向左下方倾斜
|
|
const horizontalOffset = -this.unit * Phaser.Math.Between(15, 17); // 固定向左方向,8-12单位
|
|
|
|
// 重新设置雨滴动画
|
|
this.tweens.add({
|
|
targets: raindrop,
|
|
x: raindrop.x + horizontalOffset, // 水平方向位移
|
|
y: this.screenHeight + this.unit * 2, // 垂直方向位移
|
|
duration: Phaser.Math.Between(1000, 1050), // 随机下落速度
|
|
ease: 'Linear',
|
|
repeat: -1, // 无限重复
|
|
delay: Phaser.Math.Between(0, 2000), // 随机延迟,使雨滴分布更均匀
|
|
onRepeat: (tween, targets) => {
|
|
// 检查目标是否存在,防止场景重启时的错误
|
|
if (targets[0] && targets[0].active) {
|
|
// 重置雨滴位置,考虑到斜向下落,初始x位置可以稍微超出屏幕边缘(相对单位)
|
|
const initialX = Phaser.Math.Between(-this.unit * 10, this.screenWidth + this.unit * 10);
|
|
targets[0].x = initialX;
|
|
targets[0].y = -this.unit * 2;
|
|
|
|
// 更新动画的x目标值
|
|
tween.data[0].value = initialX + horizontalOffset;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 为开始和退出按钮添加点击功能
|
|
*/
|
|
addButtonClickHandlers() {
|
|
// 为开始文本添加点击区域
|
|
const startClickArea = this.add.rectangle(
|
|
this.startText.x,
|
|
this.startText.y,
|
|
this.startText.width + 40, // 增加点击区域宽度
|
|
this.startText.height + 20, // 增加点击区域高度
|
|
0x000000,
|
|
0 // 透明
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 设置开始按钮可交互
|
|
startClickArea.setInteractive({ useHandCursor: true });
|
|
|
|
// 添加开始按钮鼠标悬停事件
|
|
startClickArea.on('pointerover', () => {
|
|
this.tweens.add({
|
|
targets: this.startText,
|
|
y: this.startText.y - this.unit * 0.5, // 字体上移
|
|
duration: 100,
|
|
ease: 'Sine.easeOut'
|
|
});
|
|
});
|
|
|
|
// 添加开始按钮鼠标离开事件
|
|
startClickArea.on('pointerout', () => {
|
|
this.tweens.add({
|
|
targets: this.startText,
|
|
y: this.startText.y + this.unit * 0.5, // 字体恢复原位
|
|
duration: 100,
|
|
ease: 'Sine.easeOut'
|
|
});
|
|
});
|
|
|
|
// 添加开始按钮点击事件
|
|
startClickArea.on('pointerdown', () => {
|
|
console.log('Start game clicked');
|
|
|
|
// 清除所有与涟漪相关的延迟调用,避免场景切换后仍然执行
|
|
this.rippleTimeouts.forEach(timeoutId => {
|
|
clearTimeout(timeoutId);
|
|
});
|
|
this.rippleTimeouts = [];
|
|
|
|
// 添加进入场景前的过渡动画
|
|
this.cameras.main.fadeOut(500, 0, 0, 0);
|
|
this.cameras.main.once('camerafadeoutcomplete', () => {
|
|
// 切换到游戏场景
|
|
this.scene.start('GameScene');
|
|
});
|
|
});
|
|
|
|
// 为退出文本添加点击区域
|
|
const exitClickArea = this.add.rectangle(
|
|
this.exitText.x,
|
|
this.exitText.y,
|
|
this.exitText.width + 40, // 增加点击区域宽度
|
|
this.exitText.height + 20, // 增加点击区域高度
|
|
0x000000,
|
|
0 // 透明
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 设置退出按钮可交互
|
|
exitClickArea.setInteractive({ useHandCursor: true });
|
|
|
|
// 添加退出按钮鼠标悬停事件
|
|
exitClickArea.on('pointerover', () => {
|
|
this.tweens.add({
|
|
targets: this.exitText,
|
|
y: this.exitText.y - this.unit * 0.5, // 字体上移
|
|
duration: 100,
|
|
ease: 'Sine.easeOut'
|
|
});
|
|
});
|
|
|
|
// 添加退出按钮鼠标离开事件
|
|
exitClickArea.on('pointerout', () => {
|
|
this.tweens.add({
|
|
targets: this.exitText,
|
|
y: this.exitText.y + this.unit * 0.5, // 字体恢复原位
|
|
duration: 100,
|
|
ease: 'Sine.easeOut'
|
|
});
|
|
});
|
|
|
|
// 添加退出按钮点击事件
|
|
exitClickArea.on('pointerdown', () => {
|
|
console.log('Exit game clicked');
|
|
// 关闭游戏(在浏览器中关闭窗口)
|
|
window.close();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 为文本添加波纹效果,模拟水下倒影
|
|
* @param {Phaser.GameObjects.Text} text - 要添加波纹效果的文本
|
|
*/
|
|
createRippleEffect(text) {
|
|
// 保存原始位置
|
|
const originalX = text.x;
|
|
const originalY = text.y;
|
|
|
|
// 创建波纹动画
|
|
this.tweens.add({
|
|
targets: text,
|
|
y: originalY + this.unit * 0.5, // 上下波动
|
|
duration: 1500,
|
|
ease: 'Sine.easeInOut',
|
|
yoyo: true,
|
|
repeat: -1 // 无限重复
|
|
});
|
|
|
|
// 添加轻微的缩放动画
|
|
this.tweens.add({
|
|
targets: text,
|
|
scaleY: -1.05, // 垂直方向轻微缩放(保持负值,继续翻转)
|
|
duration: 2000,
|
|
ease: 'Sine.easeInOut',
|
|
yoyo: true,
|
|
repeat: -1 // 无限重复
|
|
});
|
|
|
|
// 添加透明度变化
|
|
this.tweens.add({
|
|
targets: text,
|
|
alpha: 0.2, // 透明度变化
|
|
duration: 2500,
|
|
ease: 'Sine.easeInOut',
|
|
yoyo: true,
|
|
repeat: -1 // 无限重复
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 为文本创建倒影效果
|
|
* @param {Phaser.GameObjects.Text} text - 要创建倒影的文本
|
|
*/
|
|
createTextReflection(text) {
|
|
// 创建倒影文本
|
|
const reflectionText = this.add.text(
|
|
text.x,
|
|
text.y + text.height, // 倒影位置在主文本下方,间隔为0
|
|
text.text,
|
|
{ fontSize: text.style.fontSize, color: '#ffffff', fontFamily: text.style.fontFamily}
|
|
).setOrigin(0.5, 0.5);
|
|
|
|
// 设置倒影效果
|
|
reflectionText.setScale(1, -1); // 垂直翻转
|
|
reflectionText.setAlpha(0.3); // 半透明效果
|
|
|
|
// 将主文本和倒影文本添加到container中
|
|
this.textContainer.add(text);
|
|
this.textContainer.add(reflectionText);
|
|
|
|
// 将倒影文本引用存储在主文本对象中,便于后续同步更新
|
|
text.reflection = reflectionText;
|
|
|
|
// 添加动态波纹效果
|
|
this.createRippleEffect(reflectionText);
|
|
|
|
return reflectionText;
|
|
}
|
|
|
|
/**
|
|
* 创建雨落下效果
|
|
*/
|
|
createRainEffect() {
|
|
// 创建雨滴container
|
|
this.rainContainer = this.add.container(0, 0);
|
|
|
|
// 创建雨滴精灵组
|
|
this.rainGroup = this.add.group();
|
|
|
|
// 创建涟漪组,用于管理所有涟漪效果
|
|
this.rippleGroup = this.add.group();
|
|
|
|
// 雨的参数
|
|
const rainCount = 50; // 雨滴数量
|
|
const screenWidth = this.sys.game.config.width;
|
|
const screenHeight = this.sys.game.config.height;
|
|
|
|
// 创建雨滴
|
|
for (let i = 0; i < rainCount; i++) {
|
|
// 创建雨滴精灵(使用简单的白色矩形)
|
|
const raindrop = this.add.rectangle(
|
|
Phaser.Math.Between(0, screenWidth * 1.2), // 随机x位置,从屏幕右侧外开始
|
|
Phaser.Math.Between(-screenHeight, 0), // 初始y位置在屏幕上方
|
|
this.unit * 0.2, // 雨滴宽度
|
|
this.unit * 1, // 雨滴高度
|
|
0xffffff, // 白色
|
|
0.3 // 半透明
|
|
);
|
|
|
|
// 将雨滴添加到container中
|
|
this.rainContainer.add(raindrop);
|
|
|
|
// 添加到组
|
|
this.rainGroup.add(raindrop);
|
|
|
|
// 设置雨滴动画 - 斜向落下(固定方向)
|
|
// 水平方向的位移量(相对单位),固定向左下方倾斜
|
|
const horizontalOffset = -this.unit * Phaser.Math.Between(15, 17); // 固定向左方向,8-12单位
|
|
|
|
// 设置雨滴动画
|
|
this.tweens.add({
|
|
targets: raindrop,
|
|
x: raindrop.x + horizontalOffset, // 水平方向位移
|
|
y: screenHeight + this.unit * 2, // 垂直方向位移
|
|
duration: Phaser.Math.Between(1000, 1050), // 随机下落速度
|
|
ease: 'Linear',
|
|
repeat: -1, // 无限重复
|
|
delay: Phaser.Math.Between(0, 2000), // 随机延迟,使雨滴分布更均匀
|
|
onRepeat: (tween, targets) => {
|
|
// 检查目标是否存在,防止场景重启时的错误
|
|
if (targets[0] && targets[0].active) {
|
|
// 重置雨滴位置,考虑到斜向下落,初始x位置可以稍微超出屏幕边缘(相对单位)
|
|
const initialX = Phaser.Math.Between(-this.unit * 10, screenWidth + this.unit * 10);
|
|
targets[0].x = initialX;
|
|
targets[0].y = -this.unit * 2;
|
|
|
|
// 更新动画的x目标值
|
|
tween.data[0].value = initialX + horizontalOffset;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 添加雨滴涟漪效果
|
|
this.createRainRipples(screenWidth, screenHeight);
|
|
}
|
|
|
|
/**
|
|
* 创建雨滴落下的涟漪效果
|
|
* @param {number} screenWidth - 屏幕宽度
|
|
* @param {number} screenHeight - 屏幕高度
|
|
*/
|
|
createRainRipples(screenWidth, screenHeight) {
|
|
// 只有在雨天效果开启时,才创建涟漪
|
|
if (this.isRainEnabled) {
|
|
// 增加涟漪数量
|
|
const rippleCount = 25;
|
|
|
|
for (let i = 0; i < rippleCount; i++) {
|
|
// 随机延迟开始
|
|
const timeoutId = setTimeout(() => {
|
|
// 确保场景仍然存在且 rippleGroup 存在
|
|
if (this.scene && this.rippleGroup) {
|
|
this.createSingleRipple(screenWidth, screenHeight);
|
|
}
|
|
}, Phaser.Math.Between(0, 3000));
|
|
|
|
// 将timeout ID保存到数组中
|
|
this.rippleTimeouts.push(timeoutId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建单个雨滴涟漪
|
|
* @param {number} screenWidth - 屏幕宽度
|
|
* @param {number} screenHeight - 屏幕高度
|
|
*/
|
|
createSingleRipple(screenWidth, screenHeight) {
|
|
// 随机位置
|
|
const x = Phaser.Math.Between(0, screenWidth);
|
|
const y = Phaser.Math.Between(0, screenHeight); // 整个屏幕高度
|
|
|
|
// 每个雨滴产生多个同心涟漪
|
|
const rippleLayers = 3;
|
|
const baseDelay = 100;
|
|
|
|
for (let i = 0; i < rippleLayers; i++) {
|
|
// 延迟创建后续的涟漪层
|
|
const timeoutId = setTimeout(() => {
|
|
// 只有在雨天效果开启时,才创建新的涟漪
|
|
if (this.isRainEnabled && this.rippleGroup) {
|
|
// 创建涟漪圆圈(只保留边框)
|
|
const ripple = this.add.circle(
|
|
x,
|
|
y,
|
|
0, // 初始半径
|
|
0xffffff, // 白色
|
|
0 // 透明填充
|
|
).setStrokeStyle(this.unit * 0.08, 0xffffff, 0.3); // 更细的边框,更高的透明度
|
|
|
|
// 将涟漪添加到雨滴container中
|
|
this.rainContainer.add(ripple);
|
|
|
|
// 将涟漪添加到涟漪组中
|
|
this.rippleGroup.add(ripple);
|
|
|
|
// 随机化每个涟漪的参数,增加自然感
|
|
const maxRadius = this.unit * Phaser.Math.Between(2, 3);
|
|
const duration = Phaser.Math.Between(800, 1500);
|
|
const startAlpha = Phaser.Math.Between(0.3, 0.5);
|
|
|
|
// 设置初始透明度
|
|
ripple.setAlpha(startAlpha);
|
|
|
|
// 创建扩散动画
|
|
this.tweens.add({
|
|
targets: ripple,
|
|
radius: maxRadius, // 最大半径
|
|
alpha: 0, // 逐渐消失
|
|
duration: duration, // 动画持续时间
|
|
ease: 'Sine.easeOut',
|
|
onComplete: (tween, targets) => {
|
|
// 动画完成后销毁涟漪
|
|
// 确保目标对象仍然存在且未被销毁
|
|
if (targets && targets[0] && targets[0].scene && targets[0].active) {
|
|
targets[0].destroy();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}, i * baseDelay);
|
|
|
|
// 将timeout ID保存到数组中
|
|
this.rippleTimeouts.push(timeoutId);
|
|
}
|
|
|
|
// 只有在雨天效果开启时,才递归创建新的涟漪
|
|
if (this.isRainEnabled) {
|
|
const timeoutId = setTimeout(() => {
|
|
this.createSingleRipple(screenWidth, screenHeight);
|
|
}, Phaser.Math.Between(500, 1500));
|
|
|
|
// 将递归调用的timeout ID保存到数组中
|
|
this.rippleTimeouts.push(timeoutId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 场景关闭时的清理工作
|
|
*/
|
|
shutdown() {
|
|
// 清除所有与涟漪相关的延迟调用
|
|
this.rippleTimeouts.forEach(timeoutId => {
|
|
clearTimeout(timeoutId);
|
|
});
|
|
|
|
// 清空timeout数组
|
|
this.rippleTimeouts = [];
|
|
|
|
// 调用父类的shutdown方法
|
|
super.shutdown();
|
|
}
|
|
} |