import { DefaultProps } from '@motajs/render-vue'; import { GameUI, SetupComponentOptions, UIComponentProps } from '@motajs/system'; import { defineComponent, nextTick, onMounted, ref } from 'vue'; import { BUTTONS_HEIGHT, BUTTONS_WIDTH, BUTTONS_X, BUTTONS_Y, HALF_HEIGHT, HALF_WIDTH, MAIN_HEIGHT, MAIN_WIDTH, TITLE_BACKGROUND_IMAGE, TITLE_FILL, TITLE_STROKE, TITLE_STROKE_WIDTH, TITLE_X, TITLE_Y } from '../shared'; import { ElementLocator, Font } from '@motajs/render'; import { ITransitionedController, transitioned, transitionedColor, useKey } from '../use'; import { ExitFullscreen, Fullscreen, SoundVolume } from '../components'; import { mainSetting, triggerFullscreen } from '@motajs/legacy-ui'; import { saveLoad } from './save'; import { MainSceneUI } from './main'; import { adjustCover } from '../utils'; import { cosh, CurveMode, linear } from '@motajs/animate'; import { sleep } from '@motajs/common'; import { materials } from '@user/client-base'; const enum TitleButton { StartGame, LoadGame, Replay } interface ButtonItem { code: TitleButton; name: string; color: string; } interface ButtonOption { code: number; color: string; name: string; hard: string; colorTrans: ITransitionedController; } export interface GameTitleProps extends DefaultProps, UIComponentProps {} const gameTitleProps = { props: ['controller', 'instance'] } satisfies SetupComponentOptions; export const GameTitle = defineComponent(props => { const bg = materials.getImageByAlias(TITLE_BACKGROUND_IMAGE); //#region 计算背景图 const [width, height] = adjustCover( bg?.width ?? MAIN_WIDTH, bg?.height ?? MAIN_HEIGHT, MAIN_WIDTH, MAIN_HEIGHT ); //#region 标题设置 /** 当前是否全屏 */ const fullscreen = ref(!!document.fullscreenElement); /** 是否开启了声音 */ const soundOpened = ref(true); /** 是否在选择难度界面 */ const selectHard = ref(false); /** 开始界面按钮定义,你可以在这新增自己的按钮 */ const buttonItems: ButtonItem[] = [ { code: TitleButton.StartGame, color: 'rgb(40, 194, 255)', name: '开始游戏' }, { code: TitleButton.LoadGame, color: 'rgb(0, 255, 55)', name: '读取存档' }, { code: TitleButton.Replay, color: 'rgb(255, 251, 0)', name: '录像回放' } ]; /** 开始界面按钮 */ const buttons = buttonItems.map(v => { return { code: v.code, color: v.color, name: v.name, hard: '', colorTrans: transitionedColor( '#fff', 400, cosh(2, CurveMode.EaseOut) )!, scale: transitioned(1, 400, cosh(2, CurveMode.EaseOut))! }; }); /** 选择难度界面按钮 */ const hard = main.levelChoose.map(v => { return { code: v.hard, color: core.arrayToRGBA(v.color!), name: v.title, hard: v.name, colorTrans: transitionedColor( '#fff', 400, cosh(2, CurveMode.EaseOut) )!, scale: transitioned(1, 400, cosh(2, CurveMode.EaseOut))! }; }); // 返回按钮 hard.push({ code: -1, color: '#aaa', name: '返回', hard: '', colorTrans: transitionedColor('#fff', 400, cosh(2, CurveMode.EaseOut))! }); /** 声音设置按钮的颜色 */ const soundColor = transitionedColor( '#ddd', 400, cosh(2, CurveMode.EaseOut) )!; /** 开始界面按钮的不透明度,选择难度界面的不透明度使用 `1-buttonsAlpha` 计算 */ const buttonsAlpha = transitioned(1, 300, linear())!; /** 开始界面的不透明度 */ const mainAlpha = transitioned(0, 600, linear())!; const buttonFilter = ` drop-shadow(3px 3px 5px rgba(0, 0, 0, 0.4)) drop-shadow(0px 0px 2px rgba(255, 255, 255, 0.5)) `; const titleFont = Font.defaults({ size: 72 }); const buttonFont = Font.defaults({ size: 24, weight: 600 }); /** 开始界面按钮的高度 */ const buttonHeight = (buttons.length - 1) * 40 + 60; /** 选择难度界面按钮的高度 */ const hardHeight = (hard.length - 1) * 40 + 60; /** 按钮的背景框高度 */ const rectHeight = transitioned( buttonHeight, 600, cosh(2, CurveMode.EaseOut) )!; //#region 按钮功能 /** * 在开始界面和选择难度界面切换 */ const toggleHard = async () => { if (selectHard.value) { // 当前在难度界面,将要切换至开始界面 enterMain(0); rectHeight.set(buttonHeight); } else { // 当前在开始界面,将要切换至难度界面 enterHard(0); rectHeight.set(hardHeight); } buttonsAlpha.set(0); await sleep(300); selectHard.value = !selectHard.value; buttonsAlpha.set(1); }; /** * 点击读取存档按钮 */ const loadGame = async () => { const loc: ElementLocator = [0, 0, MAIN_WIDTH, MAIN_HEIGHT]; const success = await saveLoad(props.controller, loc); if (success) { props.controller.close(props.instance); props.controller.open(MainSceneUI, {}); } }; /** * 点击录像回放按钮 */ const replay = () => { core.chooseReplayFile(); }; /** * 选择难度并开始游戏 * @param hard 选择的难度 */ const startGame = async (hard: string) => { mainAlpha.set(0); await sleep(600); props.controller.close(props.instance); props.controller.open(MainSceneUI, {}); nextTick(() => { core.startGame(hard); }); }; /** * 点击按钮时触发 * @param code 选中的按钮的代码 */ const clickButton = (code: number, index: number) => { if (selectHard.value) { if (index === hard.length - 1) { // 选中了最后一个按钮 toggleHard(); return; } const item = hard[index]; startGame(item.name); } else { switch (code) { case TitleButton.StartGame: toggleHard(); break; case TitleButton.LoadGame: loadGame(); break; case TitleButton.Replay: replay(); break; } } }; //#region 键盘操作 const selected = ref(0); const [key] = useKey(); key.realize( '@start_up', () => { selected.value--; if (selected.value < 0) { selected.value = 0; } if (selectHard.value) { enterHard(selected.value); } else { enterMain(selected.value); } }, { type: 'down' } ) .realize( '@start_down', () => { selected.value++; if (selectHard.value) { if (selected.value > hard.length - 1) { selected.value = hard.length - 1; } enterHard(selected.value); } else { if (selected.value > buttons.length - 1) { selected.value = buttons.length - 1; } enterMain(selected.value); } }, { type: 'down' } ) .realize('confirm', () => { if (selectHard.value) { clickButton(hard[selected.value].code, selected.value); } else { clickButton(buttons[selected.value].code, selected.value); } }); //#region 鼠标操作 soundOpened.value = mainSetting.getValue('audio.bgmEnabled', true); if (soundOpened.value) { soundColor.set('#ddd'); } else { soundColor.set('#d22'); } const enterMain = (index: number) => { buttons.forEach((v, i) => { if (index !== i) { v.colorTrans.set('#fff'); } else { v.colorTrans.set(v.color); } }); selected.value = index; }; const enterHard = (index: number) => { hard.forEach((v, i) => { if (index !== i) { v.colorTrans.set('#fff'); } else { v.colorTrans.set(v.color); } }); selected.value = index; }; const toggleSound = () => { soundOpened.value = !soundOpened.value; mainSetting.setValue('audio.bgmEnabled', soundOpened.value); if (soundOpened.value) { soundColor.set('#ddd'); } else { soundColor.set('#d22'); } }; const toggleFullscreen = async () => { await triggerFullscreen(!fullscreen.value); fullscreen.value = !!document.fullscreenElement; }; onMounted(() => { enterMain(0); mainAlpha.set(1); }); return () => ( {!fullscreen.value ? ( ) : ( )} ); }, gameTitleProps); export const GameTitleUI = new GameUI('game-title', GameTitle);