Add orbit controls and mouse interaction to ParticlesComponent
Integrated Three.js `OrbitControls` for smoother camera navigation and implemented new mouse interaction methods (`onMouseDown`, `onMouseUp`, `onMouseMove`) for enhanced usability. Adjusted scene, camera, and renderer setup for better responsiveness.
This commit is contained in:
parent
52d8658be3
commit
1875f050c6
|
|
@ -11,6 +11,9 @@
|
|||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.plane-controls {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {MatCardModule} from '@angular/material/card';
|
|||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {HeaderComponent} from '../header/header.component';
|
||||
import * as THREE from 'three';
|
||||
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
|
||||
// Define particle types
|
||||
export enum ParticleType {
|
||||
|
|
@ -77,6 +78,7 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
private scene!: THREE.Scene;
|
||||
private camera!: THREE.PerspectiveCamera;
|
||||
private renderer!: THREE.WebGLRenderer;
|
||||
private controls!: OrbitControls;
|
||||
private playerModel!: THREE.Group;
|
||||
private intersectionPlane!: THREE.Mesh;
|
||||
private particles: THREE.Mesh[] = [];
|
||||
|
|
@ -127,16 +129,34 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
this.scene = new THREE.Scene();
|
||||
this.scene.background = new THREE.Color(0xf0f0f0);
|
||||
|
||||
// Get container dimensions
|
||||
const containerWidth = this.rendererContainer.nativeElement.clientWidth;
|
||||
const containerHeight = 400; // Fixed height as defined in CSS
|
||||
|
||||
// Create camera
|
||||
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
this.camera = new THREE.PerspectiveCamera(75, containerWidth / containerHeight, 0.1, 1000);
|
||||
this.camera.position.set(0, 1, 3);
|
||||
this.camera.lookAt(0, 1, 0);
|
||||
|
||||
// Create renderer
|
||||
this.renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
this.renderer.setSize(window.innerWidth * 0.6, window.innerHeight * 0.6);
|
||||
this.renderer.setSize(containerWidth, containerHeight);
|
||||
|
||||
// Center the canvas in the container
|
||||
this.renderer.domElement.style.display = 'block';
|
||||
this.renderer.domElement.style.margin = 'auto';
|
||||
|
||||
this.rendererContainer.nativeElement.appendChild(this.renderer.domElement);
|
||||
|
||||
// Initialize orbit controls
|
||||
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
||||
this.controls.enableDamping = true; // Add smooth damping effect
|
||||
this.controls.dampingFactor = 0.05;
|
||||
this.controls.minDistance = 2; // Minimum zoom distance
|
||||
this.controls.maxDistance = 10; // Maximum zoom distance
|
||||
this.controls.target.set(0, 1, 0); // Set target to player's center
|
||||
this.controls.update();
|
||||
|
||||
// Add lights
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
||||
this.scene.add(ambientLight);
|
||||
|
|
@ -152,7 +172,9 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
this.createIntersectionPlane();
|
||||
|
||||
// Add event listeners
|
||||
this.renderer.domElement.addEventListener('click', this.onMouseClick.bind(this));
|
||||
this.renderer.domElement.addEventListener('mousedown', this.onMouseDown.bind(this));
|
||||
this.renderer.domElement.addEventListener('mouseup', this.onMouseUp.bind(this));
|
||||
this.renderer.domElement.addEventListener('mousemove', this.onMouseMove.bind(this));
|
||||
window.addEventListener('resize', this.onWindowResize.bind(this));
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +225,8 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
|
||||
// Create the intersection plane
|
||||
private createIntersectionPlane(): void {
|
||||
const planeGeometry = new THREE.PlaneGeometry(2, 2);
|
||||
// Make the plane larger to cover 1 block above and below the player (3 blocks tall total)
|
||||
const planeGeometry = new THREE.PlaneGeometry(2, 3);
|
||||
const planeMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0x00ff00,
|
||||
transparent: true,
|
||||
|
|
@ -213,6 +236,8 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
|
||||
this.intersectionPlane = new THREE.Mesh(planeGeometry, planeMaterial);
|
||||
this.intersectionPlane.position.z = 0;
|
||||
// Center the plane vertically with the player (player is about 2 blocks tall)
|
||||
this.intersectionPlane.position.y = 1;
|
||||
this.scene.add(this.intersectionPlane);
|
||||
}
|
||||
|
||||
|
|
@ -226,8 +251,35 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
this.intersectionPlane.position.z = zPosition;
|
||||
}
|
||||
|
||||
// Track if mouse is being dragged
|
||||
private isDragging = false;
|
||||
private mouseDownTime = 0;
|
||||
|
||||
// Handle mouse down event
|
||||
private onMouseDown(event: MouseEvent): void {
|
||||
this.isDragging = false;
|
||||
this.mouseDownTime = Date.now();
|
||||
}
|
||||
|
||||
// Handle mouse up event
|
||||
private onMouseUp(event: MouseEvent): void {
|
||||
// If mouse was down for less than 200ms and didn't move much, consider it a click, not a drag
|
||||
if (Date.now() - this.mouseDownTime < 200 && !this.isDragging) {
|
||||
this.handlePlaneClick(event);
|
||||
}
|
||||
this.isDragging = false;
|
||||
}
|
||||
|
||||
// Handle mouse move event
|
||||
private onMouseMove(event: MouseEvent): void {
|
||||
// If mouse moves while button is pressed, it's a drag
|
||||
if (event.buttons > 0) {
|
||||
this.isDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle mouse click on the plane
|
||||
private onMouseClick(event: MouseEvent): void {
|
||||
private handlePlaneClick(event: MouseEvent): void {
|
||||
// Calculate mouse position in normalized device coordinates
|
||||
const rect = this.renderer.domElement.getBoundingClientRect();
|
||||
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
|
|
@ -245,6 +297,11 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
}
|
||||
}
|
||||
|
||||
// Legacy handler for backward compatibility
|
||||
private onMouseClick(event: MouseEvent): void {
|
||||
// This is now handled by onMouseUp
|
||||
}
|
||||
|
||||
// Add a particle at the specified position
|
||||
private addParticle(x: number, y: number, z: number): void {
|
||||
// Create a visual representation of the particle
|
||||
|
|
@ -280,18 +337,45 @@ export class ParticlesComponent implements OnInit, AfterViewInit {
|
|||
|
||||
// Handle window resize
|
||||
private onWindowResize(): void {
|
||||
this.camera.aspect = window.innerWidth / window.innerHeight;
|
||||
const containerWidth = this.rendererContainer.nativeElement.clientWidth;
|
||||
const containerHeight = 400; // Fixed height as defined in CSS
|
||||
|
||||
this.camera.aspect = containerWidth / containerHeight;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.renderer.setSize(window.innerWidth * 0.6, window.innerHeight * 0.6);
|
||||
this.renderer.setSize(containerWidth, containerHeight);
|
||||
}
|
||||
|
||||
// Animation loop
|
||||
private animate(): void {
|
||||
requestAnimationFrame(this.animate.bind(this));
|
||||
|
||||
// Rotate player model slightly for better view
|
||||
if (this.playerModel) {
|
||||
this.playerModel.rotation.y += 0.005;
|
||||
// Update controls
|
||||
if (this.controls) {
|
||||
this.controls.update();
|
||||
}
|
||||
|
||||
// Update plane orientation based on camera position
|
||||
if (this.intersectionPlane && this.camera) {
|
||||
// Calculate the angle between camera and player (in the XZ plane)
|
||||
const cameraAngle = Math.atan2(
|
||||
this.camera.position.x,
|
||||
this.camera.position.z
|
||||
);
|
||||
|
||||
// Determine which quadrant the camera is in (0-90, 90-180, 180-270, 270-360 degrees)
|
||||
const quadrant = Math.floor((cameraAngle + Math.PI) / (Math.PI / 2)) % 4;
|
||||
|
||||
// Rotate the plane to face the camera
|
||||
if (quadrant === 0) {
|
||||
this.intersectionPlane.rotation.y = 0; // Camera in front
|
||||
} else if (quadrant === 1) {
|
||||
this.intersectionPlane.rotation.y = Math.PI / 2; // Camera on right
|
||||
} else if (quadrant === 2) {
|
||||
this.intersectionPlane.rotation.y = Math.PI; // Camera behind
|
||||
} else {
|
||||
this.intersectionPlane.rotation.y = -Math.PI / 2; // Camera on left
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user