-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
626 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package assets | ||
|
||
import ( | ||
"embed" | ||
"image" | ||
_ "image/png" | ||
"io/fs" | ||
|
||
"github.com/hajimehoshi/ebiten/v2" | ||
"golang.org/x/image/font" | ||
"golang.org/x/image/font/opentype" | ||
) | ||
|
||
//go:embed * | ||
var assets embed.FS | ||
|
||
var PlayerSprite = mustLoadImage("player.png") | ||
var MeteorSprites = mustLoadImages("meteors/*.png") | ||
var LaserSprite = mustLoadImage("laser.png") | ||
var ScoreFont = mustLoadFont("font.ttf") | ||
|
||
func mustLoadImage(name string) *ebiten.Image { | ||
f, err := assets.Open(name) | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer f.Close() | ||
|
||
img, _, err := image.Decode(f) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return ebiten.NewImageFromImage(img) | ||
} | ||
|
||
func mustLoadImages(path string) []*ebiten.Image { | ||
matches, err := fs.Glob(assets, path) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
images := make([]*ebiten.Image, len(matches)) | ||
for i, match := range matches { | ||
images[i] = mustLoadImage(match) | ||
} | ||
|
||
return images | ||
} | ||
|
||
func mustLoadFont(name string) font.Face { | ||
f, err := assets.ReadFile(name) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
tt, err := opentype.Parse(f) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
face, err := opentype.NewFace(tt, &opentype.FaceOptions{ | ||
Size: 48, | ||
DPI: 72, | ||
Hinting: font.HintingVertical, | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return face | ||
} |
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package game | ||
|
||
import ( | ||
"math" | ||
|
||
"github.com/hajimehoshi/ebiten/v2" | ||
|
||
"github.com/ThreeDotsLabs/meteors/assets" | ||
) | ||
|
||
const ( | ||
bulletSpeedPerSecond = 350.0 | ||
) | ||
|
||
type Bullet struct { | ||
position Vector | ||
rotation float64 | ||
sprite *ebiten.Image | ||
} | ||
|
||
func NewBullet(pos Vector, rotation float64) *Bullet { | ||
sprite := assets.LaserSprite | ||
|
||
bounds := sprite.Bounds() | ||
halfW := float64(bounds.Dx()) / 2 | ||
halfH := float64(bounds.Dy()) / 2 | ||
|
||
pos.X -= halfW | ||
pos.Y -= halfH | ||
|
||
b := &Bullet{ | ||
position: pos, | ||
rotation: rotation, | ||
sprite: sprite, | ||
} | ||
|
||
return b | ||
} | ||
|
||
func (b *Bullet) Update() { | ||
speed := bulletSpeedPerSecond / float64(ebiten.TPS()) | ||
|
||
b.position.X += math.Sin(b.rotation) * speed | ||
b.position.Y += math.Cos(b.rotation) * -speed | ||
} | ||
|
||
func (b *Bullet) Draw(screen *ebiten.Image) { | ||
bounds := b.sprite.Bounds() | ||
halfW := float64(bounds.Dx()) / 2 | ||
halfH := float64(bounds.Dy()) / 2 | ||
|
||
op := &ebiten.DrawImageOptions{} | ||
op.GeoM.Translate(-halfW, -halfH) | ||
op.GeoM.Rotate(b.rotation) | ||
op.GeoM.Translate(halfW, halfH) | ||
|
||
op.GeoM.Translate(b.position.X, b.position.Y) | ||
|
||
screen.DrawImage(b.sprite, op) | ||
} | ||
|
||
func (b *Bullet) Collider() Rect { | ||
bounds := b.sprite.Bounds() | ||
|
||
return NewRect( | ||
b.position.X, | ||
b.position.Y, | ||
float64(bounds.Dx()), | ||
float64(bounds.Dy()), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package game | ||
|
||
import ( | ||
"fmt" | ||
"image/color" | ||
"time" | ||
|
||
"github.com/hajimehoshi/ebiten/v2" | ||
"github.com/hajimehoshi/ebiten/v2/text" | ||
|
||
"github.com/ThreeDotsLabs/meteors/assets" | ||
) | ||
|
||
const ( | ||
screenWidth = 800 | ||
screenHeight = 600 | ||
|
||
meteorSpawnTime = 1 * time.Second | ||
|
||
baseMeteorVelocity = 0.25 | ||
meteorSpeedUpAmount = 0.1 | ||
meteorSpeedUpTime = 5 * time.Second | ||
) | ||
|
||
type Game struct { | ||
player *Player | ||
meteorSpawnTimer *Timer | ||
meteors []*Meteor | ||
bullets []*Bullet | ||
|
||
score int | ||
|
||
baseVelocity float64 | ||
velocityTimer *Timer | ||
} | ||
|
||
func NewGame() *Game { | ||
g := &Game{ | ||
meteorSpawnTimer: NewTimer(meteorSpawnTime), | ||
baseVelocity: baseMeteorVelocity, | ||
velocityTimer: NewTimer(meteorSpeedUpTime), | ||
} | ||
|
||
g.player = NewPlayer(g) | ||
|
||
return g | ||
} | ||
|
||
func (g *Game) Update() error { | ||
g.velocityTimer.Update() | ||
if g.velocityTimer.IsReady() { | ||
g.velocityTimer.Reset() | ||
g.baseVelocity += meteorSpeedUpAmount | ||
} | ||
|
||
g.player.Update() | ||
|
||
g.meteorSpawnTimer.Update() | ||
if g.meteorSpawnTimer.IsReady() { | ||
g.meteorSpawnTimer.Reset() | ||
|
||
m := NewMeteor(g.baseVelocity) | ||
g.meteors = append(g.meteors, m) | ||
} | ||
|
||
for _, m := range g.meteors { | ||
m.Update() | ||
} | ||
|
||
for _, b := range g.bullets { | ||
b.Update() | ||
} | ||
|
||
// Check for meteor/bullet collisions | ||
for i, m := range g.meteors { | ||
for j, b := range g.bullets { | ||
if m.Collider().Intersects(b.Collider()) { | ||
g.meteors = append(g.meteors[:i], g.meteors[i+1:]...) | ||
g.bullets = append(g.bullets[:j], g.bullets[j+1:]...) | ||
g.score++ | ||
} | ||
} | ||
} | ||
|
||
// Check for meteor/player collisions | ||
for _, m := range g.meteors { | ||
if m.Collider().Intersects(g.player.Collider()) { | ||
g.Reset() | ||
break | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (g *Game) Draw(screen *ebiten.Image) { | ||
g.player.Draw(screen) | ||
|
||
for _, m := range g.meteors { | ||
m.Draw(screen) | ||
} | ||
|
||
for _, b := range g.bullets { | ||
b.Draw(screen) | ||
} | ||
|
||
text.Draw(screen, fmt.Sprintf("%06d", g.score), assets.ScoreFont, screenWidth/2-100, 50, color.White) | ||
} | ||
|
||
func (g *Game) AddBullet(b *Bullet) { | ||
g.bullets = append(g.bullets, b) | ||
} | ||
|
||
func (g *Game) Reset() { | ||
g.player = NewPlayer(g) | ||
g.meteors = nil | ||
g.bullets = nil | ||
g.score = 0 | ||
g.meteorSpawnTimer.Reset() | ||
g.baseVelocity = baseMeteorVelocity | ||
g.velocityTimer.Reset() | ||
} | ||
|
||
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { | ||
return screenWidth, screenHeight | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package game | ||
|
||
import ( | ||
"math" | ||
"math/rand" | ||
|
||
"github.com/hajimehoshi/ebiten/v2" | ||
|
||
"github.com/ThreeDotsLabs/meteors/assets" | ||
) | ||
|
||
const ( | ||
rotationSpeedMin = -0.02 | ||
rotationSpeedMax = 0.02 | ||
) | ||
|
||
type Meteor struct { | ||
position Vector | ||
rotation float64 | ||
movement Vector | ||
rotationSpeed float64 | ||
sprite *ebiten.Image | ||
} | ||
|
||
func NewMeteor(baseVelocity float64) *Meteor { | ||
target := Vector{ | ||
X: screenWidth / 2, | ||
Y: screenHeight / 2, | ||
} | ||
|
||
angle := rand.Float64() * 2 * math.Pi | ||
r := screenWidth / 2.0 | ||
|
||
pos := Vector{ | ||
X: target.X + math.Cos(angle)*r, | ||
Y: target.Y + math.Sin(angle)*r, | ||
} | ||
|
||
velocity := baseVelocity + rand.Float64()*1.5 | ||
|
||
direction := Vector{ | ||
X: target.X - pos.X, | ||
Y: target.Y - pos.Y, | ||
} | ||
normalizedDirection := direction.Normalize() | ||
|
||
movement := Vector{ | ||
X: normalizedDirection.X * velocity, | ||
Y: normalizedDirection.Y * velocity, | ||
} | ||
|
||
sprite := assets.MeteorSprites[rand.Intn(len(assets.MeteorSprites))] | ||
|
||
m := &Meteor{ | ||
position: pos, | ||
movement: movement, | ||
rotationSpeed: rotationSpeedMin + rand.Float64()*(rotationSpeedMax-rotationSpeedMin), | ||
sprite: sprite, | ||
} | ||
return m | ||
} | ||
|
||
func (m *Meteor) Update() { | ||
m.position.X += m.movement.X | ||
m.position.Y += m.movement.Y | ||
m.rotation += m.rotationSpeed | ||
} | ||
|
||
func (m *Meteor) Draw(screen *ebiten.Image) { | ||
bounds := m.sprite.Bounds() | ||
halfW := float64(bounds.Dx()) / 2 | ||
halfH := float64(bounds.Dy()) / 2 | ||
|
||
op := &ebiten.DrawImageOptions{} | ||
op.GeoM.Translate(-halfW, -halfH) | ||
op.GeoM.Rotate(m.rotation) | ||
op.GeoM.Translate(halfW, halfH) | ||
|
||
op.GeoM.Translate(m.position.X, m.position.Y) | ||
|
||
screen.DrawImage(m.sprite, op) | ||
} | ||
|
||
func (m *Meteor) Collider() Rect { | ||
bounds := m.sprite.Bounds() | ||
|
||
return NewRect( | ||
m.position.X, | ||
m.position.Y, | ||
float64(bounds.Dx()), | ||
float64(bounds.Dy()), | ||
) | ||
} |
Oops, something went wrong.