-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathassets.go
209 lines (174 loc) · 5.39 KB
/
assets.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package main
import (
"embed"
"image"
"io"
"log"
_ "image/png"
"github.com/hajimehoshi/ebiten/v2"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
)
// Assets is the embedded assets directory
//
//go:embed assets
var Assets embed.FS
// Background is a tiling background image
type Background struct {
Image *Image
Velocity Vector
offset Vector
}
// Update updates the background offset, moving it down depending on the player's current position
func (background *Background) Update() {
offset := background.Velocity.ScaledBy((1. / 100) / 60)
background.offset.Add(offset)
}
// Draw draws the background image to the game screen
func (background *Background) Draw(screen *ebiten.Image) {
background.Image.DrawTiled(screen, Vector{}, Vector{X: 2, Y: 2}, 0, background.offset)
}
// Font is a wrapper around the font.Face type
type Font struct {
font.Face
}
// LoadFont loads a font from the assets directory
func LoadFont(path string, op opentype.FaceOptions) *Font {
data, err := Assets.ReadFile("assets/" + path)
if err != nil {
log.Fatal(err)
}
tt, err := opentype.Parse(data)
if err != nil {
log.Fatal(err)
}
if op.Size == 0 {
op = opentype.FaceOptions{
Size: 12,
DPI: 72,
Hinting: font.HintingFull,
}
}
fnt, err := opentype.NewFace(tt, &opentype.FaceOptions{
Size: 12,
DPI: 72,
Hinting: font.HintingFull,
})
if err != nil {
log.Fatal(err)
}
return &Font{fnt}
}
var (
// OriginTop represents the top of an image, using relative coordinates
OriginTop = Vector{X: 0.5, Y: 0}
// OriginTopLeft represents the top left of an image, using relative coordinates
OriginTopLeft = Vector{X: 0, Y: 0}
// OriginTopRight represents the top right of an image, using relative coordinates
OriginTopRight = Vector{X: 1, Y: 0}
// OriginCenter represents the center of an image, using relative coordinates
OriginCenter = Vector{X: 0.5, Y: 0.5}
// OriginLeft represents the center left of an image, using relative coordinates
OriginLeft = Vector{X: 0, Y: 0.5}
// OriginRight represents the center right of an image, using relative coordinates
OriginRight = Vector{X: 1, Y: 0.5}
// OriginBottom represents the bottom of an image, using relative coordinates
OriginBottom = Vector{X: 0.5, Y: 1}
// OriginBottomLeft represents the bottom left of an image, using relative coordinates
OriginBottomLeft = Vector{X: 0, Y: 1}
// OriginBottomRight represents the bottom right of an image, using relative coordinates
OriginBottomRight = Vector{X: 1, Y: 1}
)
// Image is a wrapper around the ebiten.Image type, with additional properties for drawing
type Image struct {
*ebiten.Image
Size Vector
Origin Vector
}
// LoadImage loads an image from the assets directory
func LoadImage(path string, origin Vector) *Image {
data, err := Assets.Open("assets/" + path)
if err != nil {
log.Fatal(err)
}
dataImg, _, err := image.Decode(data)
if err != nil {
log.Fatal(err)
}
image := ebiten.NewImageFromImage(dataImg)
width, height := image.Size()
return &Image{
Image: image,
Size: Vector{
X: float64(width),
Y: float64(height),
},
Origin: origin,
}
}
func translateScaleAndRotateImage(geom *ebiten.GeoM, position Vector, scale Vector, rotation float64) {
geom.Translate(position.X, position.Y)
geom.Scale(float64(scale.X), float64(scale.Y))
geom.Rotate(rotation)
}
// Draw draws the image onto a target
func (image *Image) Draw(target *ebiten.Image, position Vector, scale Vector, rotation float64) {
op := &ebiten.DrawImageOptions{}
position.Subtract(image.Origin.Dot(image.Size))
translateScaleAndRotateImage(&op.GeoM, position, scale, rotation)
target.DrawImage(image.Image, op)
}
var tilingShader = LoadShader("shaders/tile.go")
// DrawTiled draws the image onto a target using a tiling shader, offset represents the offset of the tiling, not the position of the image
func (image *Image) DrawTiled(target *ebiten.Image, position Vector, scale Vector, rotation float64, offset Vector) {
images := []*Image{image}
uniforms := map[string]any{
"Offset": []float32{float32(offset.X), float32(offset.Y)},
}
tilingShader.Draw(target, position, scale, rotation, images, uniforms)
}
// Shader is a wrapper around the ebiten.Shader type
type Shader struct {
*ebiten.Shader
}
// LoadShader loads a shader from the assets directory
func LoadShader(path string) *Shader {
file, err := Assets.Open("assets/" + path)
if err != nil {
log.Fatal(err)
}
data, err := io.ReadAll(file)
if err != nil {
log.Fatal(err)
}
shader, err := ebiten.NewShader(data)
if err != nil {
log.Fatal(err)
}
return &Shader{
Shader: shader,
}
}
// Draw draws the shader onto a target
func (shader *Shader) Draw(target *ebiten.Image, position Vector, scale Vector, rotation float64, images []*Image, uniforms map[string]any) {
op := &ebiten.DrawRectShaderOptions{}
width := target.Bounds().Dx()
height := target.Bounds().Dy()
// If there are images, use the size of the first image.
// All images must be the same size.
if images != nil {
width, height = images[0].Image.Size()
}
op.Uniforms = map[string]any{
"Time": float32(TimeSinceStart().Seconds()),
"Resolution": []float32{float32(width), float32(height)},
}
for i, image := range images {
op.Images[i] = image.Image
}
for key, value := range uniforms {
op.Uniforms[key] = value
}
translateScaleAndRotateImage(&op.GeoM, position, scale, rotation)
target.DrawRectShader(width, height, shader.Shader, op)
}