Codea Craft and Documentation: Can we do better?

I’m not sure what you mean by “flat”. They’re plain and un-fancy and use a simple system instead of a cool one, but I suspect you’d find those things virtues, as I do.

As to what rigs mean to me, I made this system because I couldn’t figure out how to place the camera.

With the viewers, there’s a viewer object, a camera object, and there’s also an entity in there doing something, and I think there’s even a camera.entity that comes into play at times, and plus also the relationship between a component that’s been add-ed and the object it was added to is never super clear in the first place, and then when you get to the BasicPlayer it adds a second viewer component to an entity inside itself, which might mean there are two full sets of variables for accessing viewers, entities, cameras, camera.entities, and what have you—I’m not sure because my head’s already swimming by that point.

I’m sure if I understood the add() system better it would have been easy for me to sort that out, but I found myself struggling like heck to just get the Walker in the place I wanted and rotated to face the direction I wanted. Who do I set the position on? Who do I rotate? Do I rotate with quats, Eulers, or the strange rx, ry system introduced in the builtin viewers? I just couldn’t sort it out.

So I said, heck with it, I need another way. I’m gonna start with something I know exactly how to place and rotate—a regular old entity—and then I’m gonna grab all this viewer code and do my best to just put the one single entity at the core of it. And then I’ll never have to struggle with how to place and rotate things again.

And so that’s what I did.

So fundamentally what a rig means to me is boiling the complex camera system down, as much as possible, to a plain old entity.

Like I said, the goal was to end up with something I could easily (and confidently) place and rotate, but that came with some unexpected bonuses.

One of which being that it’s super easy to change rigs. In the example project there’s a button that instantly switches between an OrbitViewer and a FirstPersonViewer, something I’ve struggled with, and never fully succeeded at, many times in other projects. Here it’s a function call.

The other bonus was that making a third-person camera effect was super easy. So much so that I basically did it by accident. The lack of a third-person view has always bugged me in the Craft Voxel Terrain example, and now, thanks to making rigs, I’m moderately excited to be able to integrate one soon.

So, you know, regarding overall design principles, I understand your objections, and I’m not sure I can say rigs are good code, but I can say that they’re obvious code. And I’m liking obvious, because I can work with it.

I like the entity.properties idea a lot, and Codea doesn’t need anything new to support it right now.

Ironically the problem it solves is the same one that makes it possible to implement, lol.

Hm, ok, I think I get what you’re trying to do. I’ve not worked enough with cameras to have a sense of what I’d do. I think we can be sure that I’d do it with classes, and probably more than one.

To be obvious to a reader, I think they’d have to be more modular. In particular, the touch stuff seems to me to be able to be separated out. As it is, it seems like one big thing that could be three or four smaller things. To me, small things working together is more obvious than one big thing. YMMV.

But, again, you’ve done it, and I haven’t, so there;s that. On the other hand, I’ve read a lot of code …

Did you see my question about sensitivity, etc.? Any idea what those are/

@RonJeffries I think setting properties on entity can be thought of as duck typing, which would require people to agree on common interfaces at some point, and as you said is easy for clobbering to occur

@UberGoober I think flat is meant in that there is no real class structure in the rigs concept, so it’s more of a loose arrangement of parts, making it difficult to discern how it works

