151 lines
5.0 KiB
Go
151 lines
5.0 KiB
Go
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)
|
|
}
|