Files
ngzz-mc/cmd/client/input.go
NianGao Dev ea97eaa616 fix(render): 方格垂直面被背面剔除(立面透明空洞/闪烁)+ 鼠标左右反转
- mesh.faceDef 六面绕序铁律:旧定义只有顶面是窗口空间顺时针
  (FrontFace=CW 的正面),底/东/西/南/北五面是逆时针被全部剔除——
  世界只剩顶面、立面透明、移动时面闪烁(数值投影验证 5 面 area>0 被剔)
- 覆盖层 cubeFaces(裂纹盒/手持方块)同步反转
- 鼠标右移应右转:yaw=0 朝 -Z,右转朝 +X 需 yaw 减小,dyaw 取反
  (原实现 dyaw += dx 左右颠倒)
2026-08-16 20:01:13 +08:00

219 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build gl
// 客户端输入处理:键鼠状态采集(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 无
// 鼠标与 UI 事件(UI与输入.md §6 屏幕交互)
cursorX, cursorY float64
clickX, clickY float32
clicked bool
escPressed bool
}
// 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.ModifierKey) {
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.escPressed = 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.clicked = true
in.clickX = float32(in.cursorX)
in.clickY = float32(in.cursorY)
}
in.breakHeld = action != glfw.Release
case glfw.MouseButtonRight:
if action == glfw.Press {
in.placeDown = true
}
}
}
// cursorCallback 鼠标移动:yaw/pitch 累积(渲染.md §2 主线程消费)。
// 约定:鼠标右移 dx>0 应右转(yaw 减小——yaw=0 朝向 -Z,右转朝 +X);
// 鼠标上移 dy<0 应抬头(pitch 增大)。
func (in *inputState) cursorCallback(_ *glfw.Window, x, y float64) {
in.cursorX, in.cursorY = x, y
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
}
// 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
// 视角(俯仰钳制在相机上,而非增量上——旧实现钳增量,抬头可翻转超过 ±90°)
if in.dyaw != 0 || in.dpitch != 0 {
cam.Yaw += in.dyaw
cam.Pitch += in.dpitch
in.dyaw, in.dpitch = 0, 0
const maxPitch = math.Pi/2 - 0.01
if cam.Pitch > maxPitch {
cam.Pitch = maxPitch
}
if cam.Pitch < -maxPitch {
cam.Pitch = -maxPitch
}
}
// 游戏中左键=破坏,不是 UI 点击:清除遗留点击状态,防止暂停菜单收到过期点击
in.clicked = false
// 水平移动(物理与碰撞.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()
}
// 放置(右键;命中工作台则打开合成界面,工作台与合成表.md §5)
if in.placeDown {
if !s.TryOpenCrafting() {
s.PlaceSelected()
}
in.placeDown = false
}
}
var _ = physics.Player{} // 保持引用(后续输入扩展用)