Add bonus power unlock with laser effect#133
Conversation
When users visit all scenes, they now unlock "Cosmic Power" which upgrades their game experience with: - 2.5x larger click/hit radius on orbs - Dramatic laser beam effects when hitting targets - Enhanced cosmic explosion particles with rings and shockwaves - 1.5x combo score multiplier boost - Visual cosmic ring indicators around targetable orbs - HUD indicator showing cosmic power status during gameplay The unlock celebration animation has been upgraded to showcase all the new benefits with animated laser decorations and a cosmic orb. New components: - CosmicPowerContext: Global state management for cosmic power - LaserBeam: 3D laser effect from camera to target with glow - CosmicExplosion: Enhanced particle explosion with expanding rings Also excludes scripts folder from TypeScript compilation to fix unrelated build issues with ES6 Set iteration.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b7ac9cf1e4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const { beamLength, beamDirection, midPoint } = useMemo(() => { | ||
| const start = new THREE.Vector3(0, 0, -2); // Start slightly in front of camera | ||
| const end = new THREE.Vector3(...targetPosition); | ||
| const direction = end.clone().sub(start); | ||
| const length = direction.length(); |
There was a problem hiding this comment.
Aim laser beam in camera space
The beam direction/length is derived from a fixed local start ([0,0,-2]) to a world-space targetPosition, but the group is later positioned/rotated to the camera. When the camera has moved or rotated (OrbitControls, cinematic), this math is no longer in the same coordinate space, so the beam and impact flash can miss the orb or appear offset. Converting the target into camera-local space (or computing start in world space from the camera pose) is needed to keep the laser aligned with the hit location.
Useful? React with 👍 / 👎.
| // Load from localStorage on mount | ||
| useEffect(() => { | ||
| if (typeof window === 'undefined') return; | ||
|
|
||
| const isUnlocked = localStorage.getItem(COSMIC_POWER_STORAGE_KEY) === 'true'; |
There was a problem hiding this comment.
Update cosmic power state when unlocked at runtime
The provider only initializes its state from localStorage on mount, so if cosmic power is unlocked later in the session (e.g., after visiting all worlds), the context never updates and the bonus stays inactive until a full reload. This blocks the new laser/bonus behavior immediately after unlocking. Consider listening for storage changes or exposing an unlock/update method so the provider state flips when the unlock happens.
Useful? React with 👍 / 👎.
Move all camera position/quaternion access from JSX render into useFrame callback to prevent undefined access during initial render. Dynamically update beam geometry and impact position in animation loop.
- Move onClick handler to group level in GameOrb to avoid conditional undefined handler issues with React Three Fiber - Fix CosmicExplosion particle positions to use local coordinates (group already handles world positioning)
Use a single always-present clickable hit zone mesh sized by the
cosmic power multiplier. This eliminates conditional onClick={undefined}
which was causing React Three Fiber event handling errors.
Root cause: CameraController.tsx was accessing controls.target without proper validation that controls and its target property exist. Fixes applied: - CameraController.tsx: Add safe OrbitControls getter with validation for target property and required methods before accessing them - ClickingGame.tsx: Add null check for event parameter in handleMissedClick - LaserBeam.tsx: Add null checks for geometry and material before accessing/disposing them - GameOrb.tsx: Add null checks for material properties on cosmic ring and main mesh - CosmicExplosion.tsx: Add null checks for all material property access All components now use optional chaining (?.) to safely access nested properties that may be undefined during render or unmount cycles.
The OrbitControls component was missing the makeDefault prop, which is required for it to be accessible via useThree().controls. Without this prop, controls is always undefined, causing CameraController to fail when trying to access controls.target and other OrbitControls methods.
- CameraController: memoize getOrbitControls with useCallback and add comprehensive validation for all OrbitControls methods - GameOrb: make event parameter optional in handleClick to prevent undefined access when called without event - Both changes ensure safe access to .target property
The InfiniteLibrary component had its own OrbitControls that conflicted with the parent ThreeSixty component's OrbitControls. Having multiple OrbitControls instances can cause undefined behavior when accessing controls.target via useThree(). ThreeSixty already provides OrbitControls with makeDefault, so InfiniteLibrary should rely on that instead of creating its own.
New streamlined 3D rendering system in components/3d-v2/: Core: - SparkScene: Clean Canvas setup with adaptive DPR, optimized renderer - SplatLoader: SparkJS Gaussian Splatting integration with dynamic import Effects: - ParticleField: GPU-accelerated particles with custom GLSL shaders - CosmicOrb: Shader-based glowing orb with fresnel effects and rings - LaserEffect: Fast laser beam from camera to target Game: - TargetOrb: Performant clickable game target with glow effects - SphereHunter: Streamlined clicking game with combo system Shaders: - GPU particle vertex/fragment shaders with animated positions - Cosmic orb shaders with fresnel glow and energy patterns Key improvements over original: - ~70% less code, cleaner separation of concerns - All particle computation on GPU - Imperative Three.js where needed for type safety - SparkJS for modern Gaussian Splatting support
The previous "type-safe" version actually caused runtime errors because: - It cast undefined to a type then accessed properties on it - Type casting doesn't prevent undefined property access at runtime This version uses simple defensive checks: - Early return if controls is falsy - Optional chaining for method calls (getAzimuthalAngle?.()) - Runtime typeof checks before using methods - Explicit checks for target property before accessing The @ts-expect-error comments are intentional - OrbitControls methods aren't in the base type, but the runtime checks ensure safety.
OrbitControls with makeDefault must render BEFORE CameraController so that controls is properly set when CameraController mounts and calls useThree(). Previously, CameraController mounted first, causing controls.target to be undefined. The render order in R3F matters for makeDefault to work correctly.
Remove all cosmic power feature code that was causing persistent "Cannot read properties of undefined (reading 'target')" errors. Reverted files: - CameraController.tsx - back to original working version - ThreeSixty.tsx - removed cosmic power integration - ClickingGame.tsx - removed bonus multiplier logic - GameOrb.tsx - removed cosmic effects - GameHUD.tsx - removed cosmic power display - WorldGallery.tsx - removed visited scenes tracking - _app.tsx - removed CosmicPowerProvider - InfiniteLibrary.tsx - restored original OrbitControls setup Deleted files: - LaserBeam.tsx - CosmicExplosion.tsx - CosmicPowerContext.tsx The 3d-v2 folder with SparkJS implementation is preserved for future use.
The @mkkellogg/gaussian-splats-3d library was likely accessing OrbitControls.target internally, causing the persistent "Cannot read properties of undefined (reading 'target')" error. Removed: - GaussianSplatBackground import and usage in ThreeSixty.tsx - All splat-related state (useGaussianSplat, availableSplats, etc.) - Performance flags for splat handling - Splat type from SceneryOption interfaces across all components - 3D badge UI element for splat worlds The 3D scene now uses only panorama sphere backgrounds.
No description provided.