Open Zireael07 opened 4 years ago
Hi!
To build the wasm file, you just have to use GOOS=js GOARCH=wasm go build -o boohu.wasm --tags js
, and then you have to load this file with html and js served statically with an http server (anyone one will do). The index.html
file in boohu webpage shows how to do that (actually, currently, the one in Harmonist website is a litter better, using lazy loading but that only works if the server is properly configured to send a mimetype for wasm files), and which js files it loads. One of them is essential (there are two), the wasm_exec.js
file: it comes with your Go compiler distribution, and may change somewhat with Go versions, so the one on boohu webpage may not be exactly the one you need, so pick the one for your Go version found under a misc/wasm
folder. Both the loaded screenfull.min.js for fullscreen, and the stylesheet are optional and can be changed/removed if you adapt index.html.
For image stuff I use the image
and base64
packages in the Go standard library.
BTW, if you just want a native tiles version, you can also try with the Tk backend.
Actually, there's one more thing for images, in case you want to change a tile or something: they're generated with a short gen.pl
script (found in tiles
folder), which generates base64 encoded images and puts then in a generated images.go
, using the tiles found in the same folder. So the images are integrated in the wasm file at the end.
Thanks! For now, I am just reusing your images.go, for replacing them I probably could use go's base64 encoder, but that's later.
Right now, I attempted to make a basic renderer for WASM based on Boohu's. It compiles, but doesn't draw anything. I know the WASM code runs because the canvas size changes, but that's all. wasm-roguelike.zip
Once I get the renderer going, I'll probably be fine on my own, I've done the basic tutorial so many times ;)
I had a look at your code: the issue comes from FlushCallback. Actually, it didn't draw the “log” as you did understand. I admit the name DrawLog is quite unintuitive, so my bad :-) It contains the “log” of frame drawing commands, I should have named it more explicitly DrawFrameLog or something like that. Index n
contains the list of drawing commands called when Flush is called for the n
th time (and only redraw parts of screen that changed). Because Boohu has replay functionality, all previous drawing commands are keeped around: if you don't want replay functionality you can keep only a list of drawing commands for the next frame.
If you grep for DrawLog in Boohu code, you'll find that it's type is []drawFrame
. In particular, you'll need the DrawLogFrame
method.
I figured that out moments before you posted.
Unfortunately, I have a weird problem with drawing a mouse highlight (I verified that the position is passed in from the mousemove callback, and I get an error in console from not being able to find tile... the TermInput along the way somehow isn't getting printed either)
Not sure what's going one at a glance, but your print uses %s
instead of %c
(or %v
, useful for debugging, and if it's not printable then print it as a number %d
), I suppose that's why you don't get the letter for which a tile is not found. Knowing the rune, it should be easier to debug.
The value is 32, which I expect corresponds to A. Maybe the problem is somewhere in the use of goroutines?
32 is space. For non-alphabetic characters, the key for the tile is not letter-character
and map LetterNames is used for them, so tile for ` the tile key is
letter-space`.
BTW, you somehow got letter-spaced
in TileMaps instead of letter-space
.
That fixed the weird issue. It looks like input isn't happening at all, since TermInput isn't getting printed...
Seems you forgot to use PlayerEvent ?
I wanted to skip that whole complex can of worms.
From what I can tell, the input channel is buffered and that ch.cap check is probably the source of the issue. What is the deal with that and why did you make that whole complex Event class?
The uiInput
type (TermInput
in your code) together with PollEvent() serves several purposes.
1) It's a common interface for all backends inspired from PollEvent() in termbox and tcell terminal backends.
2) Even if you only target wasm, if you want keyboard, mouse and mouse motion events, you want to unify that in a single event queue so that when checking user input you just check a single channel.
3) It allows for non-blocking input thanks to the channel: if you press keys fast, they will not be dropped even if your program is still computing/drawing last action, or displaying an animation. The size of the channel is limited to 5 to avoid unwanted extra key presses in case the game is slow (for example because of some slow animation or whatever and the player was [unsafely] holding down a key and it got repeated too much).
Ah, now I think I misread your question because you named PlayerEvent a function that calls PollEvent (which was your current problem, nothing to do with game Event class).
With respect to Event class (I suppose you mean game events, not UI ones), you can of course use other approaches for that, there are quite a few, which may be simpler or not depending of what features you want in your game. The goal was to have a common interface to anything that can happen in a given order at a given time. A single priority queue handles them, so that you can have monster turn, player turn, a programmed explosion, status end, fire/cloud progression or whatever scheduled using the same mechanism.
So yeah, the original issue was you seemed to not make any calls to PollEvent.
One step forward, one step back. Trying to call PollEvent in FlushCallback (aka Request Animation Frame) caused a deadlock. So I wrote a main loop and uncovered a weird problem with actually using game's methods, as well as: it seems to be blocking in spite of using channels! As in, I get input flooding my browser console, but nothing is drawn. wasm-roguelike.zip
You put your eventLoop before clear/drawing methods, it seems. And your highlightPos drawings never get flushed either, I would say.
And in your previous version, I think the problem was newGame did not block, so you had an infinite loop of newGame (the goal of that loop is to allow to play two games in a row).
Maybe you can draw a diagram with main loop and goroutines control, and channel reading/writing points. It may help to debug such things.
Thanks, putting pressAnyKey() at the end of newGame, and flushing the terminal fixed most issues, except the fact that I still can't move the main loop to game struct, it keeps telling me game has no methods or properties ...
You defined your gameeventLoop
as a function applied to type game instead of as a method for type game.
Thanks for giving me something to check - I figured out the difference with the power of Google :)
I think this is out of the teething stage now, input and drawing work fine: wasm-roguelike.zip
That's great to hear! :-)
Currently halfway through part 2 of the tutorial, because I've got to figure out data structures - instead of imitating OOP with Go's structs, I'll probably use an ECS. But that's an aside.
I noticed you're using an extant JS library for fullscreen mode: https://github.com/anaseto/boohu/blob/9103191d9e1a4b337e81102e979dba0a46264d62/js.go#L346
Can this be done with any custom JS code/library? How does the js/syscall module know what to call/how do I figure out what's available in JS.Global() ? (BTW that's for future stuff, waay past the end of the tutorial ;) )
You use any js library from Go (as long as it's loaded before in the html of course). I used screenful because portable fullscreen is hard. js.Global()
contains all the identifiers/stuff that are available at toplevel in js, AFAIK. Then you can do anything you want to them using the js/syscall
Go interface for calling js (actually probably based on wasm capabilities to call js). I haven't really see any big limitations in the js/syscall
interface, though I'm by no means an expert of js stuff, but even stuff for passing data around beetween Go and js seemd enough to me.
About OOP, ECS (never tried for real) and other stuff, I cannot say much, as I always do a kind of procedural/OO/functional mix or whatever feels right at the moment :-)
1) What files exactly do I need to build WASM version locally? 2) What did you use to convert images to byte string? I am guessing it's base64, was it a go package or some external tool?