I begin to try to make sense of Codea’s ambitious and powerful Craft components.
I am minded of one of my favorite quotes, from Mary Doria Russell:
Wisdom begins when we learn the difference between “That makes no sense” and “I don’t understand”.
Codea, the marvelous little Lua system for iOS, has a very comprehensive recent addition called “Craft”. I suspect this is named in reference to Minecraft, but don’t know that for sure. Certainly Craft includes some voxel-building capabilities, and much more.
Codea comes with lots of programming examples, including more than 15 under the Craft heading. Many of these are very impressive little programs in their own right. Nonetheless, I find it difficult to build up my kind of understanding of how Craft works, and the examples are not usually programmed in the style I tend to prefer.
Therefore, for something interesting to do, I’m going to try to learn how Codea Craft works, and to write up my understanding, together with examples. I’ll try to work toward examples that suit my sense of how such things should be built.
I should note right here that the examples given are quite good, and I have no legitimate argument about their quality. Some of the code is particularly nice, as I’ll write about as we go along in these articles. My way is better for me, and I’m hoping that together with the examples in Codea, the combination of ideas will be useful to someone.
I have no illusions that anyone will ever read these articles, but I’ll keep writing them as long as I’m having fun.
Codea Craft Basics
You may wish to check out Codea’s reference material. It’s fairly good but there are issues. Because it’s compact, the documentation is short on explanations. Because it’s highly modular and indexed, it’s difficult to form a narrative in my mind. And for some reason, as we’ll see in the provided examples, there are capabilities not described in the reference that are actively used in the examples.
Still, it’s good stuff and it’ll be the basis of much of what we do here, together with targeted examples. Let’s start with a quote from the reference:
Craft provides extended capabilities to Codea that make it much easier to create 3D scenes, physics and game logic. Craft introduces a number of new classes. These include craft.entity, which is the basic building block of 3D scenes.
Several systems are introduced, including: * craft.scene - The core class in craft, draws and updates all other objects and systems * craft.physics - Physics settings and helper functions * craft.voxels - Streaming voxel terrain, block types and helper functions * craft.ar - Augmented Reality, world tracking and helper functions
Each system can be used to access built-in objects, change settings or trigger specialised features.
scene includes a number of general parameters for the, well, scene being drawn, such as the color of ambient lighting, the location of the camera, and the like. Mostly, however, I think of it as a collection of things in the world. Craft calls those “entities”.
scene includes a
draw function, which is called repeatedly as in all of Codea’s graphics. The convention is that
draw will call
update, which (somehow) gets passed to any elements that need to be updated. I’m saying “elements” here, because I’m not sure at this point how the update is supposed to connect to one’s own objects. I suspect that it’s up to the programmer to maintain a collection of objects, update them, and have each one tell its associated entity where to go, and so on. We’ll find out.
As I mentioned, I think of the
scene as primarily a collection of entities. An entity is a craft-drawable … thing. The document says:
Entities are flexible objects that can be customised to change their appearance and behaviour. You can customise an entity by adding components. Each type of component serves a different purpose. Renderer components change entity appearance, physics components give entities physical behaviour.
It appears that
entity are both classes. You use the colon notation to talk to them. For example, without explanation:
scene = craft.scene() -- note the dot entity = scene:entity(). -- note the colon entity:destroy() -- note the colon.
However, these objects are generally manipulated by direct access to their member variables, as in
entity.model = craft.model.cube(vec3(1,1,1)) entity.material = craft.material("Materials:Standard")
This saves a lot of setters and getters, so it’s not an unreasonable thing to do.
Since we’ve mentioned
material, here’s what I’ve gleaned so far. The model is the shape of the thing. Above we have a cube of dimensions <1,1,1>. The shape can be pretty much anything: Codea includes a library of 3d objects that you can use, mostly in the style of Minecraft and other simple 3d games. And apparently it can import
.obj files such as are produced by 3D building apps such as Blender. Perhaps we’ll find out about that as time goes on.
Anyway, the model is the geometry of the entity. The material is an abstraction of what it’s made of. Materials are basically shaders, controlling the object’s shine, bump maps and such. I’ve not dug into this at all, and there are only a few basic Materials provided.
entity can have children, i.e. other entities of which it is the parent. As I read the documents, a child’s parameters like
position are relative to the parent. We’ll find out. Right now I have no immediate plans to have any more children.
Some Disappointment with entity:add
entity includes a method
add that can be used to add a class instance to any entity. The idea seems to be that this is where one might build the update logic for a given kind of thing. Unfortunately, the example in the documents just plain doesn’t work. I’ve posted on the forum for a little help, but meanwhile let’s see about building an example based on something from the built-in examples.
The example named “Cameras” in the Learn Codea example looks like a reasonable place to start. It looks like this:
-- Craft Camera function setup() -- Create a new craft scene scene = craft.scene() scene.sky.active = false createGround(-1.125) -- The scene.camera entity only lets you control the camera postion / rotation -- The camera component has settings for things like the field of view and helper methods cameraSettings = scene.camera:get(craft.camera) myEntity = scene:entity() myEntity.model = craft.model("Blocky Characters:Orc") myEntity.y = -1 myEntity.z = 0 myEntity.scale = vec3(1,1,1) / 8 myEntity.eulerAngles = vec3(0, 180, 0) scene.camera.z = -4 parameter.number("CameraX", 0, 360, 0) parameter.number("CameraY", 0, 360, 0) parameter.number("FieldOfView", 45, 90, 60) parameter.boolean("Ortho", false) parameter.number("OrthoSize", 1,10,5) end function update(dt) if CurrentTouch.state == MOVING then CameraX = CameraX - CurrentTouch.deltaX * 0.25 CameraY = CameraY - CurrentTouch.deltaY * 0.25 end cameraSettings.fieldOfView = FieldOfView -- Orthographic mode cameraSettings.ortho = Ortho cameraSettings.orthoSize = OrthoSize --Set the camera rotation to look at the center of the scene scene.camera.eulerAngles = vec3(CameraY, CameraX, 0) scene.camera.position = -scene.camera.forward * 5 -- Update the scene (physics, transforms etc) scene:update(dt) end -- Called automatically by codea function draw() update(DeltaTime) -- Draw the scene scene:draw() -- 2D drawing goes here drawStepName() end function PrintExplanation() output.clear() print("The scene contains a built-in camera entity that you can move around") print("Since the camera is an entity you can use all the same properties to move it as the previous example") end
This produces a view of a block character “Orc”, standing on a platform. It looks like this:
My plan is to copy the main idea from the faulty
add example to add a simple object to move my orc around a bit on the platform. I should mention, by the way, that in Codea, x is left to right, y is up and down, and z is forward and back. I’m sure there are reasons for that.
I’ll begin by creating a little class and giving it an update method, and then I’ll add an instance to the entity that draws my orc. My first implementation will just print the orc’s position. I expect that to be <0, -1, 0> based on my reading of the code above.
RonMover = class() function RonMover:init(entity) self.entity = entity end function RonMover:update(dt) print(self.entity.position) end
There’s my class, and here’s where I patched it in:
myEntity = scene:entity() myEntity.model = craft.model("Blocky Characters:Orc") myEntity.y = -1 myEntity.z = 0 myEntity.scale = vec3(1,1,1) / 8 myEntity.eulerAngles = vec3(0, 180, 0) myEntity:add(RonMover)
After I work around the Codea problem of not always updating your code, that prints (0,-1,0) as expected. Now let’s move this guy around a bit. How far should I move him? Well, the floor should give me a clue as to how big an area is on screen. We find that, after some searching, here:
-- Creates the ground using a box model and applies a simple textured material function createGround(y) local ground = scene:entity() ground.model = craft.model.cube(vec3(4,0.125,4)) ground.material = craft.material("Materials:Specular") ground.material.map = readImage("Blocks:Dirt") ground.material.specular = color(0, 0, 0, 255) ground.material.offsetRepeat = vec4(0,0,5,5) ground.y = y return ground end
So the floor is 4 meters wide and deep, and an eighth of a meter thick. I’ll move my guy back and forth a couple of meters in the x direction. Now some numbers.
The update is called with a parameter dt, delta time, which is the amount of time in seconds since the last update. You’re expected to use that value to adjust your moves to keep apparent speed constant. It’s nominally 50 times a second, I think.
Let’s move him between -2 and 2, at maybe 1 meter per second. So the step size to use is, if I’m thinking clearly, 1*dt. And if the value of x goes outside -2 to 2, we want to negate that speed so as to move back. Here goes:
RonMover = class() function RonMover:init(entity) self.entity = entity self.speed = 1 end function RonMover:update(dt) -- print(dt) local x = self.entity.x if math.abs(x) >= 2 then self.speed = -self.speed end x = x + self.speed*DeltaTime -- dt is coming back nil FSR self.entity.x = x end
This took a bit of time. I expected that
dt was going to equal DeltaTime, but it was coming back nil. Since DeltaTime is a global, I just used it directly and the effect is as intended:
There seem to be issues here. Sometimes the orc vanishes from the screen when I stop and start the code, or reset it. Maybe I’ve not linked my object in correctly. We’ll try to sort that out next time. But we’ve made a little progress.
Does this seem chaotic to you? It should. The reason is that I’m describing what really happens to me when I try to learn some new framework, including all the confusion that arises. I’m not trying to show you a perfect, clean path with all the mistakes and missteps taken out. What we see here is what really happened to a real practitioner trying to fumble his way through something new.
I hope you’d do better, but I suspect you’ll encounter similar issues as you work with things like this.
Anyway, we’ve begun to get a handle on some things. We’ve managed to add a class instance to an
entity, so that in addition to letting Codea Craft draw it, we get an update call where we can move the thing around. That’s a good first step by my lights.
On the darker side, since the simple example flat doesn’t work, I’ve embedded my tiny solution in a pretty big demonstration example. Next time, I think I’ll try to disassemble that example and come up with something of a reasonable size.
Right now, however, I’m off to find lunch. See you next time!