This commit is contained in:
syuilo 2026-05-31 16:09:58 +09:00
commit 7fa8bcc9fd
4 changed files with 32 additions and 4 deletions

View file

@ -132,6 +132,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</div>
</XOverlayPanel>
<XOverlayPanel v-if="isPlayerInfoOpen" :isNarrow="isNarrow" :title="room.name" @close="isPlayerInfoOpen = false">
<template #icon>
<i class="ti ti-user"></i>
</template>
<div class="_gaps_s">
<MkA :to="`${userPage(pointedPlayerInfo)}`" :behavior="'window'">
<MkUserCardMini :user="pointedPlayerInfo" :withChart="false"/>
</MkA>
</div>
</XOverlayPanel>
</div>
</template>
@ -147,6 +159,7 @@ import XEnvOptions from './room.env-options.vue';
import XOverlayPanel from './OverlayPanel.vue';
import type { RoomControllerOptions } from '@/world/room/controller.js';
import type { RoomState, RoomAttachments } from 'misskey-world/src/room/type.js';
import type { PlayerProfile } from 'misskey-world-engine/src/PlayerContainer.js';
import MkWorldMonoOptionsForm from '@/components/MkWorldMonoOptionsForm.vue';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
@ -177,13 +190,14 @@ const canvas = useTemplateRef('canvas');
const isMyRoom = computed(() => props.room.userId === $i?.id);
const isNarrow = deviceKind === 'smartphone';
const isModified = ref(false);
const pointedPlayerInfo = ref<PlayerProfile | null>(null);
const isMenuShowing = ref(!isNarrow);
const isRoomSettingsOpen = ref(false);
const isRoomInfoOpen = ref(false);
const isFurnitureSettingsOpen = ref(false);
const isPlayerInfoOpen = ref(false);
watch(isFurnitureSettingsOpen, () => {
if (isFurnitureSettingsOpen.value) {
@ -385,6 +399,11 @@ watch([graphicsQuality, fps, resolution, antialias], () => {
refresh();
});
controller.addListener('playerPointed', ({ playerId }) => {
pointedPlayerInfo.value = multiplayer.playerProfiles.value[playerId] ?? null;
isPlayerInfoOpen.value = true;
});
function resize() {
controller.resize();
}

View file

@ -19,9 +19,11 @@ export type EngineControllerBaseOptions = {
type EngineEventsOf<T> = T extends EngineBase<infer X> ? X : EngineBaseEvents;
type ControllerEvents = EventEmitter.ValidEventTypes;
// UIとエンジンの間に挟まり抽象化を行うレイヤー。
// UIからは、エンジンが直で動いててもワーカーで動いてても同じように操作できるように見える
export abstract class EngineControllerBase<T extends EngineBase<EngineBaseEvents>> {
export abstract class EngineControllerBase<T extends EngineBase<EngineBaseEvents>, E extends EventEmitter.ValidEventTypes = EventEmitter.ValidEventTypes> extends EventEmitter<ControllerEvents & E> {
private worker: Worker | null = null;
private engine: T | null = null;
private canvas: HTMLCanvasElement | null = null;
@ -34,6 +36,7 @@ export abstract class EngineControllerBase<T extends EngineBase<EngineBaseEvents
private destroyed = false;
constructor(options: EngineControllerBaseOptions) {
super();
this.options = options;
}

View file

@ -25,7 +25,9 @@ export type RoomControllerOptions = {
};
// 抽象化レイヤー
export class RoomController extends EngineControllerBase<RoomEngine> {
export class RoomController extends EngineControllerBase<RoomEngine, {
'playerPointed': { playerId: string; };
}> {
public isSitting = ref(false);
public isEditMode = ref(false);
public isRoomLightOn = ref(true);
@ -115,6 +117,10 @@ export class RoomController extends EngineControllerBase<RoomEngine> {
engineEvents.on('playSfxUrl', ({ url, options }) => {
sound.playUrl(url, options);
});
engineEvents.on('playerPointed', ({ playerId }) => {
this.emit('playerPointed', { playerId });
});
}
public async reset(canvas: HTMLCanvasElement, attachments: RoomAttachments, roomState?: RoomState | null, options?: RoomControllerOptions | null) {

View file

@ -18,7 +18,7 @@ export class Multiplayer {
private controller: RoomController;
private connection: Misskey.IChannelConnection<Misskey.Channels['worldRoom']> | null = null;
private roomId: string;
private playerProfiles: Record<string, PlayerProfile> = {};
public playerProfiles: Record<string, PlayerProfile> = {};
constructor(roomId: string, controller: RoomController) {
this.roomId = roomId;