main.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Copyright 2020 The Ebiten Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "bytes"
  17. "fmt"
  18. "image"
  19. _ "image/png"
  20. "log"
  21. "math"
  22. "golang.org/x/image/math/f64"
  23. "github.com/hajimehoshi/ebiten/v2"
  24. "github.com/hajimehoshi/ebiten/v2/ebitenutil"
  25. "github.com/hajimehoshi/ebiten/v2/examples/resources/images"
  26. )
  27. const (
  28. screenWidth = 640
  29. screenHeight = 480
  30. )
  31. const (
  32. tileSize = 16
  33. tileXCount = 25
  34. )
  35. const (
  36. worldWidth = 480
  37. worldHeight = 320
  38. worldSizeX = worldWidth / tileSize
  39. )
  40. var (
  41. tilesImage *ebiten.Image
  42. )
  43. func init() {
  44. // Decode an image from the image file's byte slice.
  45. img, _, err := image.Decode(bytes.NewReader(images.Tiles_png))
  46. if err != nil {
  47. log.Fatal(err)
  48. }
  49. tilesImage = ebiten.NewImageFromImage(img)
  50. }
  51. type Camera struct {
  52. ViewPort f64.Vec2
  53. Position f64.Vec2
  54. ZoomFactor int
  55. Rotation int
  56. }
  57. func (c *Camera) String() string {
  58. return fmt.Sprintf(
  59. "T: %.1f, R: %d, S: %d",
  60. c.Position, c.Rotation, c.ZoomFactor,
  61. )
  62. }
  63. func (c *Camera) viewportCenter() f64.Vec2 {
  64. return f64.Vec2{
  65. c.ViewPort[0] * 0.5,
  66. c.ViewPort[1] * 0.5,
  67. }
  68. }
  69. func (c *Camera) worldMatrix() ebiten.GeoM {
  70. m := ebiten.GeoM{}
  71. m.Translate(-c.Position[0], -c.Position[1])
  72. // We want to scale and rotate around center of image / screen
  73. m.Translate(-c.viewportCenter()[0], -c.viewportCenter()[1])
  74. m.Scale(
  75. math.Pow(1.01, float64(c.ZoomFactor)),
  76. math.Pow(1.01, float64(c.ZoomFactor)),
  77. )
  78. m.Rotate(float64(c.Rotation) * 2 * math.Pi / 360)
  79. m.Translate(c.viewportCenter()[0], c.viewportCenter()[1])
  80. return m
  81. }
  82. func (c *Camera) Render(world, screen *ebiten.Image) {
  83. screen.DrawImage(world, &ebiten.DrawImageOptions{
  84. GeoM: c.worldMatrix(),
  85. })
  86. }
  87. func (c *Camera) ScreenToWorld(posX, posY int) (float64, float64) {
  88. inverseMatrix := c.worldMatrix()
  89. if inverseMatrix.IsInvertible() {
  90. inverseMatrix.Invert()
  91. return inverseMatrix.Apply(float64(posX), float64(posY))
  92. } else {
  93. // When scaling it can happened that matrix is not invertable
  94. return math.NaN(), math.NaN()
  95. }
  96. }
  97. func (c *Camera) Reset() {
  98. c.Position[0] = 0
  99. c.Position[1] = 0
  100. c.Rotation = 0
  101. c.ZoomFactor = 0
  102. }
  103. type Game struct {
  104. layers [][]int
  105. world *ebiten.Image
  106. camera Camera
  107. }
  108. func (g *Game) Update() error {
  109. if ebiten.IsKeyPressed(ebiten.KeyA) || ebiten.IsKeyPressed(ebiten.KeyArrowLeft) {
  110. g.camera.Position[0] -= 1
  111. }
  112. if ebiten.IsKeyPressed(ebiten.KeyD) || ebiten.IsKeyPressed(ebiten.KeyArrowRight) {
  113. g.camera.Position[0] += 1
  114. }
  115. if ebiten.IsKeyPressed(ebiten.KeyW) || ebiten.IsKeyPressed(ebiten.KeyArrowUp) {
  116. g.camera.Position[1] -= 1
  117. }
  118. if ebiten.IsKeyPressed(ebiten.KeyS) || ebiten.IsKeyPressed(ebiten.KeyArrowDown) {
  119. g.camera.Position[1] += 1
  120. }
  121. if ebiten.IsKeyPressed(ebiten.KeyQ) {
  122. if g.camera.ZoomFactor > -2400 {
  123. g.camera.ZoomFactor -= 1
  124. }
  125. }
  126. if ebiten.IsKeyPressed(ebiten.KeyE) {
  127. if g.camera.ZoomFactor < 2400 {
  128. g.camera.ZoomFactor += 1
  129. }
  130. }
  131. if ebiten.IsKeyPressed(ebiten.KeyR) {
  132. g.camera.Rotation += 1
  133. }
  134. if ebiten.IsKeyPressed(ebiten.KeySpace) {
  135. g.camera.Reset()
  136. }
  137. return nil
  138. }
  139. func (g *Game) Draw(screen *ebiten.Image) {
  140. // Draw each tile with each DrawImage call.
  141. // As the source images of all DrawImage calls are always same,
  142. // this rendering is done very effectively.
  143. // For more detail, see https://pkg.go.dev/github.com/hajimehoshi/ebiten/v2#Image.DrawImage
  144. for _, l := range g.layers {
  145. for i, t := range l {
  146. op := &ebiten.DrawImageOptions{}
  147. op.GeoM.Translate(float64((i%worldSizeX)*tileSize), float64((i/worldSizeX)*tileSize))
  148. sx := (t % tileXCount) * tileSize
  149. sy := (t / tileXCount) * tileSize
  150. g.world.DrawImage(tilesImage.SubImage(image.Rect(sx, sy, sx+tileSize, sy+tileSize)).(*ebiten.Image), op)
  151. }
  152. }
  153. g.camera.Render(g.world, screen)
  154. worldX, worldY := g.camera.ScreenToWorld(ebiten.CursorPosition())
  155. ebitenutil.DebugPrint(
  156. screen,
  157. fmt.Sprintf("TPS: %0.2f\nMove (WASD/Arrows)\nZoom (QE)\nRotate (R)\nReset (Space)", ebiten.ActualTPS()),
  158. )
  159. ebitenutil.DebugPrintAt(
  160. screen,
  161. fmt.Sprintf("%s\nCursor World Pos: %.2f,%.2f",
  162. g.camera.String(),
  163. worldX, worldY),
  164. 0, screenHeight-32,
  165. )
  166. }
  167. func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
  168. return screenWidth, screenHeight
  169. }
  170. func main() {
  171. g := &Game{
  172. layers: [][]int{
  173. {
  174. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  175. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  176. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243,
  177. 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 244, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  178. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  179. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  180. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  181. 243, 243, 244, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243,
  182. 243, 243, 243, 243, 243, 243, 243, 243, 243, 219, 243, 243, 243, 219, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 243, 243,
  183. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  184. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  185. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  186. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  187. 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  188. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  189. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  190. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  191. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 243, 243,
  192. 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  193. 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
  194. },
  195. {
  196. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  197. 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 64, 65, 66, 67, 68, 0, 0, 0, 0,
  198. 0, 0, 0, 0, 0, 51, 52, 53, 54, 55, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 89, 90, 91, 92, 93, 0, 0, 0, 0,
  199. 0, 0, 0, 0, 0, 76, 77, 78, 79, 80, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 114, 115, 116, 117, 118, 0, 0, 0, 0,
  200. 0, 0, 0, 0, 0, 101, 102, 103, 104, 105, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 139, 140, 141, 142, 143, 0, 0, 0, 0,
  201. 0, 0, 0, 0, 0, 126, 127, 128, 129, 130, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 288, 0, 0, 0, 0,
  202. 0, 0, 0, 0, 0, 303, 303, 245, 242, 303, 303, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0,
  203. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0,
  204. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0,
  205. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0,
  206. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 45, 46, 47, 48,
  207. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 70, 71, 72, 73,
  208. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 95, 96, 97, 98,
  209. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 120, 121, 122, 123,
  210. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 145, 146, 147, 148,
  211. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0,
  212. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0,
  213. 0, 0, 0, 0, 0, 0, 0, 245, 267, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, 270, 242, 0, 0, 0, 0, 0,
  214. 0, 0, 0, 0, 0, 0, 0, 245, 192, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 222, 0, 0, 0, 0, 0,
  215. 0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  216. },
  217. },
  218. camera: Camera{ViewPort: f64.Vec2{screenWidth, screenHeight}},
  219. }
  220. g.world = ebiten.NewImage(worldWidth, worldHeight)
  221. ebiten.SetWindowSize(screenWidth, screenHeight)
  222. ebiten.SetWindowTitle("Camera (Ebitengine Demo)")
  223. if err := ebiten.RunGame(g); err != nil {
  224. log.Fatal(err)
  225. }
  226. }