player/src/engine/LabelManager.js
МИН 87444ee2c8 Initial public release: Rublox Player v1.0
Open-source web player for Rublox games, dual-licensed under
AGPL-3.0 + Commercial.

Highlights:
- Babylon.js 7 + React 18 + Vite 5 stack
- Self-contained engine (~46k lines): BlockManager, ModelManager,
  PlayerController, ScriptSandboxWorker, MultiplayerSync, 30+ GD
  gamemodes
- Configurable backend via VITE_API_BASE and friends — works against
  staging (dev-api.rublox.pro) out of the box
- Standalone mode (VITE_STANDALONE=true) loads a bundled sample game
  for first-run without any backend
- Full docs: README, ARCHITECTURE, CONTRIBUTING, SECURITY, CHANGELOG
- Lint + format scaffolding (ESLint + Prettier + EditorConfig)
- Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md
- Issue templates: bug_report, feature_request, security_disclosure

Removed before public release:
- frontend_deploy.py (contained production SSH credentials)
- ~27 admin endpoints (kept in private repo)
- Hard-coded internal URLs and IPs
- All previous git history (clean repo init)
2026-05-27 23:04:04 +03:00

92 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* LabelManager — billboard-метки (текст-плашки) над 3D-объектами.
*
* Используется для game.scene.setLabel(ref, text) — имена/HP над
* персонажами, врагами, предметами. Метка всегда повёрнута лицом к камере
* (billboardMode=7), всегда поверх геометрии (renderingGroupId=1).
*
* Метка привязывается к мешу объекта (parent) и висит над ним.
*/
import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture';
import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
import { Color3 } from '@babylonjs/core/Maths/math.color';
export class LabelManager {
constructor(scene) {
this.scene = scene;
// ref-строка объекта → { plane, tex, mat }
this.labels = new Map();
}
/**
* Установить/обновить метку над объектом.
* ref — ref-строка объекта (от scene.spawn / scene.find).
* anchorMesh — Babylon-меш объекта (метка крепится к нему).
* text — текст метки.
* opts — { color: '#fff', height: 2.5 (м над объектом), size: 1 }
*/
setLabel(ref, anchorMesh, text, opts = {}) {
if (!anchorMesh) return;
const color = opts.color || '#ffffff';
const heightAbove = Number.isFinite(opts.height) ? opts.height : 2.5;
const sizeMul = Number.isFinite(opts.size) ? opts.size : 1;
// Если метка уже есть — пересоздаём (текст/цвет могли измениться).
this.clearLabel(ref);
const W = 1024, H = 256;
const tex = new DynamicTexture(`lblTex_${ref}_${Date.now()}`,
{ width: W, height: H }, this.scene, true);
tex.updateSamplingMode?.(3); // TRILINEAR
tex.anisotropicFilteringLevel = 8;
const ctx = tex.getContext();
ctx.clearRect(0, 0, W, H);
ctx.font = 'bold 120px "Roboto Condensed", "Segoe UI", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.lineWidth = 16;
ctx.lineJoin = 'round';
ctx.strokeStyle = '#000';
ctx.strokeText(String(text), W / 2, H / 2);
ctx.fillStyle = color;
ctx.fillText(String(text), W / 2, H / 2);
tex.update(true);
tex.hasAlpha = true;
const plane = MeshBuilder.CreatePlane(`lbl_${ref}`,
{ width: 2.2 * sizeMul, height: 0.55 * sizeMul }, this.scene);
const mat = new StandardMaterial(`lblMat_${ref}`, this.scene);
mat.diffuseTexture = tex;
mat.diffuseTexture.hasAlpha = true;
mat.emissiveColor = new Color3(1, 1, 1);
mat.disableLighting = true;
mat.backFaceCulling = false;
mat.disableDepthWrite = true;
plane.material = mat;
plane.billboardMode = 7; // всегда лицом к камере
plane.renderingGroupId = 1; // поверх геометрии
plane.isPickable = false;
// Крепим к объекту: метка висит над ним и двигается вместе с ним.
plane.parent = anchorMesh;
plane.position.set(0, heightAbove, 0);
this.labels.set(ref, { plane, tex, mat });
}
/** Убрать метку с объекта. */
clearLabel(ref) {
const rec = this.labels.get(ref);
if (!rec) return;
try { rec.plane.dispose(); } catch (e) { /* ignore */ }
try { rec.tex.dispose(); } catch (e) { /* ignore */ }
try { rec.mat.dispose(); } catch (e) { /* ignore */ }
this.labels.delete(ref);
}
/** Удалить все метки (при выходе из Play). */
clearAll() {
for (const ref of [...this.labels.keys()]) this.clearLabel(ref);
}
}