hud observation for generic game objects

This commit is contained in:
2025-11-07 12:48:36 +02:00
parent b972ab25f0
commit 48c7ea2e2a
7 changed files with 152 additions and 43 deletions
@@ -1,3 +1,5 @@
import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxMaxLength } from "@/lib/MeshUtils";
class GenericObject{ class GenericObject{
constructor(engine, data){ constructor(engine, data){
return new Promise(async(resolve, reject)=>{ return new Promise(async(resolve, reject)=>{
@@ -6,9 +8,32 @@ class GenericObject{
if (!data.exclude){ if (!data.exclude){
engine.clickable.add(this.object, e=>{ engine.clickable.add(this.object, e=>{
if (engine.dashboard && data.description){ if (engine.dashboard){
if (data.description){
engine.dashboard.update({ hint: data.description }) engine.dashboard.update({ hint: data.description })
} }
if (data.hud){
if (this.object._hud ){
engine.dashboard.detach(this.object);
}else{
let bb = getBoundingBox(this.object);
let scale = 0.5/getBoundingBoxMaxLength(bb);
let position = getBoundingBoxCenterPoint(bb, this.object.position).multiplyScalar(scale).negate()
let placement = {
quaternion: { x:0, y:0, z:0, w:0 },
position,
scale: {
x: scale*this.object.scale.x,
y: scale*this.object.scale.y,
z: scale*this.object.scale.z
}
}
engine.dashboard.attach(this.object, placement, true);
}
}
}
}); });
} }
@@ -34,34 +34,11 @@ class VideoPlayer {
}) })
vi.addEventListener('play', ()=>{ vi.addEventListener('play', ()=>{
if (engine.dashboard?.active){ if (engine.dashboard?.active){
data = { engine.dashboard.attach(plane);
parent: plane.parent,
location: {
position: plane.position.clone(),
quaternion: plane.quaternion.clone(),
scale: plane.scale.clone()
}
}
engine.dashboard.group.attach(plane);
engine.motionQueue.add({
o: plane,
a: {
rotation: { x:0, y:0, z:0 },
position: { x:0, y:0, z:-0.25 },
scale: { x: 1, y:1, z:1 }
},
t: 1
})
} }
}) })
vi.addEventListener('pause', ()=>{ vi.addEventListener('pause', ()=>{
data.parent.attach(plane); engine.dashboard?.detach(plane);
engine.motionQueue.add({
o: plane,
a: data.location,
t: 1
})
}) })
}) })
} }
+88 -5
View File
@@ -1,4 +1,7 @@
import { PlaneGeometry, CylinderGeometry, CanvasTexture, Group, Mesh, MeshStandardMaterial, DoubleSide } from "three"; import {
PlaneGeometry, CylinderGeometry, CanvasTexture, Group,
Mesh, MeshStandardMaterial, MeshBasicMaterial, DoubleSide
} from "three";
import Utils from "./Utils"; import Utils from "./Utils";
class DashBoard { class DashBoard {
constructor(engine) { constructor(engine) {
@@ -20,6 +23,7 @@ class DashBoard {
let texture = new CanvasTexture(canvas) let texture = new CanvasTexture(canvas)
let updating = false; let updating = false;
let params = {} let params = {}
let occupied = false;
img.addEventListener('load', function () { img.addEventListener('load', function () {
ctx.drawImage(img, 0, 0, engine.w, engine.h); ctx.drawImage(img, 0, 0, engine.w, engine.h);
@@ -28,18 +32,36 @@ class DashBoard {
updating = false; updating = false;
}) })
const dash = new Group(); const dash = new Group(), hud = new Group(), hudTarget = new Group();
hud.visible = false;
let hudAnimation, hudPlane;
this.group = dash; this.group = dash;
dash.add(hud);
hud.add(hudTarget)
dash.visible = false; dash.visible = false;
const dashGeometry = new PlaneGeometry(engine.aspect, 1); const dashGeometry = new PlaneGeometry(engine.aspect, 1);
const dashMesh = new Mesh(dashGeometry, new MeshStandardMaterial({ const dashMesh = new Mesh(dashGeometry, new MeshBasicMaterial({
roughness: 0, metalness:0.1, transparent: true, transparent: true,
map: texture map: texture
})) }))
dash.add(dashMesh); dash.add(dashMesh);
engine.scene.add(dash) engine.scene.add(dash);
(async()=>{
hudPlane = new Mesh(
new PlaneGeometry(engine.aspect, 1),
new MeshBasicMaterial({
map: await engine.loadTexture('/static/textures/hud.png', ''),
opacity: 0.37,
transparent:true
})
);
hudPlane.position.z = -0.25;
hud.add(hudPlane)
})()
engine.addEventListener('beforeRender', ()=>{ engine.addEventListener('beforeRender', ()=>{
dash.quaternion.copy(engine.camera.quaternion) dash.quaternion.copy(engine.camera.quaternion)
@@ -98,6 +120,67 @@ class DashBoard {
}); });
} }
this.attach = (object, dashPlacement, rotate)=>{
hud.visible = true;
hudPlane.scale.set(0, .1, 1);
hudPlane.material.opacity = 0.5;
engine.motionQueue.add([{
o:hudPlane, a:{material:{opacity:0.73}}, t:.4, d:.8
},{
o:hudPlane, a:{scale:{x:1}}, t:.4
},{
o:hudPlane, a:{scale:{y:1}}, t:.4, d:.4,
}])
if (occupied) return false;
object._hud = {
parent: object.parent,
placement: {
position: object.position.clone(),
quaternion: object.quaternion.clone(),
scale: object.scale.clone()
}
}
hudTarget.attach(object);
occupied = true;
engine.motionQueue.add({
o: object,
a: dashPlacement || {
quaternion: { x:0, y:0, z:0, w:0 },
position: { x:0, y:0, z:0 },
scale: { x: 1, y:1, z:1 }
},
t: 1
})
if (rotate){
engine.motionQueue.add(hudAnimation = {
o: hudTarget,
a: {
rotation: { y: 2*Math.PI }
},
t: 4,
r: true,
d: 1
})
}
}
this.detach = object=>{
engine.motionQueue.remove(hudAnimation);
object._hud.parent.attach(object);
hud.rotation.y = 0;
hud.visible = false;
engine.motionQueue.add({
o: object,
a: object._hud.placement,
t: 1
});
delete object._hud;
occupied = false;
hudAnimation = null;
}
this.createProgressBar(); this.createProgressBar();
this.update(); this.update();
} }
-6
View File
@@ -485,12 +485,6 @@ class GameEngine extends THREE.EventDispatcher{
this.hero?.characterControls?.idleReset(); this.hero?.characterControls?.idleReset();
} }
autoScale(object, mk = 1) {
let bb = new THREE.Box3().setFromObject(object);
let k = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
object.scale.multiplyScalar(mk / k);
}
setCamera(camera) { setCamera(camera) {
//camera.updateProjectionMatrix(); //camera.updateProjectionMatrix();
this.camera = camera; this.camera = camera;
+25 -2
View File
@@ -1,4 +1,4 @@
import { TextureLoader } from "three"; import { TextureLoader, Box3, Vector3 } from "three";
function assignParams(mesh, params){ function assignParams(mesh, params){
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p])); ['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
@@ -26,4 +26,27 @@ function assignMaterial(mesh, params){
} }
} }
export { assignParams, assignMaterial } function getBoundingBox(object){
return new Box3().setFromObject(object);
}
function getBoundingBoxMaxLength(bb){
return Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z)
}
function getBoundingBoxCenterPoint(bb, relativeTo){
relativeTo = relativeTo || new Vector3(0,0,0)
return new Vector3(
bb.min.x + (bb.max.x - bb.min.x)/2 - relativeTo.x,
bb.min.y + (bb.max.y - bb.min.y)/2 - relativeTo.y,
bb.min.z + (bb.max.z - bb.min.z)/2 - relativeTo.z
)
}
function autoScale(object, mk = 1) {
let bb = getBoundingBox(object);
let k = getBoundingBoxMaxLength(bb);
object.scale.multiplyScalar(mk / k);
}
export { assignParams, assignMaterial, autoScale, getBoundingBox, getBoundingBoxMaxLength, getBoundingBoxCenterPoint }
+8 -2
View File
@@ -26,6 +26,8 @@ class MotionEngine {
return target; return target;
} }
// a = {o-object, a-action, t-time, f-finish event, d-delay, m-mode, r-repeat,
// rd-repeat the delay, rf-reset on finish}
this.add = function (a) { this.add = function (a) {
a = Array.isArray(a) ? a : [a]; a = Array.isArray(a) ? a : [a];
a.forEach(e => { a.forEach(e => {
@@ -35,7 +37,7 @@ class MotionEngine {
this.clear = function (object) { this.clear = function (object) {
for (var i = aq.length - 1; i >= 0; i--) { for (var i = aq.length - 1; i >= 0; i--) {
if (object && aq[i].o == object || !object && aq[i].ct == aq[i].t) { if (object && aq[i].o == object || !object && (aq[i].ct == aq[i].t || aq[i].rr)) {
aq.splice(i, 1); aq.splice(i, 1);
} }
} }
@@ -75,7 +77,11 @@ class MotionEngine {
e.ct = e.ct + t; e.ct = e.ct + t;
if (e.ct > e.t) { if (e.ct > e.t) {
e.ct = e.t; e.ct = e.t;
e.f && e.f(); e.f?.();
if (e.rf){
e.ct = t = 0;
e.rr = true;
}
} }
calcValues(e.o, e.a, e.iv, e.ct / e.t, e.m || 'value'); calcValues(e.o, e.a, e.iv, e.ct / e.t, e.m || 'value');
if (e.ct == e.t && e.r) { if (e.ct == e.t && e.r) {
+2 -1
View File
@@ -1,6 +1,7 @@
import { InteractiveObject } from '@/components/InteractiveObjects/InteractiveObject'; import { InteractiveObject } from '@/components/InteractiveObjects/InteractiveObject';
import { GameEngine } from '@/lib/GameEngine'; import { GameEngine } from '@/lib/GameEngine';
import { Hero } from '@/lib/Hero'; import { Hero } from '@/lib/Hero';
import { autoScale } from '@/lib/MeshUtils';
let gameEngine = null; let gameEngine = null;
export default { export default {
@@ -185,7 +186,7 @@ export default {
object[p].copy(l[data.id][p]) object[p].copy(l[data.id][p])
}) })
}else if (!data.type || data.type == 'GenericObject'){ }else if (!data.type || data.type == 'GenericObject'){
gameEngine.autoScale(object, autoScaleFactor); autoScale(object, autoScaleFactor);
} }
l[data.id] = l[data.id] || {}; l[data.id] = l[data.id] || {};
['position', 'scale', 'rotation', 'visible'].forEach(p=>{ ['position', 'scale', 'rotation', 'visible'].forEach(p=>{