TomatitoToho commited on
Commit
e86e998
·
verified ·
1 Parent(s): 8b2d569

Upload src/components/game/GameCanvas.tsx with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/components/game/GameCanvas.tsx +138 -0
src/components/game/GameCanvas.tsx ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { useEffect, useState, useRef } from 'react'
4
+ import { Canvas } from '@react-three/fiber'
5
+ import { GameScene } from './Scene'
6
+ import { usePlayerStore } from '@/stores/playerStore'
7
+ import { useWorldStore } from '@/stores/worldStore'
8
+ import { useGameStore } from '@/stores/gameStore'
9
+ import { useUIStore } from '@/stores/uiStore'
10
+ import { usePlayerControls, updatePlayerMovement } from '@/hooks/usePlayerControls'
11
+ import { Hotbar } from '../ui/Hotbar'
12
+ import { HUD } from '../ui/HUD'
13
+ import { DebugOverlay } from '../ui/DebugOverlay'
14
+ import { Crosshair } from '../ui/Crosshair'
15
+ import { MobileControls } from '../ui/MobileControls'
16
+ import { CreativeInventory } from '../ui/CreativeInventory'
17
+ import { BLOCK_DEFS, CREATIVE_BLOCKS, BLOCK_AIR } from '@/engine/constants'
18
+
19
+ export function GameCanvas() {
20
+ const { keys, isPointerLocked, requestPointerLock, raycastBlock } = usePlayerControls()
21
+ const [ready, setReady] = useState(false)
22
+ const lastTimeRef = useRef(0)
23
+
24
+ // Init
25
+ useEffect(() => {
26
+ useWorldStore.getState().initWorld(Math.floor(Math.random() * 1000000))
27
+ usePlayerStore.getState().initPlayer()
28
+ useGameStore.getState().setLoading(false)
29
+ setReady(true)
30
+ }, [])
31
+
32
+ // Game loop
33
+ useEffect(() => {
34
+ let animId: number
35
+ const loop = (time: number) => {
36
+ const delta = Math.min((time - lastTimeRef.current) / 1000, 0.1)
37
+ lastTimeRef.current = time
38
+ if (keys.current) updatePlayerMovement(keys.current, delta)
39
+ useWorldStore.getState().updateWorldTime()
40
+
41
+ // Handle mobile block interaction
42
+ if (window.__MC_MOBILE_BREAK) {
43
+ const hit = raycastBlock()
44
+ if (hit) useWorldStore.getState().setBlock(hit.blockPos[0], hit.blockPos[1], hit.blockPos[2], BLOCK_AIR)
45
+ window.__MC_MOBILE_BREAK = false
46
+ }
47
+ if (window.__MC_MOBILE_PLACE) {
48
+ const hit = raycastBlock()
49
+ const { player } = usePlayerStore.getState()
50
+ if (hit && player) {
51
+ const slot = useUIStore.getState().hotbarSlot
52
+ const item = player.inventory.hotbar[slot]
53
+ if (item && BLOCK_DEFS[item.id]?.solid) {
54
+ useWorldStore.getState().setBlock(hit.placePos[0], hit.placePos[1], hit.placePos[2], item.id)
55
+ }
56
+ }
57
+ window.__MC_MOBILE_PLACE = false
58
+ }
59
+
60
+ animId = requestAnimationFrame(loop)
61
+ }
62
+ animId = requestAnimationFrame(loop)
63
+ return () => cancelAnimationFrame(animId)
64
+ }, [keys, raycastBlock])
65
+
66
+ if (!ready) {
67
+ return (
68
+ <div className="w-screen h-screen bg-[#2C2C2C] flex flex-col items-center justify-center gap-4">
69
+ <div className="text-4xl font-bold text-white" style={{ fontFamily: 'monospace', textShadow: '2px 2px #000' }}>
70
+ Minecraft Clone
71
+ </div>
72
+ <div className="w-48 h-2 bg-gray-700 rounded overflow-hidden">
73
+ <div className="h-full bg-green-500 animate-pulse" style={{ width: '60%' }} />
74
+ </div>
75
+ <div className="text-gray-400 text-sm">Generating world...</div>
76
+ </div>
77
+ )
78
+ }
79
+
80
+ const isMobileDevice = typeof window !== 'undefined' && ('ontouchstart' in window || navigator.maxTouchPoints > 0)
81
+
82
+ return (
83
+ <div className="relative w-screen h-screen overflow-hidden bg-black select-none">
84
+ <Canvas
85
+ gl={{ antialias: false, powerPreference: 'high-performance', alpha: false, depth: true, stencil: false }}
86
+ dpr={[1, isMobileDevice ? 1 : 1.5]}
87
+ camera={{ fov: isMobileDevice ? 80 : 70, near: 0.05, far: 300, position: [8, 80 + 1.62, 8] }}
88
+ style={{ position: 'absolute', inset: 0 }}
89
+ onCreated={({ gl }) => { gl.toneMapping = THREE.ACESFilmicToneMapping }}
90
+ >
91
+ <GameScene />
92
+ </Canvas>
93
+
94
+ {/* Crosshair (desktop only) */}
95
+ {!isMobileDevice && <Crosshair />}
96
+
97
+ {/* Hotbar */}
98
+ <div className="absolute bottom-1 left-1/2 -translate-x-1/2 z-10 sm:bottom-2">
99
+ <Hotbar />
100
+ </div>
101
+
102
+ {/* HUD */}
103
+ <HUD />
104
+
105
+ {/* Debug */}
106
+ <DebugOverlay />
107
+
108
+ {/* Creative inventory panel */}
109
+ <CreativeInventory />
110
+
111
+ {/* Mobile controls */}
112
+ <MobileControls />
113
+
114
+ {/* Click to play overlay (desktop only) */}
115
+ {!isMobileDevice && !document.pointerLockElement && (
116
+ <div
117
+ className="absolute inset-0 flex items-center justify-center bg-black/60 cursor-pointer z-50"
118
+ onClick={requestPointerLock}
119
+ >
120
+ <div className="text-center text-white p-8 rounded-xl bg-black/40 backdrop-blur-sm">
121
+ <h2 className="text-3xl sm:text-5xl font-bold mb-4" style={{ fontFamily: 'monospace', textShadow: '3px 3px #000' }}>
122
+ ⛏ Minecraft Clone
123
+ </h2>
124
+ <p className="text-xl mb-4 text-green-400">Click to play</p>
125
+ <div className="text-sm opacity-70 space-y-1 text-left max-w-md mx-auto">
126
+ <p>🎮 <b>WASD</b> - Move | <b>Space</b> - Jump | <b>Shift</b> - Sneak</p>
127
+ <p>🖱️ <b>Left Click</b> - Break | <b>Right Click</b> - Place</p>
128
+ <p>⚡ <b>F</b> - Fly | <b>Ctrl</b> - Sprint | <b>1-9</b> - Hotbar</p>
129
+ <p>🔧 <b>F3</b> - Debug | <b>E</b> - Inventory | <b>Scroll</b> - Switch item</p>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ )}
134
+ </div>
135
+ )
136
+ }
137
+
138
+ import * as THREE from 'three'