package glossary import ( "math" "github.com/faiface/pixel" "github.com/faiface/pixel/imdraw" "golang.org/x/image/colornames" ) // Camera is a tool to get the screen center to be able to follow a certain point on a plane. type Camera struct { anglePhysic float64 // Angle in radians (math.Pi) angleFollow float64 // Angle expected to be in the near future. zoomPosPhysic float64 // Z zoomPosFollow float64 // Z expected to be in the near future. planePosPhysic pixel.Vec // X, Y planePosFollow pixel.Vec // X, Y expected to be in the near future. screenBound pixel.Rect moveSmooth bool } // NewCamera is a constructor. func NewCamera(_pos pixel.Vec, _screenBound pixel.Rect) *Camera { return &Camera{ anglePhysic: 0, angleFollow: 0, zoomPosPhysic: 1.0, zoomPosFollow: 1.0, planePosPhysic: _pos, planePosFollow: _pos, screenBound: _screenBound, moveSmooth: true, } } // ------------------------------------------------------------------------- // Read only // Transform returns a transformation matrix of a camera. // Use Transform().Project() to convert a game position to a screen position. // To do the inverse operation, it is recommended to use Camera#Unproject() rather than Transform().Unproject() func (camera Camera) Transform() pixel.Matrix { return pixel.IM. // This transformation order is significant. // ScaledXY(camera.planePos, pixel.V(camera.zoomPos, camera.zoomPos)). Scaled(camera.planePosPhysic, camera.zoomPosPhysic). // Scaling Rotated(camera.planePosPhysic, camera.anglePhysic). // Rotatation Moved(camera.screenBound.Center().Sub(camera.planePosPhysic)) // Translation } // Unproject converts a screen position to a game position. // This method is a replacement of Transform().Unproject() which might return a bit off position. func (camera Camera) Unproject(screenPosition pixel.Vec) (gamePosition pixel.Vec) { matrix1 := pixel.IM. Scaled(camera.planePosPhysic, camera.zoomPosPhysic). // Scaling Moved(camera.screenBound.Center().Sub(camera.planePosPhysic)) // Translation matrix2 := pixel.IM. Rotated(camera.planePosPhysic, -camera.anglePhysic) // Rotatation return matrix2.Project(matrix1.Unproject(screenPosition)) } // Angle returns the angle of a camera in radians. func (camera Camera) Angle() float64 { return camera.anglePhysic } // XYZ returns a camera's coordinates value X, Y, and Z in a current physical state. func (camera Camera) XYZ() (float64, float64, float64) { return camera.planePosPhysic.X, camera.planePosPhysic.Y, camera.zoomPosPhysic } // XY returns the X and Y of a camera as a vector. func (camera Camera) XY() pixel.Vec { return camera.planePosPhysic } // Z returns the zoom depth of a camera. func (camera Camera) Z() float64 { return camera.zoomPosPhysic } // ------------------------------------------------------------------------- // Read and Write // Update a camera's current physical state (physics) // by calculating coordinates X, Y, Z and its angle after delta time in seconds. func (camera *Camera) Update(dt float64) { if camera.moveSmooth { // lerp the camera position towards the target angle := pixel.V(camera.anglePhysic, 0) angleFollow := pixel.V(camera.angleFollow, 0) angle = pixel.Lerp(angle, angleFollow, 1-math.Pow(1.0/128, dt)) camera.anglePhysic = angle.X camera.planePosPhysic = pixel.Lerp(camera.planePosPhysic, camera.planePosFollow, 1-math.Pow(1.0/128, dt)) zoomPos := pixel.V(camera.zoomPosPhysic, 0) zoomFollow := pixel.V(camera.zoomPosFollow, 0) zoomPos = pixel.Lerp(zoomPos, zoomFollow, 1-math.Pow(1.0/128, dt)) camera.zoomPosPhysic = zoomPos.X } else { camera.anglePhysic = camera.angleFollow camera.planePosPhysic = camera.planePosFollow camera.zoomPosPhysic = camera.zoomPosFollow } } // Rotate a camera by certain degrees. // + ) Counterclockwise // - ) Clockwise func (camera *Camera) Rotate(degree float64) { camera.angleFollow += degree * math.Pi / 180 } // Zoom in and out with a camera by certain levels. // + ) Zoom in // - ) Zoom out func (camera *Camera) Zoom(byLevel float64) { const zoomAmount = 1.2 camera.zoomPosFollow *= math.Pow(zoomAmount, byLevel) } // Move camera a specified distance. func (camera *Camera) Move(distance pixel.Vec) { camera.planePosFollow = camera.planePosFollow.Add(distance) } // MoveTo () moves a camera to a point on a plane. func (camera *Camera) MoveTo(posAim pixel.Vec) { camera.planePosFollow = posAim } // SetScreenBound of a camera. func (camera *Camera) SetScreenBound(screenBound pixel.Rect) { camera.screenBound = screenBound } // ------------------------------------------------------------------- // Unnecessary // Aim for experiments. type Aim struct { pos pixel.Vec } // Draw aim as a dot. func (aim Aim) Draw(t pixel.Target) { imd := imdraw.New(nil) imd.Color = colornames.Red imd.Push(aim.pos) imd.Circle(10, 0) imd.Draw(t) }