diff --git a/Makefile b/Makefile index 714f7a9..812731f 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ install: .PHONY: build build: test - @go build -a -ldflags '-extldflags "-static"' ./... + @go build -buildmode=pie -a -ldflags '-extldflags "-static"' ./... test: @go test -count=1 ./... diff --git a/README.md b/README.md index 5a86f00..18ff151 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -# game-builder -Game-builder is a game builder lite using [SDL2 veandco](https://github.com/veandco/go-sdl2). +# Go-ui + +go-ui is a game builder lite using [SDL2 veandco](https://github.com/veandco/go-sdl2). It's not convention to compose a game, just proof of concept today. The wrapper is implemented with goroutines. @@ -12,35 +13,39 @@ I would be pleased to talk about that with you on mail: `mohl.clauzade@gmail.com` # Requirements + See official github to SDL2 binding for Go by [veandco](https://github.com/veandco/go-sdl2). # Installation + To install Golang see [getting started](https://golang.org/doc/install) To get SDL2 wrapper see [veandco](https://github.com/veandco/go-sdl2) `go get -v github.com/veandco/go-sdl2/{sdl,mix,img,ttf}` -To get Game-Builder: +To get go-ui: -`go get -v github.com/ymohl-cl/game-builder` +`go get -v github.com/ymohl-cl/go-ui` #### OSX + Install SDL2: `brew install sdl2 sdl2_gfx sdl2_image sdl2_mixer sdl2_net sdl2_ttf` # Example + You can view implementation example on this project [gomoku game](https://github.com/ymohl-cl/gomoku) Please, read godoc to know the specifications -``` +``` GOLANG package main import ( - "github.com/ymohl-cl/game-builder/drivers" - "github.com/ymohl-cl/game-builder/scripter" + "github.com/ymohl-cl/go-ui/drivers" + "github.com/ymohl-cl/go-ui/scripter" "github.com/ymohl-cl/gomoku/database" "github.com/ymohl-cl/gomoku/scenes/gomoku" "github.com/ymohl-cl/gomoku/scenes/loader" @@ -60,7 +65,7 @@ func main() { var d drivers.VSDL var data *database.Data - // init drivers sdl from game-builder + // init drivers sdl from go-ui if d, err = drivers.Init(windowWidth, windowHeight, "Title of my windows"); err != nil { panic(err) } @@ -71,7 +76,7 @@ func main() { panic(err) } - // get new scripter application from game-builder + // get new scripter application from go-ui s := scripter.New() // get loader scene from my app @@ -79,7 +84,7 @@ func main() { if loaderScene, err = loader.New(nil, d.GetRenderer()); err != nil { panic(err) } - // add scene on the scripter (game-builder) + // add scene on the scripter (go-ui) if err = s.AddLoader(loaderScene); err != nil { panic(err) } @@ -89,7 +94,7 @@ func main() { if menuScene, err = menu.New(data, d.GetRenderer()); err != nil { panic(err) } - // add scene on the scripter (game-builder) + // add scene on the scripter (go-ui) if err = s.AddScene(menuScene, indexMenu, true); err != nil { panic(err) } @@ -99,29 +104,35 @@ func main() { if gameScene, err = gomoku.New(data, d.GetRenderer()); err != nil { panic(err) } - // add scene on the scripter (game-builder) + // add scene on the scripter (go-ui) if err = s.AddScene(gameScene, indexGomoku, false); err != nil { panic(err) } - // run application from game-builder + // run application from go-ui s.Run(d) } ``` # FAQ + #### Why shaders aren't implemented ? -Game-builder is a proof of concept for the moment. This lib provide that which are needest to make a simple project. + +go-ui is a proof of concept for the moment. This lib provide that which are needest to make a simple project. If you need shaders, please contact us. #### How do I contribute ? + Contact me by mail: `mohl.clauzade@gmail.com` # Acknowledgment + Thanks at [veandco](https://github.com/veandco/go-sdl2) for their work. # License -game-builder is BSD 3-clause licensed. + +go-ui is BSD 3-clause licensed. # Version -V-0.1.1: implement library game-builder + +V-0.1.1: implement library go-ui diff --git a/Ressources/stocky.ttf b/Ressources/stocky.ttf deleted file mode 100755 index c33a1fa..0000000 Binary files a/Ressources/stocky.ttf and /dev/null differ diff --git a/audio/audio.go b/audio/audio.go index 66d7908..7f5ce63 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -6,7 +6,7 @@ import ( "github.com/veandco/go-sdl2/mix" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Audio structure diff --git a/config.go b/config.go new file mode 100644 index 0000000..2f87a5a --- /dev/null +++ b/config.go @@ -0,0 +1,13 @@ +package gamebuilder + +// Window configuration +type Window struct { + Title string + Width int32 + Height int32 +} + +// ConfigUI about ui +type ConfigUI struct { + Window Window +} diff --git a/drivers/drivers.go b/drivers/drivers.go deleted file mode 100644 index 6b9e121..0000000 --- a/drivers/drivers.go +++ /dev/null @@ -1,77 +0,0 @@ -package drivers - -import ( - "github.com/veandco/go-sdl2/mix" - "github.com/veandco/go-sdl2/sdl" - "github.com/veandco/go-sdl2/ttf" -) - -// VSDL veandco sdl drivers -type VSDL struct { - window *sdl.Window - renderer *sdl.Renderer - widthScreen int32 - heightScreen int32 -} - -// Destroy close sdl objects correctly -func (V *VSDL) Destroy() { - if V.renderer != nil { - V.renderer.Destroy() - } - - if V.window != nil { - V.window.Destroy() - } - ttf.Quit() - mix.Quit() - sdl.Quit() -} - -// Init create sdl window and the renderer objects -func Init(width, height int32, title string) (VSDL, error) { - var V VSDL - var err error - - sdl.Init(sdl.INIT_EVERYTHING) - - V.widthScreen = width - V.heightScreen = height - - V.window, err = sdl.CreateWindow(title, sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, V.widthScreen, V.heightScreen, sdl.WINDOW_OPENGL) - if err != nil { - return V, err - } - - V.renderer, err = sdl.CreateRenderer(V.window, -1, sdl.RENDERER_ACCELERATED) - if err != nil { - return V, err - } - - if err := mix.Init(sdl.INIT_AUDIO); err != nil { - return V, err - } - - mix.AllocateChannels(255) - if err := mix.OpenAudio(48000, mix.DEFAULT_FORMAT, mix.DEFAULT_CHANNELS, mix.DEFAULT_CHUNKSIZE); err != nil { - return V, err - } - - if err := ttf.Init(); err != nil { - return V, err - } - - V.renderer.SetDrawBlendMode(sdl.BLENDMODE_BLEND) - sdl.StopTextInput() - return V, nil -} - -// GetScreenSize : return width and height of screen -func (V VSDL) GetScreenSize() (int32, int32) { - return V.widthScreen, V.heightScreen -} - -// GetRenderer : return renderer SDL2 -func (V VSDL) GetRenderer() *sdl.Renderer { - return V.renderer -} diff --git a/error.go b/error.go new file mode 100644 index 0000000..61cf3ef --- /dev/null +++ b/error.go @@ -0,0 +1,7 @@ +package gamebuilder + +const ( + errorScriptModifier = "the script configuration can't be updated after to have start the app/game" + errorSceneNotFound = "scene not found" + errorSceneUsed = "scene is currently used" +) diff --git a/event.go b/event.go new file mode 100644 index 0000000..5bee069 --- /dev/null +++ b/event.go @@ -0,0 +1,83 @@ +package gamebuilder + +import ( + "github.com/veandco/go-sdl2/sdl" + "github.com/ymohl-cl/go-ui/objects" +) + +// event : catch mouse and keyboard events +func event(e sdl.Event, s Scene) error { + var err error + + switch e.(type) { + case *sdl.MouseMotionEvent: + err = mouseMotionEvent(e.(*sdl.MouseMotionEvent), s) + case *sdl.MouseButtonEvent: + err = mouseButtonEvent(e.(*sdl.MouseButtonEvent), s) + case *sdl.KeyboardEvent: + err = keyboardEvent(e.(*sdl.KeyboardEvent), s) + } + if err != nil { + return err + } + return nil +} + +// mouseMotionEvent define a new mouse's position +func mouseMotionEvent(mm *sdl.MouseMotionEvent, s Scene) error { + layers, m := s.GetLayers() + m.Lock() + defer m.Unlock() + + size := len(layers) + for i := size - 1; i >= 0; i-- { + layer := layers[uint8(i)] + for _, object := range layer { + if object.IsOver(mm.X, mm.Y) { + if object.GetStatus() != objects.SClick { + go object.SetStatus(objects.SOver) + } + } else { + go object.SetStatus(objects.SBasic) + } + } + } + return nil +} + +// mouseButtonEvent define a new mouse's action (click) +func mouseButtonEvent(b *sdl.MouseButtonEvent, s Scene) error { + if b.Button != sdl.BUTTON_LEFT { + return nil + } + layers, m := s.GetLayers() + m.Lock() + defer m.Unlock() + + size := len(layers) + for i := size - 1; i >= 0; i-- { + layer := layers[uint8(i)] + for _, object := range layer { + if b.State == sdl.PRESSED { + if object.GetStatus() == objects.SOver { + go object.SetStatus(objects.SClick) + break + } + } else if b.State == sdl.RELEASED { + if object.GetStatus() == objects.SClick { + go object.SetStatus(objects.SOver) + go object.Click() + break + } + } + } + } + + return nil +} + +// keyboardEvent : _ +func keyboardEvent(k *sdl.KeyboardEvent, s Scene) error { + go s.KeyboardEvent(k) + return nil +} diff --git a/gamebuilder.go b/gamebuilder.go new file mode 100644 index 0000000..4735a9e --- /dev/null +++ b/gamebuilder.go @@ -0,0 +1,165 @@ +package gamebuilder + +import ( + "sync" + + "github.com/veandco/go-sdl2/img" + "github.com/veandco/go-sdl2/mix" + "github.com/veandco/go-sdl2/sdl" + "github.com/veandco/go-sdl2/ttf" +) + +const ( + frame = 60 +) + +// GameBuilder make the relation between sdl2 driver and the application service. Provide an easy way to setup application's ui or game'ui +type GameBuilder interface { + Close() error + Script() Script + Renderer() Renderer + Run(scene string) error + loop() error +} + +type gameBuilder struct { + renderer Renderer + script Script + running bool +} + +// New configure a new gamebuilder instance and setup a sdl driver ready to use +func New(c ConfigUI) (GameBuilder, error) { + var err error + var g gameBuilder + + sdl.Init(sdl.INIT_EVERYTHING) + // renderer configuration + if g.renderer, err = NewRenderer(c); err != nil { + return nil, err + } + // audio configuration + if err = mix.Init(sdl.INIT_AUDIO); err != nil { + return nil, err + } + mix.AllocateChannels(255) + if err = mix.OpenAudio(48000, + mix.DEFAULT_FORMAT, + mix.DEFAULT_CHANNELS, + mix.DEFAULT_CHUNKSIZE); err != nil { + return nil, err + } + // font type configuration + if err = ttf.Init(); err != nil { + return nil, err + } + sdl.StopTextInput() + g.script = NewScript() + return &g, nil +} + +// Close the sdl resources +func (g *gameBuilder) Close() error { + var err error + + g.renderer.Close() + ttf.Quit() + mix.Quit() + img.Quit() + sdl.Quit() + + if err = g.script.Close(); err != nil { + return err + } + return nil +} + +// Script getter +func (g gameBuilder) Script() Script { + return g.script +} + +// Renderer getter +func (g gameBuilder) Renderer() Renderer { + return g.renderer +} + +// Run the sdl loop and start with the scene targeted by sceneIndex +func (g *gameBuilder) Run(sceneIndex string) error { + // SDL.Main allow use sdl.Do() to queue sdl instructions. + c := make(chan error, 1) + sdl.Main(func() { + if err := g.script.LoadScene(sceneIndex); err != nil { + c <- err + return + } + + err := g.loop() + c <- err + return + }) + return <-c +} + +// loop app/game to cach event and draw scene / frame +func (g *gameBuilder) loop() error { + var err error + var s Scene + var wg sync.WaitGroup + + for ok := true; ok; { + index := g.script.SceneIndex() + if s, err = g.script.Scene(index); err != nil { + return err + } + + sdl.Do(func() { + for e := sdl.PollEvent(); e != nil; e = sdl.PollEvent() { + switch e.(type) { + case *sdl.QuitEvent: + ok = false + break + default: + if err = event(e, s); err != nil { + ok = false + } + break + } + } + }) + if err != nil || !ok { + break + } + // update scene data + go s.Update() + // clear image + wg.Add(1) + sdl.Do(func() { + defer wg.Done() + if err = g.renderer.Clear(); err != nil { + ok = false + } + }) + if err != nil { + break + } + wg.Wait() + // prepare new image + g.renderer.Scene(s) + // draw new image + wg.Add(1) + sdl.Do(func() { + defer wg.Done() + g.renderer.Draw() + }) + wg.Wait() + // wait frame + sdl.Do(func() { + sdl.Delay(1000 / frame) + }) + } + if err != nil { + return err + } + return nil +} diff --git a/go.mod b/go.mod index 0c1577f..8de7440 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ -module github.com/ymohl-cl/game-builder +module github.com/ymohl-cl/go-ui go 1.15 require ( + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/veandco/go-sdl2 v0.4.5 + golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect golang.org/x/tools v0.1.0 // indirect ) diff --git a/go.sum b/go.sum index 5aadde5..8ae7291 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,13 @@ +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/veandco/go-sdl2 v0.4.5 h1:GFIjMabK7y2XWpr9sGvN7RDKHt7vrA7XPTUW60eOw+Y= github.com/veandco/go-sdl2 v0.4.5/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= diff --git a/objects/block/block.go b/objects/block/block.go index d0dc8e9..b53084d 100644 --- a/objects/block/block.go +++ b/objects/block/block.go @@ -4,7 +4,7 @@ import ( "errors" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) const ( diff --git a/objects/block/i_block.go b/objects/block/i_block.go index 3fd7c2d..4cc95eb 100644 --- a/objects/block/i_block.go +++ b/objects/block/i_block.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Init object to draw it. If error occurred, object can't be drawn diff --git a/objects/button/button.go b/objects/button/button.go index 8e1a409..a8c8a8f 100644 --- a/objects/button/button.go +++ b/objects/button/button.go @@ -2,9 +2,9 @@ package button import ( "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects/block" - "github.com/ymohl-cl/game-builder/objects/image" - "github.com/ymohl-cl/game-builder/objects/text" + "github.com/ymohl-cl/go-ui/objects/block" + "github.com/ymohl-cl/go-ui/objects/image" + "github.com/ymohl-cl/go-ui/objects/text" ) // Button object implementation diff --git a/objects/button/i_button.go b/objects/button/i_button.go index 46fc626..dd2b829 100644 --- a/objects/button/i_button.go +++ b/objects/button/i_button.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Init object to draw it. If error occurred, object can't be drawn diff --git a/objects/image/i_image.go b/objects/image/i_image.go index 8b5078a..f2f4880 100644 --- a/objects/image/i_image.go +++ b/objects/image/i_image.go @@ -6,7 +6,7 @@ import ( "github.com/veandco/go-sdl2/img" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Init object to draw it. If error occurred, object can't be drawn diff --git a/objects/image/image.go b/objects/image/image.go index 0870499..5a9620f 100644 --- a/objects/image/image.go +++ b/objects/image/image.go @@ -2,7 +2,7 @@ package image import ( "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Image object implementation diff --git a/objects/image/p_image.go b/objects/image/p_image.go index 203ce72..f16df87 100644 --- a/objects/image/p_image.go +++ b/objects/image/p_image.go @@ -1,7 +1,7 @@ package image import ( - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) /* diff --git a/objects/input/i_input.go b/objects/input/i_input.go index 70e7c1a..e71edde 100644 --- a/objects/input/i_input.go +++ b/objects/input/i_input.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Init object to draw it. If error occurred, object can't be drawn diff --git a/objects/input/input.go b/objects/input/input.go index 944aaa0..33443dd 100644 --- a/objects/input/input.go +++ b/objects/input/input.go @@ -5,9 +5,9 @@ import ( "strings" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" - "github.com/ymohl-cl/game-builder/objects/block" - "github.com/ymohl-cl/game-builder/objects/text" + "github.com/ymohl-cl/go-ui/objects" + "github.com/ymohl-cl/go-ui/objects/block" + "github.com/ymohl-cl/go-ui/objects/text" ) const ( diff --git a/objects/objects.go b/objects/objects.go index af9ad18..2ad5a37 100644 --- a/objects/objects.go +++ b/objects/objects.go @@ -33,6 +33,8 @@ const ( ErrorData = "data scene not loaded" ) +type Test struct{} + // Object interface it's a model to the game builder. type Object interface { // Init object to draw it. If error occurred, object can't be drawn diff --git a/objects/text/i_text.go b/objects/text/i_text.go index 94cdb68..5ff7110 100644 --- a/objects/text/i_text.go +++ b/objects/text/i_text.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Init object to draw it. If error occurred, object can't be drawn diff --git a/objects/text/text.go b/objects/text/text.go index 5978d3c..0d8ce39 100644 --- a/objects/text/text.go +++ b/objects/text/text.go @@ -5,7 +5,7 @@ import ( "github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/ttf" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Text object implementation diff --git a/renderer.go b/renderer.go new file mode 100644 index 0000000..a2f3951 --- /dev/null +++ b/renderer.go @@ -0,0 +1,96 @@ +package gamebuilder + +import ( + "sync" + + "github.com/veandco/go-sdl2/sdl" +) + +// Renderer type to manage the drawing +type Renderer interface { + Close() + Scene(s Scene) + Clear() error + Draw() + Driver() *sdl.Renderer +} + +type renderer struct { + driver *sdl.Renderer + window *sdl.Window +} + +// NewRenderer sdl +func NewRenderer(c ConfigUI) (Renderer, error) { + var r renderer + var err error + + if r.window, err = sdl.CreateWindow(c.Window.Title, + sdl.WINDOWPOS_CENTERED, + sdl.WINDOWPOS_CENTERED, + c.Window.Width, + c.Window.Height, + sdl.WINDOW_OPENGL); err != nil { + return nil, err + } + if r.driver, err = sdl.CreateRenderer(r.window, -1, sdl.RENDERER_ACCELERATED); err != nil { + return nil, err + } + r.driver.SetDrawBlendMode(sdl.BLENDMODE_BLEND) + + return &r, nil +} + +// Close sdl renderer resources +func (r *renderer) Close() { + if r.driver != nil { + r.driver.Destroy() + } + if r.window != nil { + r.window.Destroy() + } +} + +// Scene renderer prepare +func (r *renderer) Scene(s Scene) { + layers, m := s.GetLayers() + m.Lock() + defer m.Unlock() + + var wg sync.WaitGroup + + // fmt.Println("DrawLayer") + for i := 0; layers[uint8(i)] != nil; i++ { + layer := layers[uint8(i)] + for _, object := range layer { + if object.IsInit() { + wg.Add(1) + go object.Draw(&wg, r.driver) + } + } + wg.Wait() + } +} + +// Clear image before write a new +func (r *renderer) Clear() error { + var err error + + if err = r.driver.SetDrawColor(0, 0, 0, 0); err != nil { + return err + } + if err = r.driver.Clear(); err != nil { + return err + } + return nil +} + +// Draw the current renderer +func (r *renderer) Draw() { + r.driver.Present() +} + +// Driver getter (*sdl.Renderer) +func (r *renderer) Driver() *sdl.Renderer { + return r.driver +} diff --git a/scene/scene.go b/scene.go similarity index 76% rename from scene/scene.go rename to scene.go index 8e2e6c0..044d46f 100644 --- a/scene/scene.go +++ b/scene.go @@ -1,10 +1,10 @@ -package scene +package gamebuilder import ( "sync" "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" + "github.com/ymohl-cl/go-ui/objects" ) // Scene is a interface and define the design model to your scenes. @@ -26,10 +26,9 @@ type Scene interface { // KeyboardEvent provide key down to the scene KeyboardEvent(*sdl.KeyboardEvent) // SetSwitcher can be called to change scene with index scene on - // first parameter and flag on true to close old scene and false to stop it only - SetSwitcher(func(uint8, bool) error) - // SetCloser quit the application - SetCloser(func()) + SetSwitcher(func(indexScene string) error) + // SetCloser call the closer scene + SetCloser(func(indexScene string) error) // Update : called on each frame Update() } diff --git a/scene/loader/build_loader.go b/scene/loader/build_loader.go deleted file mode 100644 index d64c3b8..0000000 --- a/scene/loader/build_loader.go +++ /dev/null @@ -1,65 +0,0 @@ -package loader - -import ( - "os" - "runtime" - - "github.com/ymohl-cl/game-builder/objects" - "github.com/ymohl-cl/game-builder/objects/block" - "github.com/ymohl-cl/game-builder/objects/text" -) - -func (L *DefaultLoader) addBlockLoading() error { - var b *block.Block - var err error - - if b, err = block.New(block.Filled); err != nil { - return err - } - b.SetVariantStyle(colorRed, colorGreen, colorBlue, colorOpacity, objects.SFix) - b.UpdateSize(L.widthBlock, L.heightBlock) - b.UpdatePosition(originX+L.widthBlock, originY+(L.heightScreen-L.heightScreen/4)) - - if err = b.Init(L.renderer); err != nil { - return err - } - L.layers[layerLoadingBar] = append(L.layers[layerLoadingBar], b) - L.loadBlock = b - return nil -} - -func (L *DefaultLoader) addTxt() error { - var t *text.Text - var err error - var x, y int32 - var fontPath string - - x = L.widthScreen / 2 - y = L.heightScreen / 4 - - // get good path to open font - fontPath = os.Getenv("GOPATH") + "/pkg/" + runtime.GOOS + "_" + runtime.GOARCH + font - - // add title - if t, err = text.New(mainTxt, sizeMainTxt, fontPath, x, y); err != nil { - return err - } - t.SetVariantStyle(colorRed, colorGreen, colorBlue, colorOpacity, objects.SFix) - if err = t.Init(L.renderer); err != nil { - return err - } - L.layers[layerText] = append(L.layers[layerText], t) - - // add signature - y = L.heightScreen / 2 - if t, err = text.New(txtBottomToLoad, sizeTxtBottomToLoad, fontPath, x, y); err != nil { - return err - } - t.SetVariantStyle(colorRed, colorGreen, colorBlue, colorOpacity, objects.SFix) - if err = t.Init(L.renderer); err != nil { - return err - } - L.layers[layerText] = append(L.layers[layerText], t) - - return nil -} diff --git a/scene/loader/event_loader.go b/scene/loader/event_loader.go deleted file mode 100644 index dcd8b8a..0000000 --- a/scene/loader/event_loader.go +++ /dev/null @@ -1,21 +0,0 @@ -package loader - -/* -** Endpoint action from scene - */ - -func (l *DefaultLoader) addLoadingBar() { - x, _ := l.loadBlock.GetPosition() - w, h := l.loadBlock.GetSize() - // check next width [padding x + blockWidt] + [width current] + [width next block] - if x+l.widthBlock+w+l.widthBlock > l.widthScreen-l.widthBlock { - l.resetLoadingBlock() - } else { - l.loadBlock.UpdateSize(w+l.widthBlock, h) - } -} - -func (l *DefaultLoader) resetLoadingBlock() { - _, h := l.loadBlock.GetSize() - l.loadBlock.UpdateSize(l.widthBlock, h) -} diff --git a/scene/loader/i_loader.go b/scene/loader/i_loader.go deleted file mode 100644 index c72af4e..0000000 --- a/scene/loader/i_loader.go +++ /dev/null @@ -1,102 +0,0 @@ -package loader - -import ( - "errors" - "sync" - - "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" -) - -/* -** interface functions - */ - -// Build describe the scene with objects needest -func (l *DefaultLoader) Build() error { - var err error - - l.layers = make(map[uint8][]objects.Object) - - if err = l.addTxt(); err != nil { - return err - } - if err = l.addBlockLoading(); err != nil { - return err - } - return nil -} - -// Init the scene -func (l *DefaultLoader) Init() error { - if l.renderer == nil { - return errors.New(objects.ErrorRenderer) - } - - if l.layers == nil { - return errors.New(errorLayers) - } - if l.loadBlock == nil { - return errors.New(errorLayers) - } - l.initialized = true - return nil -} - -// IsInit return status initialize -func (l *DefaultLoader) IsInit() bool { - return l.initialized -} - -// Run the scene -func (l *DefaultLoader) Run() error { - l.addLoadingBar() - return nil -} - -// Stop scene -func (l *DefaultLoader) Stop() { - l.resetLoadingBlock() -} - -// Close the scene -func (l *DefaultLoader) Close() error { - var err error - - l.initialized = false - - l.m.Lock() - defer l.m.Unlock() - if err = objects.Closer(l.layers); err != nil { - return err - } - - l.layers = nil - return nil -} - -// GetLayers provide all scene's objects to draw them -func (l *DefaultLoader) GetLayers() (map[uint8][]objects.Object, *sync.Mutex) { - return l.layers, l.m -} - -// KeyboardEvent provide key down to the scene -func (l *DefaultLoader) KeyboardEvent(keyboard *sdl.KeyboardEvent) { - return -} - -// SetSwitcher can be call to change scene with index scene and flag closer -func (l *DefaultLoader) SetSwitcher(f func(uint8, bool) error) { - l.switcher = f -} - -// Update : called on each frame -func (l *DefaultLoader) Update() { - l.addLoadingBar() - return -} - -// SetCloser : allow quit the application -func (l *DefaultLoader) SetCloser(f func()) { - // nothing to do -} diff --git a/scene/loader/loader.go b/scene/loader/loader.go deleted file mode 100644 index 32d72d0..0000000 --- a/scene/loader/loader.go +++ /dev/null @@ -1,78 +0,0 @@ -package loader - -import ( - "errors" - "sync" - - "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" - "github.com/ymohl-cl/game-builder/objects/block" -) - -const ( - // order layers of scene - //layerBackground = iota - layerText = iota - layerLoadingBar - - // text to the scene loader - font = "/github.com/ymohl-cl/game-builder/Ressources/stocky.ttf" - mainTxt = "LOADING ..." - txtBottomToLoad = "this app use gogame-builder github.com/ymohl-cl/game-builder" - sizeMainTxt = 78 - sizeTxtBottomToLoad = 20 - - // color block to loading bar - colorRed = 52 - colorGreen = 201 - colorBlue = 36 - colorOpacity = 255 - - // position - originX = 0 - originY = 1 - - // errors message - errorLayers = "layers not define on the default loader" -) - -// DefaultLoader is the default loader if it don't provided -type DefaultLoader struct { - /* infos scene */ - initialized bool - refresh bool - switcher func(uint8, bool) error - - /* objects by layers */ - m *sync.Mutex - layers map[uint8][]objects.Object - loadBlock *block.Block - - /* size to load objects */ - widthScreen int32 - heightScreen int32 - widthBlock int32 - heightBlock int32 - - /* sdl ressources */ - renderer *sdl.Renderer -} - -/* -** constructor - */ - -// New provide a new object -func New(r *sdl.Renderer, width, height int32) (*DefaultLoader, error) { - if r == nil { - return nil, errors.New(objects.ErrorRenderer) - } - - l := DefaultLoader{renderer: r} - l.m = new(sync.Mutex) - l.widthScreen = width - l.heightScreen = height - l.widthBlock = (width / 100) * 2 - l.heightBlock = (height / 100) * 3 - return &l, nil -} diff --git a/script.go b/script.go new file mode 100644 index 0000000..0d6ef86 --- /dev/null +++ b/script.go @@ -0,0 +1,178 @@ +package gamebuilder + +import ( + "errors" + "sync" +) + +// Script describe the application/game scenes and the current use +type Script interface { + Close() error + SetLoader(s Scene) error + AddScene(index string, s Scene) error + LoadScene(index string) error + CloseScene(index string) error + Scene(index string) (Scene, error) + SceneIndex() string +} + +type script struct { + sync.Mutex + loader Scene + scenes map[string]Scene + scene string + running bool +} + +// NewScript instance +func NewScript() Script { + return &script{scenes: make(map[string]Scene)} +} + +// Close free all scenes resources +func (s *script) Close() error { + var err error + + s.scene = "" + if s.loader != nil && s.loader.IsInit() { + if err = s.loader.Close(); err != nil { + return err + } + } + for _, sn := range s.scenes { + if err = sn.Close(); err != nil { + return err + } + } + return nil +} + +// SetLoader build a loader scene to use it in transition +func (s *script) SetLoader(sn Scene) error { + var err error + + if s.running { + return errors.New(errorScriptModifier) + } + if s.loader != nil { + if err = s.loader.Close(); err != nil { + return err + } + } + + s.loader = sn + return nil +} + +// AddScene ajoute the scene in the gamebuilder script. The scene should be called by the index +func (s *script) AddScene(index string, sn Scene) error { + var err error + + if s.running { + return errors.New(errorScriptModifier) + } + if old, ok := s.scenes[index]; ok { + if err = old.Close(); err != nil { + return err + } + } + s.scenes[index] = sn + sn.SetSwitcher(s.LoadScene) + sn.SetCloser(s.CloseScene) + return nil +} + +// LoadScene by index reference +func (s *script) LoadScene(index string) error { + var err error + var sn Scene + + if !s.running { + s.running = true + } + ok := false + if sn, ok = s.scenes[index]; !ok { + return errors.New(errorSceneNotFound) + } + + s.Lock() + defer s.Unlock() + // start loader if configured + if s.loader != nil { + if !s.loader.IsInit() { + if err = s.loader.Build(); err != nil { + return err + } + if err = s.loader.Init(); err != nil { + return err + } + } + if err = s.loader.Run(); err != nil { + return err + } + } + + // stop the previous scene + if s.scene != "" { + s.scenes[s.scene].Stop() + } + + // setup the new scene + if !sn.IsInit() { + if err = sn.Build(); err != nil { + return err + } + if err = sn.Init(); err != nil { + return err + } + } + + // start the new scene + s.scene = index + if err = sn.Run(); err != nil { + return err + } + + // stop the loader if configured + if s.loader != nil { + s.loader.Stop() + } + + return nil +} + +// CloseScene by index reference +func (s *script) CloseScene(index string) error { + var err error + var sn Scene + + ok := false + if sn, ok = s.scenes[index]; !ok { + return errors.New(errorSceneNotFound) + } + + s.Lock() + defer s.Unlock() + if err = sn.Close(); err != nil { + return err + } + + return nil +} + +// SceneIndex getter return the current scene loaded +func (s *script) SceneIndex() string { + return s.scene +} + +// Scene getter return current scene by index reference +func (s *script) Scene(index string) (Scene, error) { + var sn Scene + + ok := false + if sn, ok = s.scenes[index]; !ok { + return nil, errors.New(errorSceneNotFound) + } + + return sn, nil +} diff --git a/scripter/README.md b/scripter/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/scripter/events.go b/scripter/events.go deleted file mode 100644 index 5938a9d..0000000 --- a/scripter/events.go +++ /dev/null @@ -1,90 +0,0 @@ -package scripter - -import ( - "errors" - - "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" -) - -// events : catch mouse and keyboard events -func (s *Script) events(E sdl.Event) { - var err error - - switch E.(type) { - case *sdl.MouseMotionEvent: - err = s.mouseMotionEvent(E.(*sdl.MouseMotionEvent)) - case *sdl.MouseButtonEvent: - err = s.mouseButtonEvent(E.(*sdl.MouseButtonEvent)) - case *sdl.KeyboardEvent: - err = s.keyboardEvent(E.(*sdl.KeyboardEvent)) - } - if err != nil { - panic(err) - } -} - -// mouseMotionEvent define a new mouse's position -func (s *Script) mouseMotionEvent(mouse *sdl.MouseMotionEvent) error { - if _, ok := s.list[s.current]; !ok { - return errors.New(errorIndexUndefined) - } - layers, m := s.list[s.current].GetLayers() - m.Lock() - size := len(layers) - for i := size - 1; i >= 0; i-- { - layer := layers[uint8(i)] - for _, object := range layer { - if object.IsOver(mouse.X, mouse.Y) { - if object.GetStatus() != objects.SClick { - go object.SetStatus(objects.SOver) - } - } else { - go object.SetStatus(objects.SBasic) - } - } - } - m.Unlock() - return nil -} - -// mouseButtonEvent define a new mouse's action (click) -func (s *Script) mouseButtonEvent(button *sdl.MouseButtonEvent) error { - if button.Button != sdl.BUTTON_LEFT { - return nil - } - if _, ok := s.list[s.current]; !ok { - return errors.New(errorIndexUndefined) - } - layers, m := s.list[s.current].GetLayers() - m.Lock() - size := len(layers) - for i := size - 1; i >= 0; i-- { - layer := layers[uint8(i)] - for _, object := range layer { - if button.State == sdl.PRESSED { - if object.GetStatus() == objects.SOver { - go object.SetStatus(objects.SClick) - break - } - } else if button.State == sdl.RELEASED { - if object.GetStatus() == objects.SClick { - go object.SetStatus(objects.SOver) - go object.Click() - break - } - } - } - } - m.Unlock() - return nil -} - -// keyboardEvent : _ -func (s *Script) keyboardEvent(keyDown *sdl.KeyboardEvent) error { - if _, ok := s.list[s.current]; !ok { - return errors.New(errorIndexUndefined) - } - go s.list[s.current].KeyboardEvent(keyDown) - return nil -} diff --git a/scripter/loop.go b/scripter/loop.go deleted file mode 100644 index 740f58d..0000000 --- a/scripter/loop.go +++ /dev/null @@ -1,44 +0,0 @@ -package scripter - -import ( - "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/drivers" -) - -// Run start the loop game on the sdl Main function -func (s *Script) Run(D drivers.VSDL) { - // SDL.Main allow use sdl.Do() to queue sdl instructions. - sdl.Main(func() { - if err := s.start(D); err != nil { - panic(err) - } - }) -} - -// Start is a loop game -func (s *Script) start(d drivers.VSDL) error { - var err error - - w, h := d.GetScreenSize() - if err = s.init(d.GetRenderer(), w, h); err != nil { - return err - } - for s.isRunning() { - sdl.Do(func() { - for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { - switch event.(type) { - case *sdl.QuitEvent: - s.running = false - default: - s.events(event) - } - } - }) - s.drawScene(d.GetRenderer()) - sdl.Do(func() { - sdl.Delay(1000 / frame) - }) - } - s.close() - return nil -} diff --git a/scripter/script_builder.go b/scripter/script_builder.go deleted file mode 100644 index b367f66..0000000 --- a/scripter/script_builder.go +++ /dev/null @@ -1,95 +0,0 @@ -package scripter - -import ( - "errors" - - "github.com/ymohl-cl/game-builder/scene" -) - -// AddLoader define the scene loader on the reserved index (0) -// if loader already exist, the old older is destroy and replace -// by the new -func (s *Script) AddLoader(l scene.Scene) error { - if s.running == true { - return errors.New(errorChangeScriptOnRunning) - } - s.list[layerLoading] = l - return nil -} - -// AddScene define the new scene on the index specified -// if index is 0 (reserved by loader), or index already used, -// a error is occured -// if first is true, the scene will be first loaded -func (s *Script) AddScene(newScene scene.Scene, index uint8, first bool) error { - if s.running == true { - return errors.New(errorChangeScriptOnRunning) - } - - if index == 0 { - return errors.New(errorIndexLoader) - } - if _, ok := s.list[index]; ok { - return errors.New(errorIndexUsed) - } - - s.list[index] = newScene - if first { - s.current = index - } - return nil -} - -// Switch change the scene by the loader step -func (s *Script) Switch(index uint8, closeOld bool) error { - var err error - - // check conditions to do the switch scene - if _, ok := s.list[index]; !ok { - return errors.New(errorIndexUndefined) - } - if !s.running { - return errors.New(errorScriptNoRunning) - } - - // save previous scene - previous := s.current - - // switch to loader - s.m.Lock() - s.current = layerLoading - if err = s.list[s.current].Run(); err != nil { - return err - } - s.m.Unlock() - - // stop and close if closeOld is true - s.list[previous].Stop() - if closeOld { - if err = s.list[previous].Close(); err != nil { - return err - } - } - - // build and init new scene - if !s.list[index].IsInit() { - s.buildNewScene(index) - } - - // switch to the new scene - s.m.Lock() - s.current = index - if err = s.list[index].Run(); err != nil { - return err - } - s.m.Unlock() - - // stop loader scene - s.list[layerLoading].Stop() - return nil -} - -// StopRun close the script -func (s *Script) StopRun() { - s.running = false -} diff --git a/scripter/scripter.go b/scripter/scripter.go deleted file mode 100644 index de708cf..0000000 --- a/scripter/scripter.go +++ /dev/null @@ -1,36 +0,0 @@ -package scripter - -import ( - "sync" - - "github.com/ymohl-cl/game-builder/scene" -) - -const ( - frame = 60 - layerLoading = 0 - errorIndexLoader = "index 0 is reserved to loader scene" - errorIndexUsed = "index to add new scene is already used" - errorChangeScriptOnRunning = "script can't be modified when is running" - errorScriptNoRunning = "script don't be running" - errorIndexUndefined = "index scene is undefined" -) - -// Script object : manage all scenes -type Script struct { - list map[uint8]scene.Scene - running bool - - // lock state current - m *sync.Mutex - current uint8 -} - -// New provide a script object -func New() *Script { - s := Script{} - - s.list = make(map[uint8]scene.Scene) - s.m = new(sync.Mutex) - return &s -} diff --git a/scripter/tools_scripter.go b/scripter/tools_scripter.go deleted file mode 100644 index eac0fe6..0000000 --- a/scripter/tools_scripter.go +++ /dev/null @@ -1,171 +0,0 @@ -package scripter - -import ( - "sync" - - "github.com/veandco/go-sdl2/sdl" - "github.com/ymohl-cl/game-builder/objects" - "github.com/ymohl-cl/game-builder/scene/loader" -) - -// buildNewScene : build the scene specified by the index parameter -func (s *Script) buildNewScene(index uint8) error { - var err error - - // set switcher function - s.list[index].SetSwitcher(s.Switch) - s.list[index].SetCloser(s.StopRun) - // build scene - if err = s.list[index].Build(); err != nil { - return err - } - // init scene - if err = s.list[index].Init(); err != nil { - return err - } - - return nil -} - -// Init : initialize the script begin and run it -func (s *Script) init(r *sdl.Renderer, widthScreen, heightScreen int32) error { - var err error - - // init loader - if _, ok := s.list[layerLoading]; !ok { - if s.list[layerLoading], err = loader.New(r, widthScreen, heightScreen); err != nil { - return err - } - } - if err = s.buildNewScene(layerLoading); err != nil { - return err - } - if err = s.list[layerLoading].Run(); err != nil { - return err - } - - // save first scene index - first := s.current - - // start on the loader scene - s.running = true - s.current = layerLoading - - // if no scene loaded, leave function - if first == layerLoading { - return nil - } - // init first scene - go func() { - if err = s.buildNewScene(first); err != nil { - panic(err) - } - if err = s.list[first].Run(); err != nil { - panic(err) - } - // continue with the first scene - s.m.Lock() - s.current = first - s.m.Unlock() - - // stop the loader - s.list[layerLoading].Stop() - }() - - return nil -} - -// IsRunning return status script -func (s Script) isRunning() bool { - return s.running -} - -// DrawScene : current scene loaded -func (s Script) drawScene(r *sdl.Renderer) { - var wg sync.WaitGroup - var layers map[uint8][]objects.Object - var m *sync.Mutex - - // Lock current acces list - s.m.Lock() - if !s.list[s.current].IsInit() { - s.m.Unlock() - return - } - // call update - go s.list[s.current].Update() - // get objects to draw them - layers, m = s.list[s.current].GetLayers() - // Lock to protect layers data - m.Lock() - // Unlock current access list - s.m.Unlock() - - // clear the screen - wg.Add(1) - go s.clearScreen(r, &wg) - wg.Wait() - - s.drawLayers(layers, r) - // Unlock layers data - m.Unlock() - s.appliesScreen(r) -} - -// clearScreen reset the screen -func (s Script) clearScreen(r *sdl.Renderer, wg *sync.WaitGroup) { - defer wg.Done() - var err error - - sdl.Do(func() { - if err = r.SetDrawColor(0, 0, 0, 0); err != nil { - panic(err) - } - if err = r.Clear(); err != nil { - panic(err) - } - }) -} - -// drawLayers, draw objects by order layer -func (s Script) drawLayers(l map[uint8][]objects.Object, r *sdl.Renderer) { - var wg sync.WaitGroup - - // fmt.Println("DrawLayer") - for i := 0; l[uint8(i)] != nil; i++ { - // fmt.Println("index: ", i) - layer := l[uint8(i)] - for _, object := range layer { - // fmt.Println("object number: ", it) - if object.IsInit() { - wg.Add(1) - go object.Draw(&wg, r) - } - } - wg.Wait() - } -} - -// appliesScreen : call Present() to applies the new screen -func (s Script) appliesScreen(r *sdl.Renderer) { - sdl.Do(func() { - r.Present() - }) -} - -// close all ressources -func (s *Script) close() { - var err error - - if scene, ok := s.list[s.current]; ok { - scene.Stop() - } - - for _, scene := range s.list { - if scene.IsInit() { - if err = scene.Close(); err != nil { - panic(err) - } - } - } -}