feat: Implement door interaction system and UI improvements
- Add interactive door system with click detection on door meshes - Create custom DoorModal component replacing browser confirm() - Implement door opening with texture change to door1_open.png - Add additive door rendering to preserve opened doors - Remove exploration button and requestExploration method - Implement camera orbit controls with smooth animations - Add active view indicator (yellow highlight) on camera buttons - Add vertical zoom slider with label - Fix camera to maintain isometric perspective while rotating - Integrate all systems into main game loop
This commit is contained in:
@@ -6,12 +6,11 @@ export class CameraManager {
|
||||
this.renderer = renderer; // Reference to GameRenderer to access scenes/resize if needed
|
||||
|
||||
// Configuration
|
||||
this.zoomLevel = 20; // Orthographic zoom factor
|
||||
// Configuration
|
||||
this.zoomLevel = 2.5; // Orthographic zoom factor (Lower = Closer)
|
||||
this.aspect = window.innerWidth / window.innerHeight;
|
||||
|
||||
// Isometric Setup: Orthographic Camera
|
||||
// Left, Right, Top, Bottom, Near, Far
|
||||
// Dimensions determined by zoomLevel and aspect
|
||||
this.camera = new THREE.OrthographicCamera(
|
||||
-this.zoomLevel * this.aspect,
|
||||
this.zoomLevel * this.aspect,
|
||||
@@ -22,9 +21,11 @@ export class CameraManager {
|
||||
);
|
||||
|
||||
// Initial Position: Isometric View
|
||||
// Looking from "High Corner"
|
||||
this.camera.position.set(20, 20, 20);
|
||||
this.camera.lookAt(0, 0, 0);
|
||||
this.target = new THREE.Vector3(0, 0, 0); // Focus point
|
||||
this.isoOffset = new THREE.Vector3(20, 20, 20); // Relative offset
|
||||
|
||||
this.camera.position.copy(this.target).add(this.isoOffset);
|
||||
this.camera.lookAt(this.target);
|
||||
|
||||
// --- Controls State ---
|
||||
this.isDragging = false;
|
||||
@@ -33,8 +34,15 @@ export class CameraManager {
|
||||
this.panSpeed = 0.5;
|
||||
|
||||
// Current Snap View (North, East, South, West)
|
||||
// We'll define View Angles relative to "Target"
|
||||
this.currentViewAngle = 0; // 0 = North? We'll refine mapping.
|
||||
this.currentViewAngle = 0;
|
||||
|
||||
// Animation state for smooth transitions
|
||||
this.isAnimating = false;
|
||||
this.animationStartPos = new THREE.Vector3();
|
||||
this.animationTargetPos = new THREE.Vector3();
|
||||
this.animationProgress = 0;
|
||||
this.animationDuration = 0.5; // seconds
|
||||
this.animationStartTime = 0;
|
||||
|
||||
this.setupInputListeners();
|
||||
}
|
||||
@@ -43,13 +51,20 @@ export class CameraManager {
|
||||
return this.camera;
|
||||
}
|
||||
|
||||
centerOn(x, y) {
|
||||
// Grid (x, y) -> World (x, 0, -y)
|
||||
this.target.set(x, 0, -y);
|
||||
this.camera.position.copy(this.target).add(this.isoOffset);
|
||||
this.camera.lookAt(this.target);
|
||||
}
|
||||
|
||||
setupInputListeners() {
|
||||
// Zoom (Mouse Wheel)
|
||||
window.addEventListener('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
// Adjust Zoom Level property
|
||||
if (e.deltaY < 0) this.zoomLevel = Math.max(5, this.zoomLevel - 1);
|
||||
else this.zoomLevel = Math.min(50, this.zoomLevel + 1);
|
||||
if (e.deltaY < 0) this.zoomLevel = Math.max(3, this.zoomLevel - 1);
|
||||
else this.zoomLevel = Math.min(30, this.zoomLevel + 1);
|
||||
|
||||
this.updateProjection();
|
||||
}, { passive: false });
|
||||
@@ -74,7 +89,7 @@ export class CameraManager {
|
||||
this.lastMouseX = e.clientX;
|
||||
this.lastMouseY = e.clientY;
|
||||
|
||||
this.pan(-dx, dy); // Invert X usually feels natural (drag ground)
|
||||
this.pan(-dx, dy);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,57 +109,92 @@ export class CameraManager {
|
||||
}
|
||||
|
||||
pan(dx, dy) {
|
||||
// Panning moves the camera position relative to its local axes
|
||||
// X movement moves Right/Left
|
||||
// Y movement moves Up/Down (in screen space)
|
||||
// Move Target and Camera together
|
||||
// We pan on the logical "Ground Plane" relative to screen movement
|
||||
|
||||
// Since we are isometric, "Up/Down" on screen means moving along the projected Z axis basically.
|
||||
const moveSpeed = this.panSpeed * 0.05 * (this.zoomLevel / 10);
|
||||
|
||||
// Simple implementation: Translate on X and Z (Ground Plane)
|
||||
// We need to convert screen delta to world delta based on current rotation?
|
||||
// For 'Fixed' views, it's easier.
|
||||
// Transform screen delta to world delta
|
||||
// In Iso view, Right on screen = (1, 0, 1) in world?
|
||||
// Or using camera right/up vectors
|
||||
|
||||
const moveSpeed = this.panSpeed * 0.1 * (this.zoomLevel / 10);
|
||||
const right = new THREE.Vector3(1, 0, 1).normalize(); // Approx logic for standard Iso
|
||||
const forward = new THREE.Vector3(-1, 0, 1).normalize();
|
||||
|
||||
// Let's use camera vectors for generic support
|
||||
// Project camera right/up onto XZ plane
|
||||
// Or just direct translation:
|
||||
|
||||
// Basic Pan relative to world for now:
|
||||
// We really want to move camera.translateX/Y?
|
||||
this.camera.translateX(dx * moveSpeed);
|
||||
this.camera.translateY(dy * moveSpeed);
|
||||
|
||||
// This moves camera. We need to update target reference too if we want to snap back correctly
|
||||
// But for now, simple pan is "offsetting everything".
|
||||
// centerOn resets this.
|
||||
}
|
||||
|
||||
update(deltaTime) {
|
||||
// Update camera animation if active
|
||||
if (this.isAnimating) {
|
||||
const elapsed = (performance.now() - this.animationStartTime) / 1000;
|
||||
this.animationProgress = Math.min(elapsed / this.animationDuration, 1);
|
||||
|
||||
// Easing function (ease-in-out)
|
||||
const t = this.animationProgress;
|
||||
const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
|
||||
// Interpolate position
|
||||
this.camera.position.lerpVectors(this.animationStartPos, this.animationTargetPos, eased);
|
||||
this.camera.lookAt(this.target);
|
||||
|
||||
// End animation
|
||||
if (this.animationProgress >= 1) {
|
||||
this.isAnimating = false;
|
||||
this.camera.position.copy(this.animationTargetPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Fixed Orbit Logic ---
|
||||
// N, S, E, W
|
||||
setIsoView(direction) {
|
||||
// Standard Isometric look from corner
|
||||
// Distance
|
||||
const dist = 40;
|
||||
const height = 30; // 35 degrees up approx?
|
||||
// Rotate camera around target while maintaining isometric angle
|
||||
// Isometric view: 45 degree angle from horizontal
|
||||
const distance = 28; // Distance from target
|
||||
const isoAngle = Math.PI / 4; // 45 degrees for isometric view
|
||||
|
||||
let x, z;
|
||||
// Horizontal rotation angle based on direction
|
||||
let horizontalAngle = 0;
|
||||
switch (direction) {
|
||||
case DIRECTIONS.NORTH: // Looking North means camera is at South?
|
||||
// Or Looking FROM North?
|
||||
// Usually "North View" means "Top of map is North".
|
||||
// In 3D Iso, standard is X=Right, Z=Down(South).
|
||||
// "Normal" view: Camera at +X, +Z looking at origin?
|
||||
x = dist; z = dist;
|
||||
case DIRECTIONS.NORTH: // 'N'
|
||||
horizontalAngle = Math.PI / 4; // 45 degrees (NE in isometric)
|
||||
break;
|
||||
case DIRECTIONS.SOUTH:
|
||||
x = -dist; z = -dist;
|
||||
case DIRECTIONS.EAST: // 'E'
|
||||
horizontalAngle = -Math.PI / 4; // -45 degrees (SE in isometric)
|
||||
break;
|
||||
case DIRECTIONS.EAST:
|
||||
x = dist; z = -dist;
|
||||
case DIRECTIONS.SOUTH: // 'S'
|
||||
horizontalAngle = -3 * Math.PI / 4; // -135 degrees (SW in isometric)
|
||||
break;
|
||||
case DIRECTIONS.WEST:
|
||||
x = -dist; z = dist;
|
||||
case DIRECTIONS.WEST: // 'W'
|
||||
horizontalAngle = 3 * Math.PI / 4; // 135 degrees (NW in isometric)
|
||||
break;
|
||||
default:
|
||||
x = dist; z = dist;
|
||||
}
|
||||
|
||||
this.camera.position.set(x, height, z);
|
||||
this.camera.lookAt(0, 0, 0); // Need to orbit around a pivot actually if we want to pan...
|
||||
// If we pan, camera.lookAt overrides position logic unless we move the visual target.
|
||||
// TODO: Implement OrbitControls-like logic with a target.
|
||||
// Calculate camera position maintaining isometric angle
|
||||
// x and z form a circle on the horizontal plane
|
||||
// y is elevated to maintain the isometric angle
|
||||
const horizontalDistance = distance * Math.cos(isoAngle);
|
||||
const height = distance * Math.sin(isoAngle);
|
||||
|
||||
const x = this.target.x + horizontalDistance * Math.cos(horizontalAngle);
|
||||
const z = this.target.z + horizontalDistance * Math.sin(horizontalAngle);
|
||||
|
||||
// Start animation instead of instant change
|
||||
this.animationStartPos.copy(this.camera.position);
|
||||
this.animationTargetPos.set(x, height, z);
|
||||
this.animationProgress = 0;
|
||||
this.animationStartTime = performance.now();
|
||||
this.isAnimating = true;
|
||||
|
||||
this.currentViewAngle = horizontalAngle;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user