// 客户端输入处理:键鼠状态采集(UI与输入.md §6)。 // // 设计:回调只更新状态,游戏逻辑在 tick 里消费状态(主循环解耦)。 package main import ( "math" "mc/internal/game" "mc/internal/physics" "github.com/go-gl/glfw/v3.3/glfw" ) // inputState 键鼠输入状态。 type inputState struct { win *glfw.Window // 按键状态 forward, back, left, right, jump, sneak, sprint bool // 鼠标 lastX, lastY float64 firstMouse bool dyaw, dpitch float64 // 累计角度增量(弧度,tick 消费) // 按键事件(tick 消费) breakDown bool breakHeld bool placeDown bool selectSlot int // -1 无 } // newInputState 创建输入状态并注册回调。 func newInputState(win *glfw.Window) *inputState { in := &inputState{win: win, firstMouse: true, selectSlot: -1} win.SetKeyCallback(in.keyCallback) win.SetMouseButtonCallback(in.mouseButtonCallback) win.SetCursorPosCallback(in.cursorCallback) win.SetScrollCallback(in.scrollCallback) return in } // keyCallback 键盘回调(动作映射:WASD/空格/Shift/Ctrl/Esc)。 func (in *inputState) keyCallback(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, _ glfw.KeyMods) { down := action != glfw.Release switch key { case glfw.KeyW: in.forward = down case glfw.KeyS: in.back = down case glfw.KeyA: in.left = down case glfw.KeyD: in.right = down case glfw.KeySpace: in.jump = down case glfw.KeyLeftShift: in.sneak = down case glfw.KeyLeftControl: in.sprint = down case glfw.KeyEscape: if action == glfw.Press { in.win.SetShouldClose(true) } case glfw.Key1, glfw.Key2, glfw.Key3, glfw.Key4, glfw.Key5, glfw.Key6, glfw.Key7, glfw.Key8, glfw.Key9: if action == glfw.Press { in.selectSlot = int(key) - int(glfw.Key1) } } } // mouseButtonCallback 鼠标按键:左键破坏(按住累积)、右键放置(UI与输入.md §6 长按)。 func (in *inputState) mouseButtonCallback(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, _ glfw.ModifierKey) { switch button { case glfw.MouseButtonLeft: if action == glfw.Press { in.breakDown = true } in.breakHeld = action != glfw.Release case glfw.MouseButtonRight: if action == glfw.Press { in.placeDown = true } } } // cursorCallback 鼠标移动:yaw/pitch 累积(渲染.md §2 主线程消费)。 func (in *inputState) cursorCallback(_ *glfw.Window, x, y float64) { if in.firstMouse { in.lastX, in.lastY = x, y in.firstMouse = false return } dx, dy := x-in.lastX, y-in.lastY in.lastX, in.lastY = x, y const sens = 0.0025 in.dyaw += dx * sens in.dpitch -= dy * sens if in.dpitch > math.Pi/2-0.01 { in.dpitch = math.Pi/2 - 0.01 } if in.dpitch < -math.Pi/2+0.01 { in.dpitch = -math.Pi/2 + 0.01 } } // scrollCallback 滚轮切热键栏(UI与输入.md §6)。 func (in *inputState) scrollCallback(_ *glfw.Window, _, yoff float64) { if yoff > 0 { in.selectSlot = -2 // 上滚 } else if yoff < 0 { in.selectSlot = -3 // 下滚 } } // apply 每 tick 把输入状态转成玩家动作(game.Session 驱动)。 func (in *inputState) apply(s *game.Session) { cam := s.Cam p := s.Player // 视角 if in.dyaw != 0 || in.dpitch != 0 { cam.Yaw += in.dyaw cam.Pitch += in.dpitch in.dyaw, in.dpitch = 0, 0 } // 水平移动(物理与碰撞.md §3:走/冲刺/潜行) f := cam.Forward() r := cam.Right() wish := [2]float64{} if in.forward { wish[0] += f[0] wish[1] += f[2] } if in.back { wish[0] -= f[0] wish[1] -= f[2] } if in.right { wish[0] += r[0] wish[1] += r[2] } if in.left { wish[0] -= r[0] wish[1] -= r[2] } speed := 4.3 if in.sprint { speed *= 1.3 } if in.sneak { speed *= 0.3 } l := math.Sqrt(wish[0]*wish[0] + wish[1]*wish[1]) if l > 0 { p.Vel[0] = wish[0] / l * speed p.Vel[2] = wish[1] / l * speed } else { p.Vel[0] = 0 p.Vel[2] = 0 } // 跳跃 if in.jump && p.OnGround { p.Jump() } // 热键栏切换 switch in.selectSlot { case -2: s.Inv.Selected = (s.Inv.Selected + 1) % 9 case -3: s.Inv.Selected = (s.Inv.Selected + 8) % 9 default: if in.selectSlot >= 0 && in.selectSlot < 9 { s.Inv.Selected = in.selectSlot } } in.selectSlot = -1 // 破坏(按住累积,方块交互与动画.md §3) if in.breakDown { s.BeginBreak() in.breakDown = false } if !in.breakHeld && s.BreakProgress() != 0 { s.EndBreak() } // 放置 if in.placeDown { s.PlaceSelected() in.placeDown = false } } var _ = physics.Player{} // 保持引用(后续输入扩展用)