Enu 0.2 is finally here! This has been "a month or two away" for about 2.5 years now, but in the end I decided to hold off on releasing it until it's actually good. Well, it's good now. This is the biggest update to Enu by far, and adds a host of new features like multithreading, multiplayer (using the amazing netty + flatty + supersnappy combo by treeform and guzba), worlds containing multiple levels, programmable level switching, level templates, in-world menus, and more.
Although there are still many missing features and rough edges, I think this release captures what Enu is all about, and should be usable by most people. Try it out! Make things! Tell your friends!
Check out the new Enu web site at https://ē.nu. Or, for folks who prefer boring urls without impossible to type characters, http://getenu.com. This has documentation created with the amazing nimibook by pietroppeter and HugoGranstrom, along with some short videos showing Enu's features. You can also join us on Discord.
The Windows version isn't signed, and you'll get a scary "Windows protected your PC" warning when you launch the installer. You'll need to press "Run anyway" which, depending on your windows version, may require clicking "More info" first. Enu will be on Steam soon(ish), which won't have this issue.
Thanks for checking out Enu. Have fun!
Wow, great news! Thank you for the fantastic job you've done.
I'm a huge Enu fan since day 1, excited to try 0.2.
Awesome!
it could be fun to use it for some advent of code visualizations :)
Nim and Godot! Cool!
How did you find integrating Nim in with Godot?
I guess I didn't really answer the question. Using Nim and Godot together has mostly been great. I wish it had better support for hot code reloading, but the fact that many errors get caught at compile time means I have to iterate less to get something working. GDScript code samples translate to Nim easily, and even native extensions, like the godot_voxel module I use, get fairly ergonomic Nim bindings generated automatically without any effort from me.
There are a few rough spots. Properties exposing value types, such as Vector3 or Transform, return an immutable version, not var, so generally you can't just say something like bot.rotation.x = 10, but instead have to put the rotation into a var, update it, then assign it back. It's pretty easy to write a helper for this, but I didn't even understand the concept of var return types when I started Enu so this was a pain point for a while. A bigger issue is cyclic imports/dependencies. In godot-nim, types that extend godot classes have both the type and it's properties, procs and methods defined in a single gdobj block, which means the usual cyclic dependency tricks of putting types into a types.nim file or using forward declarations don't really work. I struggled with this for quite a while, and still don't have a great general solution. Ultimately I moved nearly all of my logic out of godot classes and mostly treat godot as an MVC style view, which in retrospect is probably a better architecture anyway, but struggling with this was a big productivity hit early in the project. I'm pretty sure godot-nim could be changed to allow defining a type and its procs separately, but unless I've missed something big it doesn't support it currently.
All of this is easily offset by the fact that you get to use Nim however, a general purpose language with an ecosystem, and a package manager, and a rich stdlib, and macros, and test frameworks, and proper error handling, and FFI support, and countless other things you don't get with GDScript. I personally think shipping their own programming language was Godot's single biggest unforced error, but by using Nim this isn't my problem. The lack of a stable Godot 4 binding is an issue though, but hopefully one that will be rectified soon.
This is GREAT! Thank you! I'm really curious to hear where the pain points are, and what you need to keep going. I'm really proud of this release, but the fact of the matter is that there are a LOT of missing features required for real game development, and I'd like to prioritize development based on what real people are doing.
Here are some things I'm thinking about for the short to medium term, in roughly the order I'm planning to tackle them. I'd love to know which you feel is most important, and if there's anything big I've missed:
Other major items that I'm not sure how to prioritize:
Anyway, thank you very much for doing this. Please let me know if there's anything I can do to help.
I'm really curious to hear where the pain points are
Honestly, the biggest for me so far has been stability. Enu crashes from time to time. I've noticed this much likely to happen when I use prototypes.
Here's the second part of my terrain generation quest: https://moigagoo.svbtle.com/adding-trees-to-random-terrains-in-enu-0-2
This time, I'm generating trees to the terrain so that it's hard to see through and makes it interesting to sneak around monsters.
Thanks for the feedback. I really appreciate it.
I'll have to do more "non happy path" testing for prototypes. It sounds like you were instancing a prototype that was instancing a prototype, which should work of course, but none of my demos actually do that so it must have broken at some point. I added it to the bug list. I also see you hit a max vm iterations bug. I thought I had fixed that, but I guess not. I'll take another look.
The trees look awesome BTW!
The "where should I put my prototypes?" thing is a real issue. You'll definitely have a proper way to hide them at some point, but I'm not sure when exactly. Once there's an inventory your prototypes will show up there, so I may add some way to toggle them. I was also thinking of a dedicated zone (maybe called The Lab or something) for stashing prototypes, away from your main level. That's pretty easy technically, but I don't know what the UX should be for getting to and from the zone.
For now, I'm just doing it in code. You can add something like this near the top of your prototype, under the name call:
if not isInstance:
show = false
quit()
This will hide the prototype, then stop the script. If you want to edit it while it's hidden you'll need to turn on god mode, which will show the prototype again but make it semi-transparent. You can do this through config.json, or by dropping a block and running player.god = true. Once there's a REPL toggling god mode will be easier.
If you want to make a public function you can just use a regular exported proc. The scripts all import each other automatically, so proc whatever*(me: Build) = ... should work from anywhere. Commands are just procs, except they make params auto if no type or default is specified, and they automatically shadow all params with a var version. I really should support the export marker for commands too, but I don't think it works yet. Eventually commands will also become part of the public API for the unit, but for now they're just a tweaked syntax for procs.
There are currently two ways to prevent players from messing with your level. One way is to add lock = true to your unit. This is what the tutorial does, so you can't mess things up too badly while going through it. The unit will work normally, but you won't be able to modify the blocks, delete it, or edit the code unless you're in god mode. Another option is to run player.playing = true. This hides the toolbar and makes it so nothing can be edited. A quick and dirty way to allow toggling this would be to drop a block and give it this code:
let menu = """
# Menu
- [Start/End Game](<nim://player.playing = not player.playing>)
"""
say(menu, more = menu)
Awesome job with this! I'm really excited to see what you come up with next.
Your code, and any bots/blocks that you place or edit should be saved automatically. If they don't that's a serious bug and I'd really like to hear more about it. If it truly isn't saving, please ping me on Discord or report it on Github so we can sort things out.
Blocks produced by a script aren't saved however, the idea being that they'll be regenerated the next time you run the script or launch the world. However, because scripts use a random seed value by default, if your script contains random elements it won't generate the same way twice unless you specify a seed (seed = 1234). I think I'm going to change this, so everything starts with a static seed, with some way to explicitly randomize it if that's what you need.
Another related issue is that large structures can take a while to regenerate. In those cases it would be nice for Enu to snapshot the generated blocks so it doesn't have to run the script every time. This is tricky though, since scripts can depend on each other, so there are cases where we have to run a script just to make sure it introduces any variables or functions that other scripts need. I'm not sure how to best handle this, but I'll have to come up with something.
Thanks for trying Enu!