Skip to content

Commit

Permalink
major refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
jcgraybill committed Apr 10, 2022
1 parent 7613dae commit 38ef0b8
Show file tree
Hide file tree
Showing 21 changed files with 462 additions and 408 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ Built in [golang](https://go.dev/) using the [ebiten](https://ebiten.org/) 2D ga
```
git clone [email protected]:jcgraybill/it-costs-money.git
go mod tidy
go build -tags "ebitensinglethread deploy"
go build
```
49 changes: 49 additions & 0 deletions coin/coin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// TODO coin should be contained within level, since it's an actor

package coin

import (
"bytes"
"fmt"

"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/audio"
"github.com/hajimehoshi/ebiten/v2/audio/wav"
"github.com/jcgraybill/it-costs-money/util"
)

type Coin struct {
Slides []*ebiten.Image
NumSlides int
AnimationSpeed int
audioContext *audio.Context
AudioPlayers [5]*audio.Player
sampleRate int
}

func New() Coin {
var c Coin
coinSprites := util.LoadSpriteSheet("assets/coin.png")
c.Slides = coinSprites[1:7]
c.NumSlides = 6
c.AnimationSpeed = 10
c.sampleRate = 48000
c.audioContext = audio.NewContext(c.sampleRate)

for i := 0; i < 5; i++ {
audioBytes, err := util.GameData(fmt.Sprintf("assets/Coins_Grab_0%d.wav", i))
if err != nil {
panic(err)
}
d, err := wav.Decode(c.audioContext, bytes.NewReader(audioBytes))
if err != nil {
panic(err)
}
c.AudioPlayers[i], err = c.audioContext.NewPlayer(d)
if err != nil {
panic(err)
}

}
return c
}
6 changes: 3 additions & 3 deletions deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

package main

func levelEditor() {
return
}
func levelEditor(g *Game) string {
return nil
}
59 changes: 38 additions & 21 deletions drawscreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,62 @@ import (

"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/jcgraybill/it-costs-money/util"
)

func (g *Game) Draw(screen *ebiten.Image) {

// TODO objects should draw themselves - add a draw.go to coin, level, player
// Each returns an appropriate image

frameBuffer.Clear()
parallax(level.bgImage1, -player.x, 4)
parallax(level.bgImage2, -player.x, 3)
parallax(level.bgImage3, -player.x, 2)
parallax(g.level.BgImage1, -g.player.X, 4)
parallax(g.level.BgImage2, -g.player.X, 3)
parallax(g.level.BgImage3, -g.player.X, 2)

viewPortOffset := player.x - (screenWidth/2 + frameWidth/2)
levelViewFinder := image.Rect(viewPortOffset, 0, viewPortOffset+screenWidth, screenHeight)
frameBuffer.DrawImage(level.levelBackgroundImage.SubImage(levelViewFinder).(*ebiten.Image), nil)
frameBuffer.DrawImage(level.levelImage.SubImage(levelViewFinder).(*ebiten.Image), nil)
viewPortOffset := g.player.X - (util.ScreenWidth/2 + util.FrameWidth/2)
levelViewFinder := image.Rect(viewPortOffset, 0, viewPortOffset+util.ScreenWidth, util.ScreenHeight)
frameBuffer.DrawImage(g.level.LevelBackgroundImage.SubImage(levelViewFinder).(*ebiten.Image), nil)
frameBuffer.DrawImage(g.level.LevelImage.SubImage(levelViewFinder).(*ebiten.Image), nil)

runnerOP := &ebiten.DrawImageOptions{}

if player.facingLeft {
if g.player.FacingLeft {
runnerOP.GeoM.Scale(-1, 1)
runnerOP.GeoM.Translate(frameWidth, 0)
runnerOP.GeoM.Translate(util.FrameWidth, 0)
}
runnerOP.GeoM.Translate(-float64(frameWidth)/2, 0)
runnerOP.GeoM.Translate(screenWidth/2, float64(player.y))
i := (g.count / 5) % len(*player.slides)

for _, actor := range actors {
if actor.exists {
if actor.kind == "c" {
if actor.x > viewPortOffset || actor.x < viewPortOffset+screenWidth/2-frameWidth {
runnerOP.GeoM.Translate(-float64(util.FrameWidth)/2, 0)
runnerOP.GeoM.Translate(util.ScreenWidth/2, float64(g.player.Y))
i := (g.count / 5) % len(*g.player.Slides)

for _, actor := range g.level.Actors {
if actor.Exists {
if actor.Kind == "c" {
if actor.X > viewPortOffset || actor.X < viewPortOffset+util.ScreenWidth/2-util.FrameWidth {
coinOp := &ebiten.DrawImageOptions{}
coinOp.GeoM.Translate(-float64(viewPortOffset-actor.x), float64(actor.y))
frameBuffer.DrawImage(coin.slides[(g.count/coin.animationSpeed)%coin.numSlides], coinOp)
coinOp.GeoM.Translate(-float64(viewPortOffset-actor.X), float64(actor.Y))
frameBuffer.DrawImage(g.coin.Slides[(g.count/g.coin.AnimationSpeed)%g.coin.NumSlides], coinOp)
}
}

}
}

frameBuffer.DrawImage((*player.slides)[i], runnerOP)
frameBuffer.DrawImage(level.levelForegroundImage.SubImage(levelViewFinder).(*ebiten.Image), nil)
frameBuffer.DrawImage((*g.player.Slides)[i], runnerOP)
frameBuffer.DrawImage(g.level.LevelForegroundImage.SubImage(levelViewFinder).(*ebiten.Image), nil)
ebitenutil.DebugPrint(frameBuffer, message)
screen.DrawImage(frameBuffer, nil)
}

func parallax(image *ebiten.Image, offset int, speed int) {
panelWidth := image.Bounds().Dx()
position := (offset / speed) % panelWidth
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(position), 0)
frameBuffer.DrawImage(image, op)

for i := 0; position+i*panelWidth+panelWidth < util.ScreenWidth; i++ {
op.GeoM.Translate(float64(panelWidth), 0)
frameBuffer.DrawImage(image, op)
}
}
119 changes: 63 additions & 56 deletions gameloop.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,148 +4,155 @@ import (
"fmt"

"github.com/hajimehoshi/ebiten/v2"
_ "github.com/jcgraybill/it-costs-money/player"
"github.com/jcgraybill/it-costs-money/util"
)

func (g *Game) Update() error {
g.count++

player.slides = &player.idleFrames
g.player.Slides = &g.player.IdleFrames

//TODO if player is mid-jump, will get stuck hovering in the air
if player.x > level.levelImage.Bounds().Dx()-screenWidth/2+frameWidth/2-1 {
message = fmt.Sprintf("Congratulations! You collected %d coins.\nPlease place them in the dumpster.", player.coins)
if g.player.X > g.level.LevelImage.Bounds().Dx()-util.ScreenWidth/2+util.FrameWidth/2-1 {
message = fmt.Sprintf("Congratulations! You collected %d coins.\nPlease place them in the dumpster.", g.player.Coins)
return nil
}

if g.count%level.coinDecay == 0 && player.coins > 0 {
player.coins--
if g.count%g.level.CoinDecay == 0 && g.player.Coins > 0 {
g.player.Coins--
}

// Detect collisions
touchingGround := false
if _, _, _, a := level.levelImage.At(player.x-frameWidth/2, player.y+frameHeight).RGBA(); a > 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X-util.FrameWidth/2, g.player.Y+util.FrameHeight).RGBA(); a > 0 {
touchingGround = true
}

touchingLeft := false
if _, _, _, a := level.levelImage.At(player.x-frameWidth, player.y+frameHeight/2).RGBA(); a > 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X-util.FrameWidth, g.player.Y+util.FrameHeight/2).RGBA(); a > 0 {
touchingLeft = true
}

leftAdjacent := false
if _, _, _, a := level.levelImage.At(player.x-frameWidth-moveSpeed, player.y+frameHeight/2).RGBA(); a > 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X-util.FrameWidth-g.level.MoveSpeed, g.player.Y+util.FrameHeight/2).RGBA(); a > 0 {
leftAdjacent = true
}

touchingRight := false
if _, _, _, a := level.levelImage.At(player.x, player.y+frameHeight/2).RGBA(); a > 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X, g.player.Y+util.FrameHeight/2).RGBA(); a > 0 {
touchingRight = true
}

rightAdjacent := false
if _, _, _, a := level.levelImage.At(player.x+moveSpeed, player.y+frameHeight/2).RGBA(); a > 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X+g.level.MoveSpeed, g.player.Y+util.FrameHeight/2).RGBA(); a > 0 {
rightAdjacent = true
}

// Jumping

if touchingGround {
player.yVelocity = 0
player.wileECoytoe = wileECoyoteFrames
g.player.YVelocity = 0

g.player.ResetWileECoyote()

if ebiten.IsKeyPressed(ebiten.KeySpace) || ebiten.IsKeyPressed(ebiten.KeyArrowUp) {
if player.timeSinceLastJump+jumpRecovery < g.count {
if _, _, _, a := level.levelImage.At(player.x-frameWidth/2, player.y-jumpHeight).RGBA(); a == 0 {
player.yVelocity = -jumpHeight
player.y += int(player.yVelocity)
player.timeSinceLastJump = g.count
player.wileECoytoe = 0
if g.player.TimeSinceLastJump+g.player.JumpRecovery < g.count {
if _, _, _, a := g.level.LevelImage.At(g.player.X-util.FrameWidth/2, g.player.Y-g.level.JumpHeight).RGBA(); a == 0 {
g.player.YVelocity = -float64(g.level.JumpHeight)
g.player.Y += int(g.player.YVelocity)
g.player.TimeSinceLastJump = g.count
g.player.WileECoyote = 0
}
}
}
} else {
player.y += int(player.yVelocity)
player.yVelocity += gravity
if player.wileECoytoe == 0 {
player.slides = &player.fallFrames
g.player.Y += int(g.player.YVelocity)
g.player.YVelocity += g.level.Gravity
if g.player.WileECoyote == 0 {
g.player.Slides = &g.player.FallFrames
} else {
player.wileECoytoe -= 1
g.player.WileECoyote -= 1
}
}

touchingTop := false
if _, _, _, a := level.levelImage.At(player.x-frameWidth/2, player.y).RGBA(); a > 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X-util.FrameWidth/2, g.player.Y).RGBA(); a > 0 {
touchingTop = true
}

// Running
// TODO allow WASD
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) {
player.facingLeft = false
if player.x < level.levelImage.Bounds().Dx()-screenWidth/2+frameWidth/2-1 && !touchingRight && !rightAdjacent {
player.slides = &player.runFrames
player.x += moveSpeed
g.player.FacingLeft = false
if g.player.X < g.level.LevelImage.Bounds().Dx()-util.ScreenWidth/2+util.FrameWidth/2-1 && !touchingRight && !rightAdjacent {
g.player.Slides = &g.player.RunFrames
g.player.X += g.level.MoveSpeed
}
}

if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) {
player.facingLeft = true
if player.x > screenWidth/2+frameWidth/2 && !touchingLeft && !leftAdjacent {
player.slides = &player.runFrames
player.x -= moveSpeed
g.player.FacingLeft = true
if g.player.X > util.ScreenWidth/2+util.FrameWidth/2 && !touchingLeft && !leftAdjacent {
g.player.Slides = &g.player.RunFrames
g.player.X -= g.level.MoveSpeed
}
}

// Correct if overlapping with terrain

if touchingTop && !touchingGround {
player.y += 1
player.yVelocity = 0
g.player.Y += 1
g.player.YVelocity = 0
}

if _, _, _, a := level.levelImage.At(player.x-frameWidth/2, player.y+frameHeight-1).RGBA(); a != 0 {
if _, _, _, a := g.level.LevelImage.At(g.player.X-util.FrameWidth/2, g.player.Y+util.FrameHeight-1).RGBA(); a != 0 {
for a != 0 {
player.y -= 1
_, _, _, a = level.levelImage.At(player.x-frameWidth/2, player.y+frameHeight-1).RGBA()
g.player.Y -= 1
_, _, _, a = g.level.LevelImage.At(g.player.X-util.FrameWidth/2, g.player.Y+util.FrameHeight-1).RGBA()
}
}

if touchingLeft && !touchingRight {
player.x += moveSpeed
g.player.X += g.level.MoveSpeed
}

if touchingRight && !touchingLeft {
player.x -= moveSpeed
g.player.X -= g.level.MoveSpeed
}

// Pick up coins
for _, actor := range actors {
if actor.exists && actor.kind == "c" && actor.x+frameWidth/2 > player.x-frameWidth && actor.x+frameWidth/2 < player.x && actor.y+frameHeight/2 > player.y && actor.y+frameHeight/2 < player.y+frameHeight {
actor.exists = false
player.coins++
coin.audioPlayers[g.count%5].Rewind()
coin.audioPlayers[g.count%5].Play()
for _, actor := range g.level.Actors {
if actor.Exists && actor.Kind == "c" && actor.X+util.FrameWidth/2 > g.player.X-util.FrameWidth && actor.X+util.FrameWidth/2 < g.player.X && actor.Y+util.FrameHeight/2 > g.player.Y && actor.Y+util.FrameHeight/2 < g.player.Y+util.FrameHeight {
actor.Exists = false
g.player.Coins++
// TODO noticeable framerate drop when sounds play
g.coin.AudioPlayers[g.count%5].Rewind()
g.coin.AudioPlayers[g.count%5].Play()
}
}

// Fall in holes
if player.y > screenHeight*4 {
// TODO make player lose money when falling in a hole
if g.player.Y > util.ScreenHeight*4 {
spawnX, spawnY := 0, 0
for _, actor := range actors {
if actor.kind == "s" {
if actor.x > spawnX && actor.x < player.x {
spawnX = actor.x + frameWidth
spawnY = actor.y
for _, actor := range g.level.Actors {
if actor.Kind == "s" {
if actor.X > spawnX && actor.X < g.player.X {
spawnX = actor.X + util.FrameWidth
spawnY = actor.Y
}

}
}
player.x = spawnX
player.y = spawnY
player.yVelocity = 0
g.player.X = spawnX
g.player.Y = spawnY
g.player.YVelocity = 0
}

message = fmt.Sprintf("Gather coins and bring them to the green chest.\nIt costs money to be alive!\nYour coins: %d", player.coins)
message = fmt.Sprintf("Gather coins and bring them to the green chest.\nIt costs money to be alive!\nYour coins: %d", g.player.Coins)

levelEditor()
message = message + levelEditor(g)

return nil
}
Loading

0 comments on commit 38ef0b8

Please sign in to comment.