fix(game): 崩溃修复(卸载区块实体冻结)+ 立面光照、掉落可见、打击感、水下效果、空手拳头、血条左/饥饿右

- 崩溃(index out of range):实体所在区块卸载后失去碰撞支撑无限下坠到
  负坐标,光查询越界 panic——WorldView 增 ChunkLoaded,未加载区块实体冻结;
  chunk 光查询加越界防御(双保险)
- 立面光照:mesh 每面取**邻格**光照(原版光照模型)——此前用方块自身光照
  (sky=0)导致悬崖/矿洞立面全黑(泥土竖向黑色根因)
- 掉落可见:生成后 1 秒拾取延迟 + 图标加大到 0.45
- 打击感:攻击命中音(block.hit)+ 实体受击白闪(uTint)+ 手持挥动动画
- 水下效果:相机入水蓝色叠加(渲染.md §8 水体)
- 空手显示肤色拳头(白羊毛 × 肤色 tint),持方块正常显示
- HUD 布局:血条左下、饥饿右下(经典布局)
This commit is contained in:
NianGao Dev
2026-08-16 20:59:28 +08:00
parent 5da460c9a2
commit 9e2f5a6186
16 changed files with 223 additions and 36 deletions

View File

@@ -71,7 +71,8 @@ func (r *Renderer) ensureBillQuad() {
}
// DrawBillboard 在 (x,y,z)(脚底)绘制 h 高 × w 宽的实体广告牌,贴图 uv。
func (r *Renderer) DrawBillboard(x, y, z float32, h, w float32, uv [4]float32) {
// flash=true 时整体白闪(受击反馈,打击感)。
func (r *Renderer) DrawBillboard(x, y, z float32, h, w float32, uv [4]float32, flash bool) {
if r.entityTex == 0 {
return
}
@@ -88,6 +89,12 @@ func (r *Renderer) DrawBillboard(x, y, z float32, h, w float32, uv [4]float32) {
gl.Uniform3f(offLoc, x, y, z)
cutLoc := gl.GetUniformLocation(r.prog, gl.Str("uCutout\x00"))
gl.Uniform1f(cutLoc, 0)
tintLoc := gl.GetUniformLocation(r.prog, gl.Str("uTint\x00"))
if flash {
gl.Uniform4f(tintLoc, 2.5, 2.5, 2.5, 1) // 受击白闪
} else {
gl.Uniform4f(tintLoc, 1, 1, 1, 1)
}
// 相机基向量(视图矩阵列 0/1 即世界系下的 right/up)
rightLoc := gl.GetUniformLocation(r.prog, gl.Str("uCamRight\x00"))
upLoc := gl.GetUniformLocation(r.prog, gl.Str("uCamUp\x00"))

View File

@@ -110,6 +110,8 @@ func (r *Renderer) DrawChunks(get func(i int) (int32, int32), n int) {
uvMaxLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00"))
gl.Uniform2f(uvMinLoc, 0, 0) // 区块顶点 UV 直接使用
gl.Uniform2f(uvMaxLoc, 1, 1)
tintLoc := gl.GetUniformLocation(r.prog, gl.Str("uTint\x00"))
gl.Uniform4f(tintLoc, 1, 1, 1, 1) // 区块无颜色乘数
// pass 1+2:不透明与 cutout(alpha test,不排序)
gl.Uniform1f(cutLoc, 0)

View File

@@ -150,6 +150,8 @@ func (r *Renderer) DrawCrackOverlay(x, y, z float32, stage int) {
uvMaxLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00"))
gl.Uniform2f(uvMinLoc, 0, 0)
gl.Uniform2f(uvMaxLoc, 1, 1)
tintLoc := gl.GetUniformLocation(r.prog, gl.Str("uTint\x00"))
gl.Uniform4f(tintLoc, 1, 1, 1, 1)
gl.Enable(gl.POLYGON_OFFSET_FILL)
gl.PolygonOffset(-1, -1)
@@ -206,8 +208,8 @@ func (r *Renderer) ClearHand() {
}
}
// DrawHand 绘制手持方块(顶点已是视图空间,仅乘投影矩阵)。
func (r *Renderer) DrawHand() {
// DrawHand 绘制手持方块(顶点已是视图空间,仅乘投影矩阵;tint 肤色乘数)。
func (r *Renderer) DrawHand(tint [3]float32) {
if r.handVao == 0 {
return
}
@@ -226,6 +228,8 @@ func (r *Renderer) DrawHand() {
uvMaxLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00"))
gl.Uniform2f(uvMinLoc, 0, 0)
gl.Uniform2f(uvMaxLoc, 1, 1)
tintLoc := gl.GetUniformLocation(r.prog, gl.Str("uTint\x00"))
gl.Uniform4f(tintLoc, tint[0], tint[1], tint[2], 1)
gl.BindVertexArray(r.handVao)
gl.DrawElements(gl.TRIANGLES, 36, gl.UNSIGNED_INT, gl.PtrOffset(0))
gl.BindVertexArray(0)

View File

@@ -250,12 +250,14 @@ void main() {
`
// chunkFragSrc 默认方块片元着色器:cutout 剔除 + 光照亮度混合。
// uTint 颜色乘数(实体受击白闪/手持肤色,默认 1,1,1,1)。
const chunkFragSrc = `
#version 330 core
in vec2 vUV;
in vec2 vLight;
uniform sampler2D uTex;
uniform float uCutout; // 1=alpha test(树叶/玻璃),0=普通/半透明
uniform vec4 uTint;
out vec4 fragColor;
void main() {
vec4 c = texture(uTex, vUV);
@@ -263,6 +265,6 @@ void main() {
float sky = vLight.x / 15.0;
float blk = vLight.y / 15.0;
float b = max(sky, blk * 0.9 + 0.06);
fragColor = vec4(c.rgb * b, c.a);
fragColor = vec4(c.rgb * b * uTint.rgb, c.a);
}
`