diff --git a/engine/camera.go b/engine/camera.go index e16fa2e..0f7f430 100644 --- a/engine/camera.go +++ b/engine/camera.go @@ -6,8 +6,9 @@ import ( "github.com/hajimehoshi/ebiten/v2" ) -// Camera models a camera that is viewing a scene. Changes to the -// configuration take effect immediately. +// Camera models a camera that is viewing a scene. +// Changes to the configuration take effect immediately. +// Camera ignores Scene.Draw and calls Scene's children's Draw. type Camera struct { ID Scene *Scene @@ -15,16 +16,20 @@ type Camera struct { // Camera controls Bounds image.Rectangle // world coordinates Centre image.Point // world coordinates - //Rotation float64 // radians - Zoom float64 // unitless - Filter ebiten.Filter + Zoom float64 // unitless game *Game } // Draw applies transformations to opts, then calls c.Scene.Draw with it. func (c *Camera) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) { + if c.Scene.Hidden { + return + } + + // Compute the geometry matrix for the camera controls. + // The lower bound on zoom is the larger of // { (ScreenWidth / BoundsWidth), (ScreenHeight / BoundsHeight) } zoom := c.Zoom @@ -54,19 +59,30 @@ func (c *Camera) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) { centre.Y = c.Bounds.Max.Y - shz } - // Apply camera controls to geom. - // 1. Move centre to the origin - opts.GeoM.Translate(-float64(centre.X), -float64(centre.Y)) - // 2. Zoom and rotate - opts.GeoM.Scale(zoom, zoom) - //geom.Rotate(c.Rotation) - // 3. Move the origin to the centre of screen space. - opts.GeoM.Translate(sw2, sh2) - // Apply other options opts.Filter = c.Filter - c.Scene.Draw(screen, opts) + // Draw everything. + og := opts.GeoM + for _, i := range c.Scene.Components { + if d, ok := i.(Drawer); ok { + cs := 1.0 + if s, ok := i.(CoordScaler); ok { + cs = s.CoordScale() + } + var geom ebiten.GeoM + // 1. Move centre to the origin, subject to CoordScale + geom.Translate(-float64(centre.X)*cs, -float64(centre.Y)*cs) + // 2. Zoom (this is also where rotation would be) + geom.Scale(zoom, zoom) + // 3. Move the origin to the centre of screen space. + geom.Translate(sw2, sh2) + // 4. Apply transforms from the caller. + geom.Concat(og) + opts.GeoM = geom + d.Draw(screen, opts) + } + } } // Update passes the call to c.Scene. diff --git a/engine/interface.go b/engine/interface.go index e389cb6..3b42a23 100644 --- a/engine/interface.go +++ b/engine/interface.go @@ -11,6 +11,12 @@ type Collider interface { CollidesWith(image.Rectangle) bool } +// CoordScaler components have a scaling factor. This is used for +// e.g. parallax layers in a scene, and can be thought of as 1/distance. +type CoordScaler interface { + CoordScale() float64 +} + // Drawer components can draw themselves. Draw is called often. // Each component is responsible for calling Draw on its child components // (so that hiding the parent can hide the children, etc).