diff --git a/js/Item.js b/js/Item.js index a066025..6864ec0 100644 --- a/js/Item.js +++ b/js/Item.js @@ -39,6 +39,8 @@ export class Item { this.loadState = "unloaded"; this.lastPosition = null; + this.links = []; + this._init(); } diff --git a/js/ItemManager.js b/js/ItemManager.js index 9baa60d..c558e9c 100644 --- a/js/ItemManager.js +++ b/js/ItemManager.js @@ -2,6 +2,7 @@ import * as THREE from 'three'; import { Player } from './Player'; import { Item } from './Item'; import { TextContent, ImageContent, VideoContent, AudioContent } from './Content'; +import { GeometryUtils } from 'three/examples/jsm/Addons.js'; export class ItemManager { constructor(filePath, scene, rapierWorld, player, interactableItems) { @@ -14,6 +15,8 @@ export class ItemManager { this.itemData = new Map(); this.loadDistance = 250; this.unloadDistance = 300; + this.linkLines = new Map(); + this.drawnLinks = new Set(); this._init(); } @@ -29,7 +32,8 @@ export class ItemManager { } const data = await resp.json(); - this._processItemData(data.items); + await this._processItemData(data.items); + this._createLinkLines(); } catch (error) { console.error("Could not load or process content JSON:", error); @@ -93,14 +97,73 @@ export class ItemManager { contentObject // The created content object ); + newItem.id = itemDef.id; + if (itemDef.links) { + newItem.links = itemDef.links; + } + if (itemDef.isCollider) { this.interactableItems.push(newItem); } - this.itemData.set(itemDef.name, newItem); + this.itemData.set(itemDef.id, newItem); } } + _createLinkLines() { + + this.itemData.forEach(item => { + console.log(item.id) + + if (!item.links) return; + + item.links.forEach(linkedItemId => { + const linkedItem = this.itemData.get(linkedItemId); + if (!linkedItem) return; + + const key = [item.id, linkedItemId].sort().join('-'); + if (this.drawnLinks.has(key)) return; + + const material = new THREE.LineDashedMaterial({ + color: 0x0000ff, + dashSize: 3, + gapSize: 1, + }); + + const sourcePos = item.spawnPosition.clone(); + const terminalPos = linkedItem.spawnPosition.clone(); + sourcePos.y = 1; terminalPos.y = 1; + + const points = [item.spawnPosition.clone(), linkedItem.spawnPosition.clone()]; + const geometry = new THREE.BufferGeometry().setFromPoints(points); + + const line = new THREE.Line(geometry, material); + line.computeLineDistances(); + + this.scene.add(line); + this.linkLines.set(key, { line, item1: item, item2: linkedItem }); + this.drawnLinks.add(key); + }); + }); + } + + _updateLinkLines() { + this.linkLines.forEach(link => { + const { line, item1, item2 } = link; + + // Only update/show the line if both items are loaded + if (item1.loadState === 'loaded' && item2.loadState === 'loaded') { + line.visible = true; + const positions = line.geometry.attributes.position; + positions.setXYZ(0, item1.object.position.x, 1, item1.object.position.z); + positions.setXYZ(1, item2.object.position.x, 1, item2.object.position.z); + positions.needsUpdate = true; + } else { + line.visible = false; + } + }); + } + update() { const playerPosition = this.player.camera.position; this.itemData.forEach(item => { @@ -122,5 +185,7 @@ export class ItemManager { item.unloadModel(); } }); + + this._updateLinkLines(); } } \ No newline at end of file diff --git a/public/json/Items.json b/public/json/Items.json index 2d103ba..3aed1f2 100644 --- a/public/json/Items.json +++ b/public/json/Items.json @@ -10,7 +10,8 @@ "isCollider": true, "position": { "x": 100, "y": 1, "z": 0 }, "rotation": { "x": 0, "y": 0, "z": 0 }, - "scale": 5 + "scale": 5, + "links": [1] }, { "id": 1, @@ -22,7 +23,8 @@ "isCollider": true, "position": { "x": -100, "y": 1, "z": 0 }, "rotation": { "x": 0, "y": 0, "z": 0 }, - "scale": 10 + "scale": 10, + "links": [0] } ] } \ No newline at end of file