The components concept (i.e. :add(), :has(), :remove() is designed to completely sidestep all these issues. You can mix and match components, have their own local storage, and can be added or removed at will to change an entity’s behavior. Me liking things like jump() are, in my mind, handy shortcuts that could be exposed and mentioned in documentation. I think its best to only add a few “hero” methods and properties for commonly used things.

You could also do something like this:

rc = myEntity:add(RigidCapsule, params)

-- if jump() method itself is added
myEntity:jump(height)

-- or if used as a custom property for the component
myEntity.rigidCapsule:jump(height)

-- or use the reference returned by add()
rc:jump(height)
 
-- or just get the component (less efficient)
myEntity:get(RigidCapsule):jump(height)

-- or we add some wacky thing like Unity, which calls jump on every component that has the relevant function

myEntity:sendMessage("jump", height)

Could also have other convenience things like

-- only add if it doesn't already exist
rc = myEntity:getOrAdd(RigidCapsule, params)

-- add or replace existing component
rc = myEntity:addOrReplace(RigidCapsule, params)

Codea 4 is going to revamp the scene system, so this stuff will evolve, given that it’s designed to work both in code and with an editor

@John the viewers have a method scroll that seems to be unused. Can you clue me in on what it’s about?

And where did that damping function come from? I’m curious.

Thanks!

@RonJeffries scroll works with scroll events created by trackpad devices, such as the iPad folio keyboard, external mouse or when running on ARM Macs

The damping function was probably something I found on StackOverlow or something similar to emulate the way Apple does their zooming (when going beyond min or max zoom levels). This is why it uses log, which starts off roughly linear and goes starts to become asymptotic

ty for those. here are a few more.

what’s the relationship between models and blocks? does one use the other somehow?

is there a way to make a model cube show transparency like the glass block does?

Is there an example of adding cubes to build up a shape? (ah, found model:addElement in blocks.)

As I figure out what to write about, i’m finding I’d like to understand parallels like these. any help will be welcome.

Blocks and models are separate types of renderers. Blocks are treated differently because they are combined to create a single optimised mesh when rendering volumes and voxel chunks. This is why you can’t just add a model directly to a block. You can do this with a dynamic block type because it implicitly creates and manages an entity on creation/destruction, which can have a model on itself. There is an example of this in Block Library:

-- A storage block
function chest(capacity)
    local chest = scene.voxels.blocks:new("Chest")
    -- must be dynamic to have custom models
    chest.dynamic = true
    -- The transparent geometry type doesn't cull adjacent blocks, which we need to use since this block has gaps around the edges
    chest.geometry = TRANSPARENT

    -- this gets called when the block is created (i.e. when generated or placed by voxel drawing code or a player)
    function chest:created()
        -- cache the block's entity
        local e = self.entity

        -- create some additional entities for the separate lid and base and the lid's hinge
        self.base = scene:entity()
        self.base.parent = e
        self.base.position = vec3(0.5, 0.3, 0.5)
        local r = self.base.model = craft.model.cube(vec3(0.8,0.6,0.8)))
        r.material = craft.material(asset.builtin.Materials.Specular)
        r.material.diffuse = color(133, 79, 30, 255)

        self.top = scene:entity()
        self.top.parent = e
        self.top.position = vec3(0.1, 0.6, 0.1)
        local r2 = self.top.model = craft.model.cube(vec3(0.8,0.2,0.8), vec3(0.4,0.1,0.4)))
        r2.material = craft.material(asset.builtin.Materials.Specular)
        r2.material.diffuse = color(66, 47, 30, 255)
        self.angle = 0
    end

    -- update (called every frame) is used here to keep the chest lid animation in sync with the tween created during opening/closing animations
    function chest:update()
        self.top.rotation = quat.eulerAngles(0,  0, self.angle)
    end

    -- custom function that can be called by player code, in this case used to open and close the chest. No actual inventory management has been created as of yet but could be
    function chest:interact()
        if not self.open then
            self.open = true
            tween(0.6, self, {angle = 90}, tween.easing.backOut)
        else
            self.open = false
            tween(0.6, self, {angle = 0}, tween.easing.cubicIn)
        end
    end

    return chest
end

For transparency you can use:

local mat = myEntity.model:getMaterial(1)
mat.blendMode = NORMAL|ADDITIVE|MULTIPLY
mat.opacity = 0.5

You can do this on any material, this will do it for the default material on a model. You can also do it directly on entity.material if the entity has one

I think, depending on context, extending entity is fair game.

To me, the major strength of Codea—I’d go so far as to say the addictive part of Codea—is that it helps you do cool things fast.

I think @dave1707 regularly (and apparently effortlessly) spits out great examples of this. I just recently had a question about rotating entities. With under 60 lines of code @dave1707 made a demonstration that not only simulates flying over a scrolling landscape but also implements touch controls for changing direction.

That’s power. Codea power. I couldn’t do it. I hope one day I’ll be able to do it. And I think it’s good that Codea can do it. But trying to apply OOP everywhere is not gonna teach anyone how to do it.

I realize this is regurgitating an age-old argument over OOP, and I think that’s the point; Codea’s a living breathing embodiment of the argument itself. It can leverage the best of both worlds. It can also fall into the pitfalls of both. And I think maybe the takeaway is that the best Codea examples show off both.

[[ I will pick up the rig discussion in the editor thread where it’s more germane ]]

There are many ways to do things. Quick and dirty is sometimes OK. Objects aren’t necessary for clarity, though they can be one good way to provide clear code. We need clear code for our future selves; we need it for teammates; and we need it for programs that grow over time, which most substantial programs do.

One thing that objects can provide is hierarchy. Instead of a program that goes on and on like a single long sentence, we can have the equivalent of paragraphs, sections, chapters, and so on. Objects are one way, but not the only way.

Lua uses grouping of functions under global names, like math.sin and string.gsub. Since functions can be local, we can build the hierarchy that way. CodeaUnit does some of that.

There isn’t really any difference with objects, because class() is just a function that builds a table, and thing:foo(x) just means thing.foo(thing,x), which means thing["foo"](thing,x), no more and no less. There’s just convenient notation. Their ability to have instances, all of that, is just tables, names, and functions.

Me, I don’t care how anyone codes, if I don’t have to work with, or understand their code. If I do have to, I’d prefer to see them producing a suitable amount of hierarchy and modularity. In my own writing, which must be a hobby, since I don’t get paid for it, I try to show what choices I make, why I make them, and what happens when I do what I do.

As should we all. Rock on!

@John asked us to work with him on improving the official Craft documentation, and here’s an example of something almost completely undocumented: setting Voxel block textures.

Apparently it has to start with this command:


    scene.voxels.blocks:addAssetPack(assetPackName)

assetPackName being the string name of an asset pack in the root Codea directory. This seems to tell the Voxel system that all subsequent references to textures should be drawn from that pack. So for example this tells the Voxel system to use the built-in “Blocks” asset pack:

scene.voxels.blocks:addAssetPack("Blocks")

Then, to make a block use a given texture, you apparently have to first define a new type of block like this:


    scene.voxels.blocks:new(newBlockTypeName)

…which returns some kind of type definition object for the new block type. It seems important to capture that returned type object, because I’m not aware of any other way to get it later, and we need it for setting the texture of that block type.

So the initialization of a new block type would look something like this:

grass = scene.voxels.blocks:new("Grass")

And now that we have that type object, we can set its texture like this:

grass.setTexture(ALL, "Blocks:Dirt Grass")

A couple things to notice here:

  1. The first parameter is a global for which there is no documentation. In the lua bindings there’s this, presumably the relevant set of globals:

 // Facing Constants
        Lua::setGlobal(L, "NORTH", BlockFace::North);
        Lua::setGlobal(L, "EAST", BlockFace::East);
        Lua::setGlobal(L, "SOUTH", BlockFace::South);
        Lua::setGlobal(L, "WEST", BlockFace::West);
        Lua::setGlobal(L, "UP", BlockFace::Up);
        Lua::setGlobal(L, "DOWN", BlockFace::Down);
        Lua::setGlobal(L, "NONE", BlockFace::None);
        Lua::setGlobal(L, "ALL", BlockFace::All);

  1. The second parameter is the name of an image file, without an extension, but preceded by the asset pack name. So even though the asset pack was defined at the start of all this it seems necessary to also explicitly include its name here.

…and having done all that, now any block that gets assigned the “Grass” type will use the defined textures.

so…

If we were to set about helping John add some official documentation for these functions, how would we do it?

I’ve bounced off Learn Craft several times … would love a tutorial!

@BeeObLeo there’s a beginner’s tutorial series just started by @RonJeffries: https://ronjeffries.com/articles/-z022/codea-craft/-z02111/cocratu-001/

Thanks @UberGoober@BeeObLeo there’s not much there yet, but there’s some. Let me know here or on Twitter if you look and have issues, questions, suggestions …

R