- 崩溃(index out of range):实体所在区块卸载后失去碰撞支撑无限下坠到 负坐标,光查询越界 panic——WorldView 增 ChunkLoaded,未加载区块实体冻结; chunk 光查询加越界防御(双保险) - 立面光照:mesh 每面取**邻格**光照(原版光照模型)——此前用方块自身光照 (sky=0)导致悬崖/矿洞立面全黑(泥土竖向黑色根因) - 掉落可见:生成后 1 秒拾取延迟 + 图标加大到 0.45 - 打击感:攻击命中音(block.hit)+ 实体受击白闪(uTint)+ 手持挥动动画 - 水下效果:相机入水蓝色叠加(渲染.md §8 水体) - 空手显示肤色拳头(白羊毛 × 肤色 tint),持方块正常显示 - HUD 布局:血条左下、饥饿右下(经典布局)
93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
// 掉落物实体:合并与拾取(实体系统.md §5)。
|
||
package entity
|
||
|
||
import (
|
||
"math"
|
||
"time"
|
||
)
|
||
|
||
// 掉落物参数(实体系统.md §5)。
|
||
const (
|
||
dropMergeDist = 1.0 // 同物品合并距离
|
||
dropStackMax = 64 // 合并上限
|
||
dropPickupDist = 1.5 // 拾取半径
|
||
dropPerChunk = 32 // 每区块掉落物上限
|
||
)
|
||
|
||
// dropPickupDelay 生成后可拾取延迟(测试可置 0)。
|
||
var dropPickupDelay = time.Second
|
||
|
||
// Drop 掉落物实体数据。
|
||
type Drop struct {
|
||
Item string
|
||
Count int
|
||
Born time.Time // 生成时刻(1 秒内不可拾取,掉落可见期)
|
||
}
|
||
|
||
// SpawnDrop 生成掉落物:同物品近距合并(上限 dropStackMax);超上限丢弃。
|
||
func (m *Manager) SpawnDrop(itemName string, count int, x, y, z float64) {
|
||
if count <= 0 {
|
||
return
|
||
}
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
// 合并
|
||
if old, ok := m.drops[itemName]; ok && !old.Dead {
|
||
dx, dz := old.Pos[0]-x, old.Pos[2]-z
|
||
if math.Sqrt(dx*dx+dz*dz) < dropMergeDist {
|
||
if d, ok := old.Data.(*Drop); ok && d.Count+count <= dropStackMax {
|
||
d.Count += count
|
||
return
|
||
}
|
||
}
|
||
}
|
||
// 上限检查
|
||
drops := 0
|
||
for _, e := range m.entities {
|
||
if e.Kind == KindItemDrop {
|
||
drops++
|
||
}
|
||
}
|
||
if drops >= dropPerChunk*8 { // 全局粗上限(按区块过滤后置)
|
||
return
|
||
}
|
||
m.next++
|
||
e := &Entity{ID: m.next, Kind: KindItemDrop, Pos: [3]float64{x, y, z}, Health: 1,
|
||
Data: &Drop{Item: itemName, Count: count, Born: time.Now()}}
|
||
e.Vel = [3]float64{(rand01(e.ID) - 0.5) * 2, 4, (rand01(e.ID>>16) - 0.5) * 2} // 弹出初速度
|
||
m.entities[e.ID] = e
|
||
m.drops[itemName] = e
|
||
}
|
||
|
||
// Data 实体附加数据。
|
||
// rand01 实体 ID 派生确定性随机(掉落初速度用,存档回放一致)。
|
||
func rand01(seed ID) float64 {
|
||
const mod = 1000003
|
||
return float64(seed%mod) / mod
|
||
}
|
||
|
||
// PickupNearby 拾取玩家附近掉落物,返回拾取的物品列表(实体系统.md §5 拾取)。
|
||
// 生成后 1 秒内不可拾取(掉落可见期,与原版一致)。
|
||
func (m *Manager) PickupNearby(px, py, pz float64) []Drop {
|
||
var out []Drop
|
||
for _, e := range m.List() {
|
||
if e.Kind != KindItemDrop || e.Dead {
|
||
continue
|
||
}
|
||
d, ok := e.Data.(*Drop)
|
||
if !ok {
|
||
continue
|
||
}
|
||
if time.Since(d.Born) < dropPickupDelay {
|
||
continue // 新鲜掉落:可拾取延迟(掉落可见)
|
||
}
|
||
dx, dy, dz := e.Pos[0]-px, e.Pos[1]-py, e.Pos[2]-pz
|
||
if dx*dx+dy*dy+dz*dz < dropPickupDist*dropPickupDist {
|
||
e.Dead = true
|
||
m.Remove(e.ID)
|
||
out = append(out, *d)
|
||
}
|
||
}
|
||
return out
|
||
}
|