Codea Craft Tutorial 001
Converging on a plan, I decide to do a series of tutorials. Herein, an explanation of what I mean and intend, and the first one.
Introduction
The exploration mode in something like Codea Craft is just too painful to write about, and must surely be painful to read. So I’ve decided to try writing a series of tutorials about Codea Craft. They will consist of examples that work, without the innumerable mistakes and false paths that I actually take to get there.
These tutorials will start out very basic and get more advanced as time goes on. If you’re already pretty good with Codea Craft, this and other early articles may be too basic for you. However, if you check them out, please provide feedback on how they could be improved, or on any errors I may make.
To the extent that I show the exploration process, which may sometimes be valuable, I’ll do it in sidebars to the tutorial, so that they’ll be easier to skip for those who want to know one way to do something, rather than several ways not to.
Over time, I’m sure I’ll be building CodeaUnit tests into the process, and of course there will be a certain amount of refactoring. However, at this writing, I do not intend to be doing one growing application, but instead, a series of small complete programs. I’m sure that quite often, a given program will start out based on one from before, but we’ll be creating a series of separate programs.
Feedback via Twitter (@RonJeffries) or email (ronjeffries at acm dot org) are always welcome. Or suggestions, ideas, corrections, questions. Whatever.
I think the short name for the series will be CoCraTu, or, very likely cocratu1.
Let’s get to it.
In the Beginning, There Was the Cube
In this first tutorial, we’ll display a cube on the Codea Craft screen, and give it a bit of behavior.
We will get our first introduction to Codea Craft’s scene
, entity
, material
, and map
, as well as a glance at the eulerAngles
of the entity. And we’ll just barely touch the camera
.
At the end of this chapter, I’ll link to some reading that relates to what we’ve seen here. And I’ll issue a small challenge.
Create a New Project
Use Codea’s + button to create a new project. Be sure to use the Craft template:
The editor will open on a screen that looks like this:
-- CoCraTu-001
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
local e = scene:entity()
end
function update(dt)
-- Update the scene (physics, transforms etc)
scene:update(dt)
end
-- Called automatically by codea
function draw()
update(DeltaTime)
-- Draw the scene
scene:draw()
end
I’ll leave the irritating comments in the template for a while, but my own practice is to work without them once I get the picture of what’s happening.
What’s This Code Doing?
- Scene
- Every Codea Craft program has at least one scene, and there is, as far as I know, only one scene at a time.
- Entity
- A scene has one or more entities, that is, “things” that are managed and displayed by Craft.
- Draw
- Codea calls the function
draw()
frequently, at the fastest speed it can manage, choosing speeds 1/120 sec, 1/60, 1/30. The frequency of its calls depends on the speed of your device and how complex the scene is. - Update
- Our responsibility is to call the function
scene:update(deltaTime)
on every draw cycle. By convention, as we see here, we write anupdate
function in Main and call it. Inside that function we can make any changes we want to the situation, and then callscene:update
. -
The changes we might make, as we’ll see below, might move objects, rotate them, or change our view of them.
It’s easy to make mistakes when programming, and very easy to make mistakes with Codea Craft. Therefore, we run our programs very often, and when we add tests to them, we run those often as well. When we run now, we get an entirely blank screen, showing the Codea console on the left.
Now let’s do the least we can to draw our first Craft object, a cube.
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
local e = scene:entity()
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
scene.camera.z = -4
end
This give us this picture:
Lets look at the three new lines we wrote:
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
scene.camera.z = -4
The model
is the shape of the entity. Craft has a few built-in models, including the cube and the “isosphere”. Our first line above says that our cube should have dimensions 1x1x1. What are the units? For now, let’s not worry about that. The question’s time will come, I’m sure. They’re just the units of things on the display.
Where is it? Our cube defaults to location (0,0,0). That will be important below.
The material
is analogous to the notion of material in Blender or other 3D drawing applications. I think of it as a kind of canvas wrapped around the shape, on which we can paint, with the capability Codea calls map
. We’ll see that soon. Here, we used the built in asset Basic.
In Codea, if we touch that line, we get this picture:
We can scroll to find Materials:
And touching that, we get a selection of materials:
I touched Basic, which was how that line got filled in.
Without a material, the object won’t be visible at all. You can test that by commenting out the material
line.
The final line is
scene.camera.z = -4
We’ll see more about cameras later on. Here, we’re using the default camera, which starts out at location (0,0,0). But our cube also sits at 0,0,0, which means the default camera is inside the cube, which means that we can’t see the cube. (It has no paint on the inside.)
So we move the camera toward ourselves. Yes, that’s right, the z axis is “out to in” on the screen. Negative z is toward you, positive is away from you.
So this program:
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
local e = scene:entity()
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
scene.camera.z = -4
end
Creates a unit cube at (0,0,0), gives it a basic material, and looks at it from four units out. No surprise, it looks like a square, since we’re looking at it straight on.
Let’s spiff this up a bit. In addition to its position, an entity has a rotation, which can be set by setting its eulerAngles
property. The rotation
is a quaternion. No one wants to create quaternions directly, so eulerAngles is a blessing.
Let’s rotate our cube by 45 degrees and see what we get.
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
local e = scene:entity()
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
e.eulerAngles = vec3(0,45,0)
scene.camera.z = -4
end
We get this picture:
It’s fairly easy to see that this is a view of our plain cube at 45 degrees. Yes. eulerAngles
is in degrees, not radians.
Paint the Cube
The cube is so plain, however, that it is quite difficult to make out where its edges are. We can improve that.
Add this line after the e.material
line:
e.material.map = readImage()
Then touch the parens, and you should see the asset browser again:
This time select Blocks, which are convenient textures for the blocks we’ll be using in later tutorials. I selected the Missing one:
The code is now this:
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
local e = scene:entity()
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
e.material.map = readImage(asset.builtin.Blocks.Missing)
e.eulerAngles = vec3(0,45,0)
scene.camera.z = -4
end
And the picture is this:
Now we can see that we really have a cube. You should wonder why our map
has appeared on more than one face of the cube. That will be because of how the material is defined, with each face of the cube using the whole material. Materials and the textures they contain are an entire topic. We may touch on them in future chapters, but they are more the domain of your favorite 3D object making program.
Rotate the Cube
But let’s go a bit further with our cube. We now know how to set the rotation of the cube. Let’s set it dynamically!
We’ll need access to our entity e
, in update, so we remove the local
in setup
, we add an angle to update, and we do the update:
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
e = scene:entity()
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
e.material.map = readImage(asset.builtin.Blocks.Missing)
e.eulerAngles = vec3(0,0,0)
scene.camera.z = -4
angle = 0
end
function update(dt)
-- Update the scene (physics, transforms etc)
angle = angle + 1
e.eulerAngles = vec3(0, angle, 0)
scene:update(dt)
end
In update
, we increment the angle and set the entity’s eulerAngles` to cause it to rotate around its y (vertical) axis.
The result is this:
Perfect! As our final trick, let’s make it tumble:
function update(dt)
-- Update the scene (physics, transforms etc)
angle = angle + 1
e.eulerAngles = vec3(angle/10, angle, angle/5)
scene:update(dt)
end
Let’s view the entire program and review:
-- CoCraTu-001
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
e = scene:entity()
e.model = craft.model.cube(vec3(1,1,1))
e.material = craft.material(asset.builtin.Materials.Basic)
e.material.map = readImage(asset.builtin.Blocks.Missing)
e.eulerAngles = vec3(0,0,0)
scene.camera.z = -4
angle = 0
end
function update(dt)
-- Update the scene (physics, transforms etc)
angle = angle + 1
e.eulerAngles = vec3(angle/10, angle, angle/5)
scene:update(dt)
end
-- Called automatically by codea
function draw()
update(DeltaTime)
-- Draw the scene
scene:draw()
end
I want to emphasize this line:
scene.camera.z = -4
This line was critical to seeing anything on the screen. Since we were drawing our cube at (0,0,0), the default camera position, which is also (0,0,0), was inside the cube and couldn’t see it. Do not forget this. If your Craft program shows nothing, or something strange, it’s likely that you need to adjust the camera. We’ll do more about that soon.
What we’ve learned today is that a Codea Craft program has a scene
, and the scene
can have an entity
. (It can actually have any number.)
The entity
has a model
, which can be a cube. (It can actually be a very complex shape.)
The model
has a material
, a canvas on which we can place colors and textures (pictures).
The model material
has a map
, a picture that appears on the canvas. The material defines how that picture paints the model. In our case, the single picture paints all the cube faces. We’ll see that there are many other possibilities.
The entity has a location in 3D space. We have only used a single location, (0,0,0). The entity also has a rotation in 3D space. We set that at first to 45 degrees around the Y (vertical) axis. Then we rotated it around that axis as the program ran. Finally we rotated our cube around all three axes.
The scene, containing all the entities, needs to be updated every time Codea draws the screen, which it does many times per second. By convention, we provide a function update
, which receives the time since the last update as a parameter, and we call update
from the draw
function, which Codea calls automatically.
In our program, we keep track of an angle
, which we just increment on each call. We use it to set the euler angles of our cube, which makes it tumble on the screen.
Refactoring
Even as simple an example as this could be improved. I would propose that we rename our entity from e
to cube
. In my personal conventions, I make global variables begin with upper case, but the Codea Craft convention would have us name scene
in lower case. That notwithstanding, I propose that we should name our own things with upper case names:
-- CoCraTu-001
function setup()
-- Create a new craft scene
scene = craft.scene()
-- Create a new entity
Cube = scene:entity()
Cube.model = craft.model.cube(vec3(1,1,1))
Cube.material = craft.material(asset.builtin.Materials.Basic)
Cube.material.map = readImage(asset.builtin.Blocks.Missing)
Cube.eulerAngles = vec3(0,0,0)
scene.camera.z = -4
angle = 0
end
function update(dt)
-- Update the scene (physics, transforms etc)
angle = angle + 1
Cube.eulerAngles = vec3(angle/10, angle, angle/5)
scene:update(dt)
end
-- Called automatically by codea
function draw()
update(DeltaTime)
-- Draw the scene
scene:draw()
end
Reading
In the Codea Craft documentation I’d recommend now reviewing the descriptions of scene, entity, and material. You can find those on line, or inside Codea’s built-in reference.
And, of course, feel free to study more and to experiment.
Challenge
Can you make our cube move around a bit, back and forth or side to side? Hint: entity has x, y, and z coordinates, just like the camera. Be careful!
Feedback
Feedback on this tutorial is welcome. I’ll use it to improve this one, and subsequent chapters in the series.
-
CoCraTu is pronounced in the Scottish fashion, Co-Cra-Tu. Hi, Alistair! ↩