package game import ( "fluids/elements" "fluids/gamedata" "fmt" "image/color" "math" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/inpututil" ) const ( GameWidth = 640 GameHeight = 360 GameFSDW = 200 GameFSDH = 100 GameSims = 3 //number of total sims ) type Game struct { //control data cycle int paused bool fluidsimidx int //key elements fluidsimd *elements.FluidSimD fluidsim10 []*elements.FluidSim10 alertbox *elements.Alert flask *elements.RoundedBottomFlask //cache elements fluidsim10width float64 fluidsim10height float64 //other fluidsim10angle float64 //purely for debugging mdown bool mdx, mdy int mdangle float64 //angle at mousedown mfangle float64 //last flask angle //colors flaskcolor color.RGBA } func NewGame() *Game { g := &Game{ paused: false, fluidsimidx: 0, alertbox: elements.NewAlert(), fluidsimd: elements.NewFluidSimD(), flask: elements.NewRoundedBottomFlask(), //fluidsim10: elements.NewFluidSim10(gamedata.Vector{X: GameFSDW, Y: GameFSDH}), //fluidsimgpt: elements.NewFlipFluidEntity(640, 480, 2, 1, 100), //fluidsim10: elements.NewFluidSim10(), } g.Initialize() return g } func (g *Game) Update() error { g.ParseInputs() if !g.paused { switch g.fluidsimidx { case 0: g.fluidsimd.Update() case 1: //g.fluidsimgpt.Update() g.UpdateFluidsim10() case 2: g.UpdateFlask() default: break } } g.cycle++ return nil } func (g *Game) UpdateFluidsim10() { for _, sim := range g.fluidsim10 { sim.Update() } } func (g *Game) Draw(screen *ebiten.Image) { screen.Clear() switch g.fluidsimidx { case 0: g.RenderFluidSimD(screen) case 1: g.RenderFluidSim10(screen) case 2: g.RenderFlask(screen) default: break } if g.paused { op := &ebiten.DrawImageOptions{} op.GeoM.Translate(50, 50) screen.DrawImage(g.alertbox.Sprite, op) } } func (g *Game) RenderFluidSimD(img *ebiten.Image) { g.fluidsimd.Draw() pos := g.fluidsimd.GetPosition() op := &ebiten.DrawImageOptions{} op.GeoM.Translate(pos.X, pos.Y) img.DrawImage(g.fluidsimd.GetSprite(), op) } func (g *Game) RenderFluidSim10(img *ebiten.Image) { for _, sim := range g.fluidsim10 { sim.Draw() angle := sim.GetAngle() pos := sim.GetPosition() op := &ebiten.DrawImageOptions{} op.GeoM.Translate(-g.fluidsim10width/2, -g.fluidsim10height/2) op.GeoM.Scale(1, -1) op.GeoM.Rotate(angle) // op.GeoM.Translate(g.fluidsim10width/2, g.fluidsim10height/2) op.GeoM.Translate(pos.X, pos.Y) img.DrawImage(sim.GetSprite(), op) } //debug info x, y := ebiten.CursorPosition() deg := g.fluidsim10angle * 180 / math.Pi str := fmt.Sprintf("Mouse (x: %d, y: %d) Origin (x: %d, y: %d) Angle (%f)", x, y, GameWidth/2, GameHeight/2, deg) ebitenutil.DebugPrint(img, str) } func (g *Game) Layout(x, y int) (int, int) { return GameWidth, GameHeight } func (g *Game) ParseInputs() { //simulation specific updates switch g.fluidsimidx { case 0: g.ManageFluidSimDInputs() case 1: g.ManageFluidSim10Inputs() case 2: g.ManageFlaskInputs() default: break } //common updates if inpututil.IsKeyJustPressed(ebiten.KeyP) { g.paused = !g.paused g.alertbox.SetText("PAUSED") g.alertbox.Draw() } //swap fluid simulations if inpututil.IsKeyJustPressed(ebiten.KeyPageUp) { g.fluidsimidx = (g.fluidsimidx + 1) % GameSims } if inpututil.IsKeyJustPressed(ebiten.KeyPageDown) { g.fluidsimidx = g.fluidsimidx - 1 if g.fluidsimidx < 0 { g.fluidsimidx = GameSims - 1 } } } func (g *Game) ManageFluidSimDInputs() { //refresh particles if inpututil.IsKeyJustPressed(ebiten.KeyR) { g.fluidsimd.InitializeParticles() } //pause simulation if inpututil.IsKeyJustPressed(ebiten.KeyP) { g.fluidsimd.SetPaused(!g.fluidsimd.Paused()) } //show quadtree quadrants if inpututil.IsKeyJustPressed(ebiten.KeyQ) { g.fluidsimd.SetRenderQuads(!g.fluidsimd.RenderQuads()) } //enable collision resolution if inpututil.IsKeyJustPressed(ebiten.KeyC) { g.fluidsimd.SetResolveCollisions(!g.fluidsimd.ResolveCollisions()) } //switch between collision resolvers if inpututil.IsKeyJustPressed(ebiten.KeyLeft) { g.fluidsimd.PreviousSolver() } if inpututil.IsKeyJustPressed(ebiten.KeyRight) { g.fluidsimd.NextSolver() } } func (g *Game) ManageFluidSim10Inputs() { //refresh particles if inpututil.IsKeyJustPressed(ebiten.KeyR) { for _, sim := range g.fluidsim10 { sim.Initialize() g.fluidsim10angle = 0 } } if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { g.mdown = true g.mdx, g.mdy = ebiten.CursorPosition() } if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if g.mdown { mx, my := ebiten.CursorPosition() for _, sim := range g.fluidsim10 { dx := float64(mx) - sim.GetPosition().X dy := float64(my) - sim.GetPosition().Y angle := math.Atan2(dy, dx) g.fluidsim10angle = angle sim.SetAngle(angle) } } } if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) { g.mdown = false } if inpututil.IsKeyJustPressed(ebiten.KeyB) { for _, sim := range g.fluidsim10 { sim.ToggleShape() } } if inpututil.IsKeyJustPressed(ebiten.KeyV) { for _, sim := range g.fluidsim10 { sim.ToggleParticles() } } } func (g *Game) Initialize() { //10MP Fluid Simulation Initialization g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10()) g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10()) g.fluidsim10width = float64(g.fluidsim10[0].GetSprite().Bounds().Dx()) g.fluidsim10height = float64(g.fluidsim10[0].GetSprite().Bounds().Dy()) x0 := float64(GameWidth / (len(g.fluidsim10) + 1)) for i, sim := range g.fluidsim10 { pos := gamedata.Vector{ X: x0 * float64(i+1), Y: GameHeight / 2., } sim.SetPosition(pos) } //Flask Initialization pos := gamedata.Vector{ X: GameWidth / 2, Y: GameHeight / 2, } g.flask.SetPosition(pos) g.flaskcolor = color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff} g.flask.SetFluidColor(g.flaskcolor) } func (g *Game) ManageFlaskInputs() { if inpututil.IsKeyJustPressed(ebiten.KeyR) { g.flask.Initialize() g.mfangle = 0 g.flask.SetAngle(g.mfangle) g.flask.SetFluidColor(g.flaskcolor) } if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { //angle at mouse down is the zero angle g.mdown = true g.mdx, g.mdy = ebiten.CursorPosition() dx := float64(g.mdx) - g.flask.GetPosition().X dy := float64(g.mdy) - g.flask.GetPosition().Y g.mdangle = math.Atan2(dy, dx) } if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if g.mdown { mx, my := ebiten.CursorPosition() dx := float64(mx) - g.flask.GetPosition().X dy := float64(my) - g.flask.GetPosition().Y angle := math.Atan2(dy, dx) g.flask.SetAngle(g.mfangle + (angle - g.mdangle)) } } if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) { g.mdown = false mx, my := ebiten.CursorPosition() dx := float64(mx) - g.flask.GetPosition().X dy := float64(my) - g.flask.GetPosition().Y angle := math.Atan2(dy, dx) g.mfangle = g.mfangle + (angle - g.mdangle) } } func (g *Game) UpdateFlask() { g.flask.Update() } func (g *Game) RenderFlask(img *ebiten.Image) { g.flask.Draw() angle := g.flask.GetAngle() dim := g.flask.GetDimensions() // //TODO: use flask position for rendering, not screen pos := g.flask.GetPosition() op := &ebiten.DrawImageOptions{} op.GeoM.Translate(-dim.X/2, -dim.Y/2) op.GeoM.Rotate(angle) op.GeoM.Scale(2, 2) op.GeoM.Translate(pos.X, pos.Y) img.DrawImage(g.flask.GetSprite(), op) }