Files
sanspie b6dd46f523
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 14m14s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 16m1s
ci / release (push) Failing after 14m38s
Stage 10a follow-up: camera scene tests, rewind check, value Adjust
- camera.NewService and SetAccessibility; AttachWorld wires the accessibility settings
- tests/camera: priorities, blends, cuts, zones, follow, bounds, outputs, shake, zero allocations, BrainStep benchmark
- integration: rewind restores the camera state across a zone change
- camera2d.Adjuster passes the view by value (no per-frame allocation), D304 updated
- modules/camera/module.mk: test-camera in CI, bench-camera
2026-10-21 01:32:28 +03:00

130 lines
2.9 KiB
Go

package camera
import (
"aego/contracts/camera2d"
"aego/core/clock"
"aego/engine"
"aego/engine/module"
"aego/engine/settings"
)
const (
ID = "camera"
ServiceID = ID + ".service"
SystemUpdate = ID + ".update"
ScriptModule = "aego/camera"
needsScripts = "script"
)
type access struct {
store *settings.Store
shakeID string
reducedID string
shake settings.Key
reduced settings.Key
}
func (a *access) read() (float32, bool) {
if a == nil || a.store == nil {
return 1, false
}
if a.shake == 0 && a.shakeID != "" {
a.shake = a.store.Key(a.shakeID)
}
if a.reduced == 0 && a.reducedID != "" {
a.reduced = a.store.Key(a.reducedID)
}
scale := float32(1)
if a.shake != 0 {
scale = float32(a.store.Float(a.shake))
}
return scale, a.reduced != 0 && a.store.Bool(a.reduced)
}
type Module struct {
acc access
}
func New() *Module {
return &Module{}
}
func (*Module) Info() module.Info {
return module.Info{ID: ID, Version: engine.Version, Requires: []string{needsScripts}, Builtin: true,
DisplayName: "Cameras 2D", DocsOrder: 45, DocsIcon: "comp.vcam"}
}
func (*Module) IconDir() string {
return "modules/camera/res/icons/src"
}
func (m *Module) Register(r *module.Registry) error {
if err := registerTypes(r); err != nil {
return err
}
r.System(engine.SystemDesc{ID: SystemUpdate, Phase: engine.PhasePostUpdate, Scope: engine.ScopePlay, Pause: engine.PauseAlways, System: update{}})
if r.Bindings() != nil {
installScript(r.ScriptModule(ScriptModule))
}
registerDocs(r)
return nil
}
func (m *Module) Init(ctx *module.InitContext) error {
m.acc = access{store: ctx.Settings}
for _, p := range ctx.Provided(camera2d.AccessibilityKind) {
if a, ok := p.Value.(camera2d.Accessibility); ok {
m.acc.shakeID, m.acc.reducedID = a.ScreenShake, a.ReducedMotion
}
}
return nil
}
func (m *Module) AttachWorld(ctx *module.AttachContext) error {
if !ctx.Play || ctx.World == nil || ctx.Scene == nil {
return nil
}
s := NewService(ctx.Scene, ctx.World)
s.acc = &m.acc
ctx.World.SetService(ServiceID, s)
ctx.World.SetView(camera2d.ServiceID, camera2d.Adjuster(s))
if s.hub != nil {
s.hub.Probe(typeBlend, s.blendAlive)
}
return nil
}
func (m *Module) DetachWorld(ctx *module.AttachContext) {
if !ctx.Play || ctx.World == nil {
return
}
if s := Of(ctx.World); s != nil && s.hub != nil {
s.hub.Probe(typeBlend, nil)
}
ctx.World.RemoveService(camera2d.ServiceID)
ctx.World.RemoveService(ServiceID)
}
func Of(w *engine.World) *Service {
if w == nil {
return nil
}
s, _ := engine.ServiceOf[*Service](w, ServiceID)
return s
}
type update struct{}
func (update) Run(ctx *engine.Context) {
s := Of(ctx.World)
if s == nil {
return
}
game, real := float32(ctx.Dt), float32(ctx.Unscaled)
if t := ctx.Time; t != nil {
game = float32(t.Span(clock.Game).Delta)
real = float32(t.Span(clock.Real).Delta)
}
s.Update(game, real)
}