feat(menu) 完善菜单场景的功能与效果

This commit is contained in:
wvellichor committed 2026-01-20 01:58:58 +08:00
1 parent 6dbce8bdd5
commit cb5c7ac51b
5 files changed
+467 -13

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
// lang/LanguageManager.js
class LanguageManager {
constructor() {
this.translations = {};
this.currentLanguage = '#^'; // 默认语言
}
/**
* 加载翻译文件
* @param {Object} translations - 翻译对象
*/
loadTranslations(translations) {
this.translations = translations;
}
/**
* 设置当前语言
* @param {string} lang - 语言代码,如 'en', 'zh'
*/
setLanguage(lang) {
if (this.translations[lang]) {
this.currentLanguage = lang;
return true;
}
return false;
}
/**
* 获取翻译文本
* @param {string} key - 文本键值
* @param {string} lang - 可选,指定语言,默认使用当前语言
* @returns {string} 翻译后的文本
*/
t(key, lang = this.currentLanguage) {
if (this.translations[lang] && this.translations[lang][key]) {
return this.translations[lang][key];
}
return key; // 如果没有找到翻译,返回原键值
}
/**
* 获取可用语言列表
* @returns {string[]} 语言代码数组
*/
getAvailableLanguages() {
return Object.keys(this.translations);
}
}
// 导出单例
export default new LanguageManager();
+18
View File
@@ -0,0 +1,18 @@
export default {
"#^": {
"title": "#*^#",
'start': "@%!$%",
"exit": "#@*%"
},
"en": {
"title": "CINC",
"start": "start",
"exit": "exit"
},
"中文": {
"title": "游戏",
"start": "开始",
"exit": "退出"
}
}
+1 -1
View File
@@ -42,8 +42,8 @@ const config = {
...gameConfig, ...gameConfig,
// 指定游戏使用的场景列表,包括主场景和所有测试场景 // 指定游戏使用的场景列表,包括主场景和所有测试场景
scene: [ scene: [
BootScene,
MenuScene, MenuScene,
BootScene,
GameScene, GameScene,
KeyboardMovementTestScene, KeyboardMovementTestScene,
TargetMovementTestScene, TargetMovementTestScene,
+59
View File
@@ -27,6 +27,8 @@ export default class GameScene extends Phaser.Scene {
// create方法:当场景创建时自动调用,用于初始化游戏元素 // create方法:当场景创建时自动调用,用于初始化游戏元素
create() { create() {
// 设置背景颜色
this.cameras.main.setBackgroundColor('#333333')
// === 0. 添加窗口大小变化监听 === // === 0. 添加窗口大小变化监听 ===
// 监听浏览器窗口大小变化事件,当窗口大小改变时调用resizeGame方法 // 监听浏览器窗口大小变化事件,当窗口大小改变时调用resizeGame方法
window.addEventListener('resize', this.resizeGame.bind(this)); window.addEventListener('resize', this.resizeGame.bind(this));
@@ -102,6 +104,63 @@ export default class GameScene extends Phaser.Scene {
x: this.sys.game.config.width - 200 - 10, // 小地图在屏幕上的x坐标 x: this.sys.game.config.width - 200 - 10, // 小地图在屏幕上的x坐标
y: 10 // 小地图在屏幕上的y坐标 y: 10 // 小地图在屏幕上的y坐标
}); });
// === 7. 添加菜单按钮 ===
// 添加返回菜单按钮
const menuButton = this.add.text(
20, // x坐标
20, // y坐标
'返回菜单', // 文本内容
{ fontSize: '16px', color: '#ffffff' } // 样式
).setOrigin(0, 0);
// 为返回菜单按钮添加点击区域
const menuClickArea = this.add.rectangle(
menuButton.x + menuButton.width / 2,
menuButton.y + menuButton.height / 2,
menuButton.width + 20,
menuButton.height + 10,
0x000000,
0 // 透明
).setOrigin(0.5, 0.5);
// 设置返回菜单按钮可交互
menuClickArea.setInteractive({ useHandCursor: true });
// 添加返回菜单按钮点击事件
menuClickArea.on('pointerdown', () => {
console.log('Return to menu clicked');
// 直接切换到菜单场景
this.scene.start('MenuScene');
});
// 添加进入boot菜单按钮
const bootButton = this.add.text(
20, // x坐标
60, // y坐标
'进入Boot菜单', // 文本内容
{ fontSize: '16px', color: '#ffffff' } // 样式
).setOrigin(0, 0);
// 为进入boot菜单按钮添加点击区域
const bootClickArea = this.add.rectangle(
bootButton.x + bootButton.width / 2,
bootButton.y + bootButton.height / 2,
bootButton.width + 20,
bootButton.height + 10,
0x000000,
0 // 透明
).setOrigin(0.5, 0.5);
// 设置进入boot菜单按钮可交互
bootClickArea.setInteractive({ useHandCursor: true });
// 添加进入boot菜单按钮点击事件
bootClickArea.on('pointerdown', () => {
console.log('Enter boot menu clicked');
// 直接切换到boot场景
this.scene.start('BootScene');
});
} }
// update方法:每帧自动调用(约60次/秒),用于更新游戏状态 // update方法:每帧自动调用(约60次/秒),用于更新游戏状态
+338 -12
View File
@@ -1,3 +1,6 @@
import LanguageManager from '../lang/LanguageManager.js';
import translations from '../lang/translations.js';
export default class MenuScene extends Phaser.Scene { export default class MenuScene extends Phaser.Scene {
constructor() { constructor() {
super('MenuScene') super('MenuScene')
@@ -9,20 +12,343 @@ export default class MenuScene extends Phaser.Scene {
// 获取主相机对象 // 获取主相机对象
const cam = this.cameras.main; const cam = this.cameras.main;
// 添加菜单文本 // 添加落雨落效果
this.add.text( this.createRainEffect();
cam.centerX/5, // 文本在x轴上的位置,相机中心的1/5
cam.centerY/5, // 文本在y轴上的位置,相机中心的1/5 // 初始化语言管理器 - 支持多语言
'CINC', LanguageManager.loadTranslations(translations);
{ fontSize: '64px', color: '#ffffff' }
// 添加菜单文本 - 使用多语言
this.titleText = this.add.text(
cam.centerX, // 文本在x轴上的位置,相机中心
cam.centerY - 80, // 文本在y轴上的位置,相机中心上方
LanguageManager.t('title'),
{ fontSize: '24px', color: '#ffffff' }
).setOrigin(0.5, 0.5); ).setOrigin(0.5, 0.5);
this.add.rectangle( // 添加菜单文本 - 使用多语言
cam.centerX/5, // 矩形在x轴上的位置,相机中心的1/5 this.startText = this.add.text(
cam.centerY, // 矩形在y轴上的位置,相机中心的1/5 cam.centerX, // 文本在x轴上的位置,相机中心
10, // 矩形的宽度 cam.centerY, // 文本在y轴上的位置,相机中心
10, // 矩形的高度 LanguageManager.t('start'),
0xffffff // 矩形的颜色,这里是白色 { fontSize: '32px', color: '#ffffff' }
).setOrigin(0.5, 0.5); ).setOrigin(0.5, 0.5);
// 添加退出按钮文本 - 使用多语言
this.exitText = this.add.text(
cam.centerX, // 文本在x轴上的位置,相机中心
cam.centerY + 50, // 文本在y轴上的位置,开始按钮下方
LanguageManager.t('exit'),
{ fontSize: '24px', color: '#ffffff' }
).setOrigin(0.5, 0.5);
// 为开始和退出按钮添加点击功能
this.addButtonClickHandlers();
// 添加语言切换按钮(仅用于测试)
this.createLanguageToggle();
}
/**
* 创建语言切换按钮(使用文本倒影效果)
*/
createLanguageToggle() {
const cam = this.cameras.main;
const languages = LanguageManager.getAvailableLanguages();
let currentLangIndex = languages.indexOf(LanguageManager.currentLanguage);
// 按钮基础位置(在退出按钮下方)
const baseY = cam.centerY + 110;
// 创建主按钮文本
const toggleText = this.add.text(
cam.centerX,
baseY,
`${LanguageManager.currentLanguage}`,
{ fontSize: '16px', color: '#ffffff' }
).setOrigin(0.5, 0.5);
// 创建倒影文本
const reflectionText = this.add.text(
cam.centerX,
baseY + 20, // 倒影位置在主文本下方
`${LanguageManager.currentLanguage}`,
{ fontSize: '16px', color: '#ffffff' }
).setOrigin(0.5, 0.5);
// 设置倒影效果
reflectionText.setScale(1, -1); // 垂直翻转
reflectionText.setAlpha(0.3); // 半透明效果
// 添加动态波纹效果
this.createRippleEffect(reflectionText);
// 创建透明的点击区域(与文本大小匹配)
const clickArea = this.add.rectangle(
cam.centerX,
baseY,
toggleText.width,
toggleText.height,
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 + 40;
// 直接更新菜单文本以显示新语言
this.titleText.setText(LanguageManager.t('title'));
this.startText.setText(LanguageManager.t('start'));
this.exitText.setText(LanguageManager.t('exit'));
});
}
/**
* 为开始和退出按钮添加点击功能
*/
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 - 5, // 字体上移
duration: 100,
ease: 'Sine.easeOut'
});
});
// 添加开始按钮鼠标离开事件
startClickArea.on('pointerout', () => {
this.tweens.add({
targets: this.startText,
y: this.startText.y + 5, // 字体恢复原位
duration: 100,
ease: 'Sine.easeOut'
});
});
// 添加开始按钮点击事件
startClickArea.on('pointerdown', () => {
console.log('Start game clicked');
// 添加进入场景前的过渡动画
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 - 5, // 字体上移
duration: 100,
ease: 'Sine.easeOut'
});
});
// 添加退出按钮鼠标离开事件
exitClickArea.on('pointerout', () => {
this.tweens.add({
targets: this.exitText,
y: this.exitText.y + 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 + 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 // 无限重复
});
}
/**
* 创建雨落下效果
*/
createRainEffect() {
// 创建雨滴精灵组
this.rainGroup = 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), // 随机x位置
Phaser.Math.Between(-screenHeight, 0), // 初始y位置在屏幕上方
2, // 雨滴宽度
10, // 雨滴高度
0xffffff, // 白色
0.3 // 半透明
);
// 添加到组
this.rainGroup.add(raindrop);
// 设置雨滴动画
this.tweens.add({
targets: raindrop,
y: screenHeight + 20, // 下落到底部
duration: Phaser.Math.Between(1000, 2000), // 随机下落速度
ease: 'Linear',
repeat: -1, // 无限重复
delay: Phaser.Math.Between(0, 2000), // 随机延迟,使雨滴分布更均匀
onRepeat: (tween, targets) => {
// 检查目标是否存在,防止场景重启时的错误
if (targets[0] && targets[0].active) {
// 重置雨滴位置
targets[0].x = Phaser.Math.Between(0, screenWidth);
targets[0].y = -20;
}
}
});
}
// 添加雨滴涟漪效果
this.createRainRipples(screenWidth, screenHeight);
}
/**
* 创建雨滴落下的涟漪效果
* @param {number} screenWidth - 屏幕宽度
* @param {number} screenHeight - 屏幕高度
*/
createRainRipples(screenWidth, screenHeight) {
// 涟漪数量
const rippleCount = 15;
for (let i = 0; i < rippleCount; i++) {
// 随机延迟开始
setTimeout(() => {
this.createSingleRipple(screenWidth, screenHeight);
}, Phaser.Math.Between(0, 2000));
}
}
/**
* 创建单个雨滴涟漪
* @param {number} screenWidth - 屏幕宽度
* @param {number} screenHeight - 屏幕高度
*/
createSingleRipple(screenWidth, screenHeight) {
// 随机位置
const x = Phaser.Math.Between(0, screenWidth);
const y = Phaser.Math.Between(0, screenHeight); // 整个屏幕高度
// 创建涟漪圆圈(只保留边框)
const ripple = this.add.circle(
x,
y,
0, // 初始半径
0xffffff, // 白色
0 // 透明填充
).setStrokeStyle(1, 0xffffff, 0.2); // 1像素白色边框,半透明
// 创建扩散动画
this.tweens.add({
targets: ripple,
radius: 20, // 最大半径
alpha: 0, // 逐渐消失
duration: 1000, // 动画持续时间
ease: 'Sine.easeOut',
onComplete: (tween, targets) => {
// 动画完成后销毁涟漪
if (targets[0] && targets[0].active) {
targets[0].destroy();
}
// 递归创建新的涟漪
this.createSingleRipple(screenWidth, screenHeight);
}
});
} }
} }