Closed amlwwalker closed 6 months ago
ping?
I guess nobody answered because that is not very Go-App related and quite a special use-case too.
My Ebiten game does just use an HTML page for mobile. But I can't really deploy it that way because it still uses too many resources. I wrote to and back with the author of Ebiten some months ago, but we could not identify the problem (only on mobile, not on desktop browsers). So, currently I am the only player and use the Desktop version. I was thinking about rewriting it for Go-App using Canvas. Which is very possible for that game as I render everything and implemented a GUI myself. We wrote some canvas based "pseudo" games while we were evaluation Go-App, and it works good. You simply need to set up a canvas and use the display update callback for rendering updates.
I am sure you can communicate between WASM modules the same way as you communicate with java-script objects (aka through window.data.whatever="test" / https://stackoverflow.com/questions/1063813/listener-for-property-value-changes-in-a-javascript-object).
WASM (as compiled by the Go compiler) has no idea about other modules AFAIK.
Setting it up to run multiple different WASM modules can be a bit tricky. At least, you need to know what you do. I did it with Go App as WASM and some extra modules that got compiled through TinyGo as proof of concept. That was using a game logic compiled by TinyGo and the display and key input handled by Go-App
But as you probably already need a backend for the user data, why don't just use that from both parts and embed the game as an iframe. Direct communication with the iframe should work too if you are serving is under the same (sub-)domain.
For my game, I just put everything inside Ebiten, because of the complexity.
BTW. Here a "pong" fake rendered using canvas as Go-App component:
package pages
import (
"math"
"math/rand"
"github.com/google/uuid"
"git.jetbrains.space/metatexx/skelly/skelly/pkg/x/dbg"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type renderChan chan struct{}
type Canvas struct {
app.Compo
Width float64
Height float64
Id string
done renderChan
running bool
}
var _ app.Composer = (*Canvas)(nil)
var _ app.Mounter = (*Canvas)(nil)
var _ app.Dismounter = (*Canvas)(nil)
func (c *Canvas) OnInit() {
dbg.Log()
c.Id = "cvs:" + uuid.NewString()
}
func (c *Canvas) OnDismount() {
dbg.Log()
// tell our go routine that we don't need it anymore
c.running = false // comment this out to see how it gets ugly :)
}
func (c *Canvas) OnMount(ctx app.Context) {
dbg.Log()
cvEl := app.Window().GetElementByID(c.Id)
cvCtx := cvEl.Call("getContext", "2d")
// run "endless"
// Notice: I actually think the whole channel thing here is not correct, but it works(tm)
c.done = make(chan struct{})
ctx.Async(func() {
scale := c.Width / 640.0
s := 10.0 * scale
x := float64(rand.Intn(int(640-50))+25) * scale
y := float64(rand.Intn(int(480-10))+5) * scale
ph := 80.0 * c.Width / 640
speed := 200.0 * scale
dx, dy := 0.21, 0.42 // speed
dx += float64(rand.Intn(100)/100) - 0.5
w := cvEl.Get("width").Float()
h := cvEl.Get("height").Float()
var renderFrame app.Func
var tlast float64
c.running = true
var tcount = 0
renderFrame = app.FuncOf(func(this app.Value, args []app.Value) interface{} {
now := args[0].Float()
tdiff := now - tlast
tlast = now
tcount++
if tcount%100 == 0 {
dbg.Logf("count %d", tcount)
}
cvCtx.Call("clearRect", 0, 0, w, h)
cvCtx.Call("beginPath")
cvCtx.Set("globalAlpha", 1)
cvCtx.Set("lineWidth", 5*scale)
cvCtx.Set("strokeStyle", "#202020")
cvCtx.Set("fillStyle", "#e0e0e0")
cvCtx.Call("rect", 0, 0, w, h)
cvCtx.Call("fill")
cvCtx.Call("stroke")
cvCtx.Call("beginPath")
cvCtx.Call("setLineDash", []interface{}{5})
cvCtx.Set("globalAlpha", 0.5)
cvCtx.Set("lineWidth", 3*scale)
cvCtx.Call("moveTo", w/2+2.5, 0)
cvCtx.Call("lineTo", w/2+2.5, h)
cvCtx.Call("stroke")
ppos := y - ph/2
if ppos < 5 {
ppos = 5
} else if (ppos + ph) > h-5 {
ppos = h - ph - 5
}
cvCtx.Call("beginPath")
cvCtx.Call("setLineDash", []interface{}{})
cvCtx.Set("fillStyle", "#30A030")
//cvCtx.Set("strokeStyle", "#000000")
cvCtx.Set("lineWidth", 2)
cvCtx.Set("globalAlpha", 1.0)
cvCtx.Call("rect", 15*scale, ppos, 10*scale, ph)
cvCtx.Call("fill")
//cvCtx.Call("stroke")
cvCtx.Call("beginPath")
cvCtx.Set("lineWidth", 2)
cvCtx.Set("fillStyle", "#3030A0")
//cvCtx.Set("strokeStyle", "#000000")
cvCtx.Call("rect", w-25*scale, ppos, 10*scale, ph)
cvCtx.Call("fill")
//cvCtx.Call("stroke")
cvCtx.Set("globalAlpha", 1.0)
cvCtx.Call("beginPath")
// cvCtx.Set("lineWidth", 0)
cvCtx.Set("fillStyle", "#A00000")
cvCtx.Set("strokeStyle", "#000000")
//cvCtx.Call("setLineDash", []interface{}{1})
cvCtx.Call("setLineDash", []interface{}{})
cvCtx.Call("arc", x, y, s, 0, 2*math.Pi)
cvCtx.Call("fill")
// cvCtx.Call("stroke")
sdx := dx * speed / tdiff
sdy := dy * speed / tdiff
if (x+sdx) > w-25*scale-s || (x+sdx) < s+25*scale {
dx *= -1
sdx *= -1
}
if (y+sdy) > h-s || (y+sdy) < s {
dy *= -1
sdy *= -1
}
x += sdx
y += sdy
if c.running {
app.Window().Call("requestAnimationFrame", renderFrame)
} else {
close(c.done)
}
return nil
})
defer renderFrame.Release()
// start the rendering
app.Window().Call("requestAnimationFrame", renderFrame)
// wait till the channel gets closed
<-c.done
dbg.Log("closed")
})
}
func (c *Canvas) Render() app.UI {
dbg.Log()
if c.Width == 0 {
if c.Height == 0 {
c.Width = 640
} else {
c.Width = math.Floor(c.Height / 3 * 4)
}
}
if c.Height == 0 {
if c.Width == 0 {
c.Height = 480
} else {
c.Height = math.Floor(c.Width / 4 * 3)
}
}
return app.Canvas().ID(c.Id).Width(int(c.Width)).Height(int(c.Height))
}
you can do multi wasm using tractor. Its pretty clever
I am using the Golang library Ebiten to make a 2D game. I want to 'embed' this into a webpage and then build the webpage around the game using Go-App.