Multiplayer WebXR Project with Vite/Node/Three.js
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

126 lines
4.6 KiB

import * as THREE from 'three';
import { Player } from './Player';
import { Item } from './Item';
import { TextContent, ImageContent, VideoContent, AudioContent } from './Content';
export class ItemManager {
constructor(filePath, scene, rapierWorld, player, interactableItems) {
this.filePath = filePath;
this.scene = scene;
this.rapierWorld = rapierWorld;
this.player = player,
this.interactableItems = interactableItems;
this.audioListener = player.audioListener;
this.itemData = new Map();
this.loadDistance = 250;
this.unloadDistance = 300;
this._init();
}
async _init() {
await this._loadFromJSON();
}
async _loadFromJSON() {
try {
const resp = await fetch(this.filePath);
if (!resp.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await resp.json();
this._processItemData(data.items);
} catch (error) {
console.error("Could not load or process content JSON:", error);
}
}
async _processItemData(itemArray) {
for (const itemDef of itemArray) {
let contentObject = null;
// Create content object based on contentType and file
if (itemDef.contentType && itemDef.file) {
switch (itemDef.contentType) {
case 'text':
// Fetch the text content from the specified file
try {
const textResponse = await fetch(itemDef.file);
if (!textResponse.ok) throw new Error(`Failed to fetch text: ${textResponse.status}`);
const textData = await textResponse.text();
contentObject = new TextContent(textData);
} catch (e) {
console.error(`Could not load text content for item "${itemDef.name}" from ${itemDef.file}`, e);
}
break;
case 'image':
contentObject = new ImageContent(itemDef.file);
break;
case 'video':
contentObject = new VideoContent(itemDef.file);
break;
case 'audio':
if (!this.audioListener) {
console.error("AudioListener not provided. Cannot create AudioContent.");
continue; // Skip this item
}
contentObject = new AudioContent(itemDef.file, this.audioListener);
break;
default:
console.warn(`Unknown content type: ${itemDef.contentType}`);
break;
}
}
// Create the Item instance
const position = new THREE.Vector3(itemDef.position.x, itemDef.position.y, itemDef.position.z);
// The Item constructor expects a single number for scale, which matches your JSON.
const scale = itemDef.scale;
const newItem = new Item(
this.rapierWorld,
this.scene,
this.player,
itemDef.isCollider,
position,
scale,
itemDef.name,
itemDef.model,
itemDef.texture,
[], // spawnedObjects placeholder
contentObject // The created content object
);
if (itemDef.isCollider) {
this.interactableItems.push(newItem);
}
this.itemData.set(itemDef.name, newItem);
}
}
update() {
const playerPosition = this.player.camera.position;
this.itemData.forEach(item => {
let distance;
if (item.lastPosition === null) {
const initPositon = item.spawnPosition;
distance = initPositon.distanceTo(playerPosition);
} else {
distance = item.lastPosition.distanceTo(playerPosition);
}
// Check if item should be loaded
if (distance < this.loadDistance && item.loadState === 'unloaded') {
item.loadModel();
}
// Check if item should be unloaded
else if (distance > this.unloadDistance && item.loadState === 'loaded') {
item.unloadModel();
}
});
}
}