initial commit
@@ -26,6 +26,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/howler": "^2.2.13",
|
"@types/howler": "^2.2.13",
|
||||||
"@types/three": "^0.184.1",
|
"@types/three": "^0.184.1",
|
||||||
|
"lil-gui": "^0.21.0",
|
||||||
"rollup": "^4.61.0",
|
"rollup": "^4.61.0",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"vite": "^6.4.3"
|
"vite": "^6.4.3"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const globalSettings: ConfigUiParamsCategories[] = [
|
|||||||
values: [
|
values: [
|
||||||
30,
|
30,
|
||||||
150,
|
150,
|
||||||
60
|
50
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -41,19 +41,19 @@ export const globalSettings: ConfigUiParamsCategories[] = [
|
|||||||
visible: "position",
|
visible: "position",
|
||||||
values: [
|
values: [
|
||||||
[
|
[
|
||||||
-10,
|
-50,
|
||||||
10,
|
50,
|
||||||
0
|
-4.79
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-10,
|
-50,
|
||||||
10,
|
50,
|
||||||
1
|
11.71
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-10,
|
-50,
|
||||||
10,
|
50,
|
||||||
5
|
-5.61
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -67,17 +67,17 @@ export const globalSettings: ConfigUiParamsCategories[] = [
|
|||||||
[
|
[
|
||||||
-360,
|
-360,
|
||||||
360,
|
360,
|
||||||
0
|
-116
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-360,
|
-360,
|
||||||
360,
|
360,
|
||||||
0
|
-20
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-360,
|
-360,
|
||||||
360,
|
360,
|
||||||
0
|
-144
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -88,7 +88,7 @@ export const globalSettings: ConfigUiParamsCategories[] = [
|
|||||||
values: [
|
values: [
|
||||||
30,
|
30,
|
||||||
150,
|
150,
|
||||||
60
|
65
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -99,19 +99,19 @@ export const globalSettings: ConfigUiParamsCategories[] = [
|
|||||||
visible: "position",
|
visible: "position",
|
||||||
values: [
|
values: [
|
||||||
[
|
[
|
||||||
-10,
|
-50,
|
||||||
10,
|
50,
|
||||||
0
|
-2.9
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-10,
|
-50,
|
||||||
10,
|
50,
|
||||||
1
|
7.0
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-10,
|
-50,
|
||||||
10,
|
50,
|
||||||
5
|
-3.4
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -125,17 +125,17 @@ export const globalSettings: ConfigUiParamsCategories[] = [
|
|||||||
[
|
[
|
||||||
-360,
|
-360,
|
||||||
360,
|
360,
|
||||||
0
|
-116
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-360,
|
-360,
|
||||||
360,
|
360,
|
||||||
0
|
-20
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-360,
|
-360,
|
||||||
360,
|
360,
|
||||||
0
|
-144
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import GUI from "lil-gui";
|
||||||
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||||
|
import { CameraC_internal, Delegate, UpdateController } from "@24tools/playable_template";
|
||||||
|
import { FollowCameraC } from "./FollowCameraC";
|
||||||
|
import { TestSceneC } from "./TestSceneC";
|
||||||
|
|
||||||
|
// Transparent overlay for OrbitControls — sits above canvas but below GUI panel.
|
||||||
|
// Allows the GUI to keep receiving clicks while OrbitControls is active.
|
||||||
|
function createOrbitOverlay(): HTMLDivElement {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.style.cssText = `
|
||||||
|
position: fixed; inset: 0;
|
||||||
|
z-index: 9000;
|
||||||
|
touch-action: auto;
|
||||||
|
cursor: grab;
|
||||||
|
`;
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CameraDebugUI {
|
||||||
|
static init() {
|
||||||
|
const gui = new GUI({ title: "📷 Camera Debug", width: 280 });
|
||||||
|
gui.domElement.style.zIndex = "99999";
|
||||||
|
gui.domElement.style.position = "fixed";
|
||||||
|
|
||||||
|
const offsetFolder = gui.addFolder("Offset — camera position (from config)");
|
||||||
|
offsetFolder.add(FollowCameraC.offset, "x", -10, 10, 0.1).name("X (left / right)");
|
||||||
|
offsetFolder.add(FollowCameraC.offset, "y", -10, 10, 0.1).name("Y (height)");
|
||||||
|
offsetFolder.add(FollowCameraC.offset, "z", -10, 10, 0.1).name("Z (distance behind)");
|
||||||
|
|
||||||
|
gui.add(FollowCameraC, "lerpSpeed", 1, 15, 0.5).name("Follow smoothness [1–15]");
|
||||||
|
|
||||||
|
const charFolder = gui.addFolder("Character");
|
||||||
|
const scaleProxy = { scale: TestSceneC.characterObject.scale.x };
|
||||||
|
charFolder
|
||||||
|
.add(scaleProxy, "scale", 0.1, 3, 0.01)
|
||||||
|
.name("Scale [0.1–3]")
|
||||||
|
.onChange((v: number) => TestSceneC.characterObject.scale.setScalar(v));
|
||||||
|
|
||||||
|
let orbitControls: OrbitControls | null = null;
|
||||||
|
let orbitDelegate: Delegate<number> | null = null;
|
||||||
|
let orbitOverlay: HTMLDivElement | null = null;
|
||||||
|
|
||||||
|
const orbitProxy = { enabled: false };
|
||||||
|
gui
|
||||||
|
.add(orbitProxy, "enabled")
|
||||||
|
.name("🔭 Free camera (OrbitControls)")
|
||||||
|
.onChange((enabled: boolean) => {
|
||||||
|
if (enabled) {
|
||||||
|
FollowCameraC.paused = true;
|
||||||
|
|
||||||
|
orbitOverlay = createOrbitOverlay();
|
||||||
|
document.body.appendChild(orbitOverlay);
|
||||||
|
|
||||||
|
orbitControls = new OrbitControls(CameraC_internal.camera!, orbitOverlay);
|
||||||
|
orbitControls.update();
|
||||||
|
|
||||||
|
orbitDelegate = UpdateController.Instance.onUpdate.addDelegate(() => {
|
||||||
|
orbitControls?.update();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (orbitDelegate !== null) {
|
||||||
|
UpdateController.Instance.onUpdate.removeListeners(orbitDelegate);
|
||||||
|
orbitDelegate = null;
|
||||||
|
}
|
||||||
|
orbitControls?.dispose();
|
||||||
|
orbitControls = null;
|
||||||
|
|
||||||
|
orbitOverlay?.remove();
|
||||||
|
orbitOverlay = null;
|
||||||
|
|
||||||
|
FollowCameraC.paused = false;
|
||||||
|
FollowCameraC.snapToTarget();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gui
|
||||||
|
.add(
|
||||||
|
{
|
||||||
|
log: () => {
|
||||||
|
const cam = CameraC_internal.camera!;
|
||||||
|
const s = TestSceneC.characterObject.scale.x;
|
||||||
|
|
||||||
|
const p = cam.position;
|
||||||
|
// Convert radians to degrees for config
|
||||||
|
const rx = Math.round(cam.rotation.x * (180 / Math.PI));
|
||||||
|
const ry = Math.round(cam.rotation.y * (180 / Math.PI));
|
||||||
|
const rz = Math.round(cam.rotation.z * (180 / Math.PI));
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`%c[CameraDebug] Paste into globalSettings.ts:
|
||||||
|
camera_position: x=${p.x.toFixed(2)}, y=${p.y.toFixed(2)}, z=${p.z.toFixed(2)}
|
||||||
|
camera_rotation: x=${rx}°, y=${ry}°, z=${rz}°
|
||||||
|
charScale: ${s.toFixed(2)}`,
|
||||||
|
"color: #7cf; font-weight: bold"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
.name("📋 Print values to console");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { CameraC_internal, UpdateController } from "@24tools/playable_template";
|
||||||
|
import { Object3D, Vector3 } from "three";
|
||||||
|
|
||||||
|
const _targetWorldPos = new Vector3();
|
||||||
|
|
||||||
|
export class FollowCameraC {
|
||||||
|
// Populated in init() from the camera position set by CameraC config
|
||||||
|
static offset = new Vector3();
|
||||||
|
static lerpSpeed = 6;
|
||||||
|
static paused = false;
|
||||||
|
|
||||||
|
private static target: Object3D | null = null;
|
||||||
|
|
||||||
|
static init(target: Object3D) {
|
||||||
|
this.target = target;
|
||||||
|
|
||||||
|
const camera = CameraC_internal.camera!;
|
||||||
|
target.getWorldPosition(_targetWorldPos);
|
||||||
|
|
||||||
|
// Offset = config camera position minus character world position.
|
||||||
|
// Rotation is already set by config — do not modify.
|
||||||
|
this.offset.copy(camera.position).sub(_targetWorldPos);
|
||||||
|
|
||||||
|
UpdateController.Instance.onUpdate.addDelegate((delta) => {
|
||||||
|
this.update(delta);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from resizeCb after CameraC.setCamera().
|
||||||
|
// Re-syncs offset from the new config (portrait/landscape may differ),
|
||||||
|
// camera position is already correct after setCamera so no snap needed.
|
||||||
|
static syncAndSnap() {
|
||||||
|
const camera = CameraC_internal.camera;
|
||||||
|
if (!this.target || !camera) return;
|
||||||
|
this.target.getWorldPosition(_targetWorldPos);
|
||||||
|
// CameraC.setCamera() placed camera.position at the config world position.
|
||||||
|
// Recompute offset as the difference between that position and current character position.
|
||||||
|
this.offset.copy(camera.position).sub(_targetWorldPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantly moves camera to character + offset without lerp.
|
||||||
|
static snapToTarget() {
|
||||||
|
const camera = CameraC_internal.camera;
|
||||||
|
if (!this.target || !camera) return;
|
||||||
|
this.target.getWorldPosition(_targetWorldPos);
|
||||||
|
camera.position.copy(_targetWorldPos.clone().add(this.offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static update(delta: number) {
|
||||||
|
if (this.paused) return;
|
||||||
|
const camera = CameraC_internal.camera;
|
||||||
|
if (!this.target || !camera) return;
|
||||||
|
|
||||||
|
this.target.getWorldPosition(_targetWorldPos);
|
||||||
|
|
||||||
|
// Position only. Rotation is fixed by config.
|
||||||
|
const desired = _targetWorldPos.clone().add(this.offset);
|
||||||
|
camera.position.lerp(desired, Math.min(1, this.lerpSpeed * delta));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +1,31 @@
|
|||||||
import { BoxGeometry, Mesh, MeshStandardMaterial, Vector3 } from "three";
|
|
||||||
import { ThreeC } from "./ThreeC";
|
import { ThreeC } from "./ThreeC";
|
||||||
import { InputC, JoystickC } from "@24tools/playable_template";
|
import { InputC } from "@24tools/playable_template";
|
||||||
|
import { Object3D } from "three";
|
||||||
|
|
||||||
export class TestSceneC {
|
export class TestSceneC {
|
||||||
static init() {
|
static mapObject: Object3D;
|
||||||
this.createPrimitive();
|
static characterObject: Object3D;
|
||||||
|
|
||||||
|
static init() {
|
||||||
|
this.loadScene();
|
||||||
|
this.loadCharacter();
|
||||||
|
|
||||||
// example of using InputC events
|
|
||||||
InputC.onTouchDown.addDelegate((event) => {
|
InputC.onTouchDown.addDelegate((event) => {
|
||||||
console.log("onMouseDown", event);
|
console.log("onMouseDown", event);
|
||||||
});
|
});
|
||||||
|
|
||||||
// if you have update in your controller
|
|
||||||
// UpdateController.Instance.onUpdate.addDelegate(() => {
|
|
||||||
// this.update();
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createPrimitive() {
|
private static loadScene() {
|
||||||
const geometry = new BoxGeometry(1, 1, 1);
|
this.mapObject = ThreeC.getObject("scene");
|
||||||
const material = new MeshStandardMaterial({ color: 0xcc0000 });
|
ThreeC.setShadowsStateForChildren(this.mapObject, true, true);
|
||||||
const cube = new Mesh(geometry, material);
|
ThreeC.addToScene(this.mapObject);
|
||||||
|
}
|
||||||
|
|
||||||
const geometryPlane = new BoxGeometry(5, 0.1, 7);
|
private static loadCharacter() {
|
||||||
const materialPlane = new MeshStandardMaterial({ color: 0xaaaaaa });
|
this.characterObject = ThreeC.getObject("character");
|
||||||
const plane = new Mesh(geometryPlane, materialPlane);
|
ThreeC.setShadowsStateForChildren(this.characterObject, true, true);
|
||||||
|
this.characterObject.position.set(0, 0, 0);
|
||||||
let planePosition = plane.position.clone();
|
this.characterObject.scale.setScalar(1);
|
||||||
|
ThreeC.addToScene(this.characterObject);
|
||||||
ThreeC.setShadowsStateForChildren(cube, true, false);
|
|
||||||
|
|
||||||
ThreeC.setShadowsStateForChildren(plane, false, true);
|
|
||||||
|
|
||||||
plane.position.copy(
|
|
||||||
new Vector3(planePosition.x, planePosition.y - 0.5, planePosition.z - 1.5)
|
|
||||||
);
|
|
||||||
|
|
||||||
ThreeC.addToScene(cube);
|
|
||||||
ThreeC.addToScene(plane);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 430 KiB |
|
After Width: | Height: | Size: 427 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 814 B |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -1,14 +1,18 @@
|
|||||||
// import { ConvertToBase64WhenRelease } from "@24tools/ads_common";
|
import { ConvertToBase64WhenRelease } from "@24tools/ads_common";
|
||||||
import { ConvertResourceType, Template3d } from "@24tools/playable_template";
|
import { ConvertResourceType, Template3d } from "@24tools/playable_template";
|
||||||
|
|
||||||
|
|
||||||
export const meshes : ConvertResourceType = {
|
export const meshes : ConvertResourceType = {
|
||||||
type: "mesh",
|
type: "mesh",
|
||||||
resources: [
|
resources: [
|
||||||
// {
|
{
|
||||||
// name: "scene",
|
name: "scene",
|
||||||
// value: ConvertToBase64WhenRelease("./SceneСombo.glb"),
|
value: ConvertToBase64WhenRelease("./ZombiePunk_Map.glb"),
|
||||||
// },
|
},
|
||||||
|
{
|
||||||
|
name: "character",
|
||||||
|
value: ConvertToBase64WhenRelease("./ZombiePunk_Character.glb"),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
loader: Template3d.meshLoader
|
loader: Template3d.meshLoader
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { BatchedRenderer, QuarksLoader, QuarksUtil } from "three.quarks";
|
||||||
|
import { Object3D, Euler, Vector3 } from "three";
|
||||||
|
import { ResourcesC, UpdateController } from "@24tools/playable_template";
|
||||||
|
import { ThreeC } from "../../ThreeC";
|
||||||
|
import { TimeC } from "../Timers/TimeC";
|
||||||
|
import { VFXType } from "../Enums/VFXType";
|
||||||
|
import { ResourcesType } from "../Enums/ResourcesType";
|
||||||
|
import { vfxTest } from "./RunTimeTest";
|
||||||
|
|
||||||
|
export class VfxManager {
|
||||||
|
static batchRenderer: BatchedRenderer;
|
||||||
|
static loader: QuarksLoader;
|
||||||
|
|
||||||
|
static Init() {
|
||||||
|
this.batchRenderer = new BatchedRenderer();
|
||||||
|
this.loader = new QuarksLoader();
|
||||||
|
ThreeC.addToScene(this.batchRenderer);
|
||||||
|
const updateDelegate = UpdateController.Instance.onUpdate.addDelegate(this.update.bind(this));
|
||||||
|
// GameTimer.onDelayGameEnd.addDelegate(() => UpdateController.Instance.onUpdate.removeListeners(updateDelegate));
|
||||||
|
// initTrailEffect();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Remove(vfx: Object3D) {
|
||||||
|
vfx.removeFromParent();
|
||||||
|
vfx.parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static update(delta: number) {
|
||||||
|
delta *= TimeC.TimeScale;
|
||||||
|
this.batchRenderer.update(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Play(type: VFXType|string, parent: Object3D | null = null, position: Vector3 | null = null, rotation: Euler | null = null, scale: Vector3 | null = null, odred: number | null = null) {
|
||||||
|
let loaded = (ResourcesC.getResource(ResourcesType.VFX, type.toString()) as { obj: any }).obj;
|
||||||
|
if (!loaded) return new Object3D();
|
||||||
|
// console.error("Type non loaded " + loaded);
|
||||||
|
const effect = loaded.clone(true) as Object3D;
|
||||||
|
QuarksUtil.setAutoDestroy(effect, true);
|
||||||
|
QuarksUtil.addToBatchRenderer(effect, this.batchRenderer);
|
||||||
|
|
||||||
|
if (parent) parent.add(effect)
|
||||||
|
else ThreeC.addToScene(effect);
|
||||||
|
if (position) effect.position.copy(position);
|
||||||
|
if (rotation) effect.rotation.copy(rotation);
|
||||||
|
if (scale) effect.scale.copy(scale);
|
||||||
|
if (odred) effect.renderOrder = odred;
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StopEmision(effect: Object3D) {
|
||||||
|
QuarksUtil.stop(effect);
|
||||||
|
}
|
||||||
|
static Restart(effect: Object3D) {
|
||||||
|
QuarksUtil.play(effect);
|
||||||
|
}
|
||||||
|
static Pause(effect: Object3D) {
|
||||||
|
QuarksUtil.pause(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,8 +1,17 @@
|
|||||||
import { TestSceneC } from "../controllers/TestSceneC";
|
import { TestSceneC } from "../controllers/TestSceneC";
|
||||||
|
import { FollowCameraC } from "../controllers/FollowCameraC";
|
||||||
import { SoundC, Template } from "@24tools/playable_template";
|
import { SoundC, Template } from "@24tools/playable_template";
|
||||||
|
|
||||||
export const afterResourcesLoadedCb: (() => void) | undefined = () => {
|
export const afterResourcesLoadedCb: (() => void) | undefined = async () => {
|
||||||
TestSceneC.init();
|
TestSceneC.init();
|
||||||
SoundC.init();
|
SoundC.init();
|
||||||
|
|
||||||
|
FollowCameraC.init(TestSceneC.characterObject);
|
||||||
|
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
const { CameraDebugUI } = await import("../controllers/CameraDebugUI");
|
||||||
|
CameraDebugUI.init();
|
||||||
|
}
|
||||||
|
|
||||||
Template.disableLoader();
|
Template.disableLoader();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { Template3d } from "@24tools/playable_template";
|
import { Template3d } from "@24tools/playable_template";
|
||||||
|
import { CameraC } from "../controllers/CameraC";
|
||||||
|
import { FollowCameraC } from "../controllers/FollowCameraC";
|
||||||
|
|
||||||
export const resizeCb = () => {
|
export const resizeCb = () => {
|
||||||
Template3d.resize();
|
Template3d.resize();
|
||||||
|
CameraC.setCamera(window.screenSize.portrait);
|
||||||
|
// Re-sync offset from the new config (portrait/landscape) and correct camera position
|
||||||
|
FollowCameraC.syncAndSnap();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
"noUnusedLocals": false,
|
"noUnusedLocals": false,
|
||||||
"noUnusedParameters": false,
|
"noUnusedParameters": false,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"allowJs": true
|
"allowJs": true,
|
||||||
|
"types": ["vite/client"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "templateLibs", "build", "zip"]
|
"exclude": ["node_modules", "dist", "templateLibs", "build", "zip"]
|
||||||
|
|||||||