From e77ca3e3764bf816542443e6cc2f468c151fbeb8 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Thu, 18 Jan 2024 15:25:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20keyboard=E7=B1=BB=20&=20todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- idea.md | 13 +++- src/core/audio/sound.ts | 2 + src/core/common/eventEmitter.ts | 1 + src/core/loader/resource.ts | 2 + src/core/main/custom/hotkey.ts | 2 + src/core/main/custom/keyboard.ts | 122 +++++++++++++++++++++++++++++++ src/core/main/init/hotkey.ts | 1 + src/core/main/init/settings.tsx | 14 ++-- src/core/main/init/toolbar.tsx | 2 + src/core/main/setting.ts | 88 +++++++++++++--------- src/core/main/storage.ts | 3 + src/core/plugin.ts | 2 + src/panel/keyboard.vue | 32 ++++++++ src/plugin/utils.ts | 23 +++++- 14 files changed, 262 insertions(+), 45 deletions(-) create mode 100644 src/core/main/custom/keyboard.ts create mode 100644 src/panel/keyboard.vue diff --git a/idea.md b/idea.md index 23bc5b2..47b16bd 100644 --- a/idea.md +++ b/idea.md @@ -100,9 +100,18 @@ dam4.png ---- 存档 59 [] 优化各种 ui [] 怪物脚下加入阴影 [x] 着色器特效 -[] 完全删除 core.plugin?(待定) +[] 完全删除 core.plugin,采用 Plugin.register 的形式进行插件编写 [] 通用 Addon 接口 [] 完善加载系统 [] 不同怪物可以在怪物手册中添加一些不同的边框 [] 按住一个按键时显示怪物的攻防血 -[] 新的事件系统? +[] 新的事件系统,并丰富自定义事件 +[] 事件、eval 内容预编译 +[] 新的存档系统,可以注册存档项 +[] 渐变切 bgm +[] 新的 Flag 系统,使用 for 申请变量,dispose 释放变量,可设为渲染进程与游戏进程共通变量 +[] 注册可录像操作,比如可以在点击的时候执行,自动计入录像 +[] 机关门显示绑定怪物 +[] 自定义状态栏,通过申请空间进行布局 +[] 复写 api,rewrite() +[] 对 vnode 进行简单的包装,提供出显示文字、显示图片等 api 以及修改 css 的 api diff --git a/src/core/audio/sound.ts b/src/core/audio/sound.ts index 543b195..236d8e5 100644 --- a/src/core/audio/sound.ts +++ b/src/core/audio/sound.ts @@ -3,6 +3,8 @@ import { AudioParamOf, AudioPlayer } from './audio'; import resource from '@/data/resource.json'; import { ResourceController } from '../loader/controller'; +// todo: 立体声,可设置音源位置 + type Panner = AudioParamOf; export class SoundEffect extends AudioPlayer { diff --git a/src/core/common/eventEmitter.ts b/src/core/common/eventEmitter.ts index 9a801c0..f8834eb 100644 --- a/src/core/common/eventEmitter.ts +++ b/src/core/common/eventEmitter.ts @@ -1,3 +1,4 @@ +// todo: 更改utils.ts的形式,使common文件夹可以同时在渲染进程和游戏进程使用 import { has } from '@/plugin/utils'; export interface EmitableEvent { diff --git a/src/core/loader/resource.ts b/src/core/loader/resource.ts index 0e3609d..3dbb5df 100644 --- a/src/core/loader/resource.ts +++ b/src/core/loader/resource.ts @@ -6,6 +6,8 @@ import JSZip from 'jszip'; import { EmitableEvent, EventEmitter } from '../common/eventEmitter'; import { loading } from './load'; +// todo: 应当用register去注册资源类型,然后进行分块处理 + interface ResourceData { image: HTMLImageElement; arraybuffer: ArrayBuffer; diff --git a/src/core/main/custom/hotkey.ts b/src/core/main/custom/hotkey.ts index 468c724..7e40dcb 100644 --- a/src/core/main/custom/hotkey.ts +++ b/src/core/main/custom/hotkey.ts @@ -2,6 +2,8 @@ import { KeyCode } from '@/plugin/keyCodes'; import { deleteWith, generateBinary, spliceBy } from '@/plugin/utils'; import { EmitableEvent, EventEmitter } from '../../common/eventEmitter'; +// todo: 按下时触发,长按(单次/连续)触发,按下连续触发,按下节流触发,按下加速节流触发 + interface HotkeyEvent extends EmitableEvent { set: (id: string, key: KeyCode, assist: number) => void; emit: (key: KeyCode, assist: number, type: KeyEmitType) => void; diff --git a/src/core/main/custom/keyboard.ts b/src/core/main/custom/keyboard.ts new file mode 100644 index 0000000..6c1bc5c --- /dev/null +++ b/src/core/main/custom/keyboard.ts @@ -0,0 +1,122 @@ +import { EmitableEvent, EventEmitter } from '@/core/common/eventEmitter'; +import { KeyCode } from '@/plugin/keyCodes'; +import { gameKey } from '../init/hotkey'; +import { unwarpBinary } from './hotkey'; +import { deleteWith } from '@/plugin/utils'; +import { cloneDeep } from 'lodash-es'; + +export interface KeyboardEmits { + key: KeyCode; + assist: number; +} + +interface KeyboardItem { + key: KeyCode; + text?: string; + x: number; + y: number; + width: number; + height: number; +} + +interface AssistManager { + end(): void; +} + +interface VirtualKeyboardEvent extends EmitableEvent { + add: (item: KeyboardItem) => void; + remove: (item: KeyboardItem) => void; + emit: (item: KeyboardItem, assist: number, index: number) => void; +} + +/** + * 虚拟按键,同一个虚拟按键实例应当只能同时操作一个,但可以显示多个 + */ +export class Keyboard extends EventEmitter { + static list: Keyboard[] = []; + + id: string; + keys: KeyboardItem[] = []; + assist: number = 0; + + constructor(id: string) { + super(); + this.id = id; + Keyboard.list.push(this); + } + + /** + * 给虚拟键盘添加一个按键 + * @param item 按键信息 + */ + add(item: KeyboardItem) { + this.keys.push(item); + this.emit('add', item); + } + + /** + * 移除一个按键 + * @param item 按键信息 + */ + remove(item: KeyboardItem) { + deleteWith(this.keys, item); + this.emit('remove', item); + } + + /** + * 创造一个在某些辅助按键已经按下的情况下的作用域,这些被按下的辅助按键还可以被玩家手动取消 + * @param assist 辅助按键 + * @returns 作用域控制器,用于结束此作用域 + */ + withAssist(assist: number): AssistManager { + const thisAssist = this.assist; + this.assist = assist; + + return { + end: () => { + this.assist = thisAssist; + } + }; + } + + /** + * 继承一个按键的按键信息 + * @param keyboard 要被继承的按键 + * @param offsetX 被继承的按键的横坐标偏移量 + * @param offsetY 被继承的按键的纵坐标偏移量 + */ + extend(keyboard: Keyboard, offsetX: number = 0, offsetY: number = 0) { + const toClone = cloneDeep(keyboard.keys); + toClone.forEach(v => { + v.x += offsetX; + v.y += offsetY; + }); + + this.keys.push(...toClone); + } + + /** + * 触发按键 + * @param key 要触发的按键 + */ + emitKey(key: KeyboardItem, index: number) { + const ev = generateKeyboardEvent(key.key, this.assist); + gameKey.emitKey(key.key, this.assist, 'up', ev); + this.emit('emit', key, this.assist, index); + } + + static get(id: string) { + return this.list.find(v => v.id === id); + } +} + +export function generateKeyboardEvent(key: KeyCode, assist: number) { + const { ctrl, alt, shift } = unwarpBinary(assist); + const ev = new KeyboardEvent('keyup', { + ctrlKey: ctrl, + shiftKey: shift, + altKey: alt + }); + + return ev; +} diff --git a/src/core/main/init/hotkey.ts b/src/core/main/init/hotkey.ts index 2a04fd0..0d462ed 100644 --- a/src/core/main/init/hotkey.ts +++ b/src/core/main/init/hotkey.ts @@ -9,6 +9,7 @@ import { GameStorage } from '../storage'; export const mainScope = Symbol.for('@key_main'); export const gameKey = new Hotkey('gameKey', '游戏按键'); +// todo: 读取上一个手动存档,存档至下一个存档栏 // ----- Register gameKey // -------------------- diff --git a/src/core/main/init/settings.tsx b/src/core/main/init/settings.tsx index e3bf6b4..0619386 100644 --- a/src/core/main/init/settings.tsx +++ b/src/core/main/init/settings.tsx @@ -3,19 +3,21 @@ import { Button, InputNumber } from 'ant-design-vue'; import { mainUi } from './ui'; import { gameKey } from './hotkey'; +// todo: 数字类型改为一个输入框,一个加按钮一个减按钮;新增单选框 + interface Components { - DefaultSetting: SettingComponent; - BooleanSetting: SettingComponent; - NumberSetting: SettingComponent; + Default: SettingComponent; + Boolean: SettingComponent; + Number: SettingComponent; HotkeySetting: SettingComponent; ToolbarEditor: SettingComponent; } export function createSettingComponents() { const com: Components = { - DefaultSetting, - BooleanSetting, - NumberSetting, + Default: DefaultSetting, + Boolean: BooleanSetting, + Number: NumberSetting, HotkeySetting, ToolbarEditor }; diff --git a/src/core/main/init/toolbar.tsx b/src/core/main/init/toolbar.tsx index 66165a9..ca09526 100644 --- a/src/core/main/init/toolbar.tsx +++ b/src/core/main/init/toolbar.tsx @@ -6,6 +6,8 @@ import type { import BoxAnimate from '@/components/boxAnimate.vue'; import { checkAssist } from '../custom/hotkey'; +// todo: 新增更改设置的ToolItem + interface Components { DefaultTool: CustomToolbarComponent; KeyTool: CustomToolbarComponent<'hotkey'>; diff --git a/src/core/main/setting.ts b/src/core/main/setting.ts index 14e600f..ef8fdc6 100644 --- a/src/core/main/setting.ts +++ b/src/core/main/setting.ts @@ -85,7 +85,7 @@ export class MotaSetting extends EventEmitter { key: string, name: string, value: MotaSettingType, - com: SettingComponent = COM.DefaultSetting, + com: SettingComponent = COM.Default, step: [number, number, number] = [0, 100, 1] ) { const setting: MotaSettingItem = { @@ -274,11 +274,34 @@ export class SettingDisplayer extends EventEmitter { } } +// todo: 优化存储方式 + export const mainSetting = new MotaSetting(); +interface SettingStorage { + showHalo: boolean; + frag: boolean; + itemDetail: boolean; + transition: boolean; + antiAlias: boolean; + fontSize: number; + smoothView: boolean; + criticalGem: boolean; + fixed: boolean; + betterLoad: boolean; + autoScale: boolean; + paraLight: boolean; + heroDetail: boolean; +} + +const storage = new GameStorage( + GameStorage.fromAuthor('AncTe', 'setting') +); + // ----- 监听设置修改 mainSetting.on('valueChange', (key, n, o) => { const [root, setting] = key.split('.'); + if (root === 'screen') { handleScreenSetting(setting, n, o); } else if (root === 'action') { @@ -367,28 +390,30 @@ function handleUtilsSetting( } // ----- 游戏的所有设置项 +// todo: 虚拟键盘缩放,小地图楼传缩放 mainSetting .register( 'screen', '显示设置', new MotaSetting() - .register('fullscreen', '全屏游戏', false, COM.BooleanSetting) - .register('halo', '光环显示', true, COM.BooleanSetting) - .register('itemDetail', '宝石血瓶显伤', true, COM.BooleanSetting) - .register('heroDetail', '勇士显伤', false, COM.BooleanSetting) - .register('transition', '界面动画', false, COM.BooleanSetting) - .register('antiAlias', '抗锯齿', false, COM.BooleanSetting) - .register('fontSize', '字体大小', 16, COM.NumberSetting, [8, 28, 1]) - .register('smoothView', '平滑镜头', true, COM.BooleanSetting) - .register('criticalGem', '临界显示方式', false, COM.BooleanSetting) + .register('fullscreen', '全屏游戏', false, COM.Boolean) + .register('halo', '光环显示', true, COM.Boolean) + .register('itemDetail', '宝石血瓶显伤', true, COM.Boolean) + .register('heroDetail', '勇士显伤', false, COM.Boolean) + .register('transition', '界面动画', false, COM.Boolean) + .register('antiAlias', '抗锯齿', false, COM.Boolean) + .register('fontSize', '字体大小', 16, COM.Number, [8, 28, 1]) + .register('smoothView', '平滑镜头', true, COM.Boolean) + .register('criticalGem', '临界显示方式', false, COM.Boolean) .setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击')) + .register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500]) ) .register( 'action', '操作设置', new MotaSetting() - .register('autoSkill', '自动切换技能', true, COM.BooleanSetting) - .register('fixed', '定点查看', true, COM.BooleanSetting) + .register('autoSkill', '自动切换技能', true, COM.Boolean) + .register('fixed', '定点查看', true, COM.Boolean) .register('hotkey', '快捷键', false, COM.HotkeySetting) .setDisplayFunc('hotkey', () => '') .register('toolbar', '自定义工具栏', false, COM.ToolbarEditor) @@ -398,37 +423,28 @@ mainSetting 'utils', '系统设置', new MotaSetting() - .register('betterLoad', '优化加载', true, COM.BooleanSetting) - .register('autoScale', '自动放缩', true, COM.BooleanSetting) + .register('betterLoad', '优化加载', true, COM.Boolean) + .register('autoScale', '自动放缩', true, COM.Boolean) ) .register( 'fx', '特效设置', new MotaSetting() - .register('paraLight', '野外阴影', true, COM.BooleanSetting) - .register('frag', '打怪特效', true, COM.BooleanSetting) + .register('paraLight', '野外阴影', true, COM.Boolean) + .register('frag', '打怪特效', true, COM.Boolean) + ) + .register( + 'ui', + 'ui设置', + new MotaSetting().register( + 'mapScale', + '小地图楼传缩放', + 300, + COM.Number, + [50, 50, 1000] + ) ); -interface SettingStorage { - showHalo: boolean; - frag: boolean; - itemDetail: boolean; - transition: boolean; - antiAlias: boolean; - fontSize: number; - smoothView: boolean; - criticalGem: boolean; - fixed: boolean; - betterLoad: boolean; - autoScale: boolean; - paraLight: boolean; - heroDetail: boolean; -} - -const storage = new GameStorage( - GameStorage.fromAuthor('AncTe', 'setting') -); - loading.once('coreInit', () => { mainSetting.reset({ 'screen.fullscreen': !!document.fullscreenElement, diff --git a/src/core/main/storage.ts b/src/core/main/storage.ts index 4cee9ab..1ad3046 100644 --- a/src/core/main/storage.ts +++ b/src/core/main/storage.ts @@ -30,12 +30,15 @@ export class GameStorage { * @param key 存储的名称 * @param value 存储的值 */ + setValue(key: K, value: T[K]): void; + setValue(key: string, value: any): void; setValue(key: K, value: T[K]) { this.data[key] = value; } getValue(key: K): T[K] | null; getValue(key: K, defaults: T[K]): T[K]; + getValue(key: string, defaults?: T): T; getValue(key: K, defaults?: T[K]) { if (this.data[key]) return this.data[key]; else { diff --git a/src/core/plugin.ts b/src/core/plugin.ts index bbf0680..f48fa57 100644 --- a/src/core/plugin.ts +++ b/src/core/plugin.ts @@ -18,6 +18,8 @@ import smooth from '@/plugin/fx/smoothView'; import frag from '@/plugin/fx/frag'; import { Mota } from '.'; +// todo: 将插件更改为注册形式,分为渲染进程和游戏进程两部分,同时分配优先级 + export function resolvePlugin() { const toForward: [keyof Mota['plugin'], any][] = [ ['pop', pop()], diff --git a/src/panel/keyboard.vue b/src/panel/keyboard.vue new file mode 100644 index 0000000..1562da6 --- /dev/null +++ b/src/panel/keyboard.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/plugin/utils.ts b/src/plugin/utils.ts index 76ad37f..e7ba5d0 100644 --- a/src/plugin/utils.ts +++ b/src/plugin/utils.ts @@ -3,10 +3,11 @@ import { MessageApi } from 'ant-design-vue/lib/message'; import { isNil } from 'lodash-es'; import { Animation, sleep, TimingFn } from 'mutate-animate'; import { ref } from 'vue'; -import { EVENT_KEY_CODE_MAP } from './keyCodes'; +import { EVENT_KEY_CODE_MAP, KeyCode } from './keyCodes'; import axios from 'axios'; import { decompressFromBase64 } from 'lz-string'; import { parseColor } from './webgl/utils'; +import { KeyboardEmits } from '@/core/main/custom/keyboard'; type CanParseCss = keyof { [P in keyof CSSStyleDeclaration as CSSStyleDeclaration[P] extends string @@ -264,6 +265,11 @@ export function pColor(color: string) { return `rgba(${arr.join(',')})` as Color; } +/** + * 删除数组内的某个项,返回删除后的数组 + * @param arr 要操作的数组 + * @param ele 要删除的项 + */ export function deleteWith(arr: T[], ele: T): T[] { const index = arr.indexOf(ele); if (index === -1) return arr; @@ -339,3 +345,18 @@ export function flipBinary(num: number, col: number) { if (num & n) return num & ~n; else return num | n; } + +/** + * 唤起虚拟键盘,并获取到一次按键操作 + * @param emitAssist 是否可以获取辅助按键,为true时,如果按下辅助按键,那么会立刻返回该按键, + * 否则会视为开关辅助按键 + * @param assist 初始化的辅助按键 + */ +export function getVitualKeyOnce( + emitAssist: boolean = false, + assist: number = 0 +): Promise { + return new Promise(res => { + res({ key: KeyCode.Unknown, assist: 0 }); + }); +}