design mode

This commit is contained in:
2025-07-01 08:42:00 +03:00
parent 4ca83b7d87
commit 03281ee582
6 changed files with 410 additions and 53 deletions
+2 -2
View File
@@ -121,9 +121,9 @@ export default {
async mounted(){ async mounted(){
gameEngine = new GameEngine(); gameEngine = new GameEngine();
//this.gameEngine = gameEngine; //this.gameEngine = gameEngine;
await gameEngine.init(this.$refs.target, {gizmo: true}); await gameEngine.init(this.$refs.target, { xr: true, gizmo: true, designMode: true, depthSense: false });
gameEngine.scene.add(gameEngine.transformControls.getHelper()); gameEngine.scene.add(gameEngine.transformControls.getHelper());
gameEngine.scene.add(new gameEngine.$.GridHelper(100,100)); //gameEngine.scene.add(new gameEngine.$.GridHelper(100,100));
this.resize(); this.resize();
//gameEngine.setCamera(gameEngine.orthographicCamera) //gameEngine.setCamera(gameEngine.orthographicCamera)
//gameEngine.setCameraOrthographic(); //gameEngine.setCameraOrthographic();
+2 -2
View File
@@ -103,7 +103,7 @@ class Hero{
if (this.ready) { if (this.ready) {
if (this.canJump) { if (this.canJump) {
//walking //walking
this.mixer.update(this.distance / 100) this.mixer.update(this.distance / 10)
} else { } else {
//were in the air //were in the air
this.mixer.update(this.delta) this.mixer.update(this.delta)
@@ -138,7 +138,7 @@ class Hero{
inputVelocity.x = 1 inputVelocity.x = 1
} }
inputVelocity.setLength(this.delta * 100) inputVelocity.setLength(this.delta * 10)
// apply camera rotation to inputVelocity // apply camera rotation to inputVelocity
euler.y = this.gameEngine.cameraYaw.rotation.y; euler.y = this.gameEngine.cameraYaw.rotation.y;
+95
View File
@@ -0,0 +1,95 @@
import { Clock } from 'three';
class MotionEngine {
constructor() {
const aq = [];
const clock = new Clock();
this.animationQueue = aq;
function calcValues(target, values, initial, k = 1, mode = 'offset') {
Object.entries(values).forEach(([key, value]) => {
if (value && typeof value === 'object') {
if (!target[key]) {
target[key] = {};
}
calcValues(target[key], value, initial[key], k, mode);
return;
}
if (mode == 'offset') {
target[key] = (initial[key] || 0) + value * k;
} else if (typeof (value) == 'function') {
target[key] = value(k);
} else {
target[key] = (initial[key] || 0) + (value - (initial[key] || 0)) * k;
}
});
return target;
}
this.add = function (a) {
a = Array.isArray(a) ? a : [a];
a.forEach(e => {
aq.push(e);
});
};
this.clear = function (object) {
for (var i = aq.length - 1; i >= 0; i--) {
if (object && aq[i].o == object || !object && aq[i].ct == aq[i].t) {
aq.splice(i, 1);
}
}
};
this.remove = function (a) {
let idx = aq.indexOf(a);
if (idx > -1) {
a.ct = 0;
this.animate(a, 0);
aq.splice(idx, 1);
}
};
this.update = () => {
let t = clock.getDelta();
if (aq.length) {
aq.forEach(e => this.animate(e, t));
this.clear();
}
};
this.animate = function (e, t) {
if (e.d && (e.dt || 0) < e.d) {
e.dt = (e.dt || 0) + t;
return;
}
if (e.ct === undefined) {
e.ct = 0;
e.iv = calcValues({}, e.a, e.o, 0);
}
e.ct = e.ct + t;
if (e.ct > e.t) {
e.ct = e.t;
e.f && e.f();
}
calcValues(e.o, e.a, e.iv, e.ct / e.t, e.m || 'value');
if (e.ct == e.t && e.r) {
e.ct = e.m == 'offset' ? undefined : 0;
if (e.rd) {
e.dt = 0;
}
}
};
this.isActive = function (object) {
return aq.find(e => e.o == object);
};
this.isIdle = function () {
return aq.length == 0;
};
}
}
export {MotionEngine}
+31 -11
View File
@@ -19,6 +19,7 @@ class GameEngine {
async init(domNode, opts = {}) { async init(domNode, opts = {}) {
this.w = domNode.clientWidth || 1200, this.h = domNode.clientHeight || 800; this.w = domNode.clientWidth || 1200, this.h = domNode.clientHeight || 800;
this.aspect = this.w / this.h this.aspect = this.w / this.h
this.opts = opts;
const gameEngine = this; const gameEngine = this;
this.perspectiveCamera = new THREE.PerspectiveCamera(45, this.aspect, 0.01, 1000); this.perspectiveCamera = new THREE.PerspectiveCamera(45, this.aspect, 0.01, 1000);
@@ -120,8 +121,10 @@ class GameEngine {
gameEngine.mixers.forEach(m => m.update(delta)); gameEngine.mixers.forEach(m => m.update(delta));
gameEngine.handleXrAction(gameEngine, delta) gameEngine.handleXrAction(gameEngine, delta)
gameEngine.render(scene, gameEngine.camera); gameEngine.render(scene, gameEngine.camera);
if (!renderer.xr.isPresenting) {
gameEngine.gizmo?.render(); gameEngine.gizmo?.render();
} }
}
renderer.setAnimationLoop(animate); renderer.setAnimationLoop(animate);
const mixer = new THREE.AnimationMixer(this.scene); const mixer = new THREE.AnimationMixer(this.scene);
@@ -290,19 +293,30 @@ class GameEngine {
} }
} }
if (gp.buttons[4]?.pressed) { if (gp.buttons[4]?.pressed) {
// gameEngine.setCameraOrthographic(); let x = performance.now() % 1000;
// gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera); if (x < 333) {
let session = gameEngine.renderer.xr.getFrame().session; gameEngine.transformControls.setMode('translate');
console.log(session); }else if (x < 666) {
session.pauseDepthSensing(); gameEngine.transformControls.setMode('scale');
}else{
gameEngine.transformControls.setMode('rotate');
} }
if (gp.buttons[5]?.pressed) { // let nextMode = {
// gameEngine.setCameraOrthographic(); // 'translate': 'scale',
// gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera); // 'scale': 'rotate',
let session = gameEngine.renderer.xr.getFrame().session; // 'rotate': 'translate'
console.log(session); // }
session.resumeDepthSensing(); // if(gameEngine.opts.designMode){;
// gameEngine.transformControls.setMode(nextMode[gameEngine.transformControls.getMode()]);
// }
} }
// if (gp.buttons[5]?.pressed) {
// // gameEngine.setCameraOrthographic();
// // gameEngine.renderer.xr.updateCamera(gameEngine.orthographicCamera);
// let session = gameEngine.renderer.xr.getFrame().session;
// console.log(session);
// session.resumeDepthSensing();
// }
} }
} }
@@ -311,6 +325,8 @@ class GameEngine {
//event.type !== 'move' && console.log(event) //event.type !== 'move' && console.log(event)
const controller = event.target; const controller = event.target;
//console.log(event) //console.log(event)
if (this.opts.designMode){
if (!controller.userData) return if (!controller.userData) return
if (controller.userData.active === false) return; if (controller.userData.active === false) return;
this.transformControls.getRaycaster().setFromXRController(controller); this.transformControls.getRaycaster().setFromXRController(controller);
@@ -327,9 +343,12 @@ class GameEngine {
break; break;
} }
} }
}
onSelect(event) { onSelect(event) {
const controller = event.target; const controller = event.target;
if (this.opts.designMode){
this.xrController1.userData.active = false; this.xrController1.userData.active = false;
this.xrController2.userData.active = false; this.xrController2.userData.active = false;
@@ -358,6 +377,7 @@ class GameEngine {
}, 100); }, 100);
} }
} }
}
$ = THREE; $ = THREE;
+96
View File
@@ -0,0 +1,96 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group, sRGBEncoding } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader';
import { MotionEngine } from '../MotionEngine';
class Game1 {
constructor(context, image, w, h) {
this.game = new Group();
const aq = new MotionEngine();
const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]];
let d = 1.2;
let bm = new BoxGeometry(1, 1, 1);
let uv = bm.getAttribute('uv');
for (let i = 0; i < 6; i++) {
let s = [(i % w) / w, (i % h) / h];
//top left
uv.array[8 * i] = s[0];
uv.array[8 * i + 1] = s[1] + 1 / h;
//top right
uv.array[8 * i + 2] = s[0] + 1 / w;
uv.array[8 * i + 3] = s[1] + 1 / h;
//bottom left
uv.array[8 * i + 4] = s[0];
uv.array[8 * i + 5] = s[1];
//bottom right
uv.array[8 * i + 6] = s[0] + 1 / w;
uv.array[8 * i + 7] = s[1];
}
let material = new MeshBasicMaterial({
map: new TextureLoader().load(image)
});
material.map.encoding = sRGBEncoding;
for (let i = 0; i < 6; i++) {
let b = bm.clone();
let mesh = new Mesh(b, material);
mesh.position.set((i % w) * d, (i % h) * d, 0);
let ri;
do {
ri = Math.floor(Math.random() * 6);
} while (ri == this.game.children.length);
mesh.rotation.set(pr[ri][0] * Math.PI / 2, pr[ri][1] * Math.PI / 2, 0);
mesh._ri = ri;
this.game.add(mesh);
}
this.game.children[0].onBeforeRender = () => {
this.update();
};
var check = () => {
if (!this.game.children.length) return false;
let i = 0;
for (let c of this.game.children) {
if (Math.abs(c.rotation.x - pr[i][0] * Math.PI / 2) > 0.0001 || Math.abs(c.rotation.y - pr[i][1] * Math.PI / 2) > 0.0001) return false;
i++;
}
return true;
};
let clickFn = (i) => {
if (!this.done && !aq.isActive(i.object)) {
i.object._ri = (i.object._ri + 1) % 6;
aq.add({
o: i.object,
a: { rotation: { x: pr[i.object._ri][0] * Math.PI / 2, y: pr[i.object._ri][1] * Math.PI / 2 } },
t: .5
});
}
};
this.game.children.forEach(c => {
context.clickable.add(c, clickFn);
});
this.update = () => {
aq.update();
if (aq.isIdle() && !this.done && check()) {
this.done = true;
this.game.children.forEach((c, i) => {
aq.add({
o: c,
a: { position: { x: i % w, y: i % h } },
t: 1,
f: i == 0 && this.onfinish
});
});
context.dashboard.addPoints(10);
}
};
}
}
export {Game1}
+146
View File
@@ -0,0 +1,146 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group, sRGBEncoding } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader';
import { MotionEngine } from '../MotionEngine';
class Game2 {
constructor(context, image, w, h) {
const texture = new TextureLoader().load(image);
texture.encoding = sRGBEncoding;
const material = new MeshBasicMaterial({
map: texture
});
const aq = new MotionEngine();
const m2 = new MeshBasicMaterial({
map: texture,
transparent: true,
opacity: 0.37
});
let last, lidx = w - 1;
this.game = new Group();
let d = 1.2, p = [];
function check() {
if (p.length) {
for (let i = 0; i < p.length; i++) {
if (p[i] != i) return false;
}
return true;
}
return false;
}
function xch(x, y) {
let temp = p[x];
p[x] = p[y];
p[y] = temp;
}
this.shuffle = function () {
function getMoves() {
let m = [], pl = p[lidx];
let xl = pl % w, yl = ~~(pl / h);
if (xl > 0) m.push(p.indexOf(pl - 1));
if (xl < w - 1) m.push(p.indexOf(pl + 1));
if (yl > 0) m.push(p.indexOf(pl - w));
if (yl < h - 1) m.push(p.indexOf(pl + w));
return m;
}
for (let i = 0; i < w * h; i++) {
p[i] = i;
}
for (let iter = 0; iter < 73 * w * h; iter++) {
let m = getMoves();
xch(m[Math.floor(Math.random() * m.length)], lidx);
}
// while (p.length<9){
// let n = Math.floor(Math.random()*9);
// if (p.indexOf(n) == -1) p.push(n);
// }
p.forEach((e, i) => {
let x = e % w, y = ~~(e / h);
this.game.children[i].position.set(x * d, y * d, 0);
});
};
for (let i = 0; i < w * h; i++) {
let x = i % w, xp = x / w;
let y = ~~(i / h), yp = y / h;
let bg = new BoxGeometry(1, 1, 1);
let uv = bg.getAttribute('uv');
for (let k = 0; k < 6; k++) {
//top left
uv.array[8 * k] = xp;
uv.array[8 * k + 1] = yp + 1 / h;
//top right
uv.array[8 * k + 2] = xp + 1 / w;
uv.array[8 * k + 3] = yp + 1 / h;
//bottom left
uv.array[8 * k + 4] = xp;
uv.array[8 * k + 5] = yp;
//bottom right
uv.array[8 * k + 6] = xp + 1 / w;
uv.array[8 * k + 7] = yp;
}
let mesh = new Mesh(bg, i != lidx ? material : m2);
mesh.position.set(x * d, y * d, 0);
this.game.add(mesh);
}
last = this.game.children[lidx];
this.game.children[0].onBeforeRender = () => {
this.update();
};
this.shuffle();
let clickFn = (i) => {
if (!this.done && !aq.isActive(i.object)) {
let idx = this.game.children.indexOf(i.object);
if (idx == lidx) return; //we ignore the empty cell
let xc = p[idx] % w, yc = ~~(p[idx] / h);
let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
if (Math.abs(xc - xl) + Math.abs(yc - yl) == 1) {
aq.add({
o: i.object,
a: { position: { x: (xl - xc) * d, y: (yl - yc) * d } },
t: .3,
m: 'offset'
});
aq.add({
o: last,
a: { position: { x: (xc - xl) * d, y: (yc - yl) * d } },
t: .3,
m: 'offset'
});
xch(idx, lidx);
}
}
};
this.game.children.forEach(c => {
context.clickable.add(c, clickFn);
});
this.update = () => {
aq.update();
if (aq.isIdle() && !this.done && check()) {
this.done = true;
this.game.children.forEach((c, i) => {
last.material = material;
aq.add({
o: c,
a: { position: { x: i % w, y: ~~(i / h) } },
t: 1,
f: i == 0 && this.onfinish
});
});
context.dashboard.addPoints(10);
}
};
}
}
export { Game2 };