CoCraTu 6: A change of direction.
Ooo, an accidental play on words. We’re going to change our cube’s direction, but also the direction of these articles.
My original model for these tutorials was to get close to the standard “cookbook” form, where the author lays out solutions that have been prediscovered and premasticated and premeditated, so they’re basically tell ‘em what you’re gonna tell ‘em, tell ‘em, then tell ‘em what ya told ‘em. I had in mind that I might present more than one program that did a particular thing, which I would probably have obtained by refactoring a flat design into a more structured one, but that it would all be presented as a fait accompli1.
I could still do that but it would take two or three or more days of discovery and search and asking questions on the forum and, generally, learning about Craft, before I could write the little section of tutorial that would hide all that learning and just provide the cookbook answer as if I had known it all along, and as if that’s all the reader needs to know.
That just doesn’t work for me. So I’m going back to my more common writing style, which is to start somewhere, code to somewhere else, then review what I’ve done and see whether there’s improvement to be had. I hope that works for you, all three of you who read these articles.
Thanks for watching me do my hobby.
Today, It Moves!
The previous tutorial rezzes a cube, which drops to a floor, and bounces around a bit. Today, we’re going to see whether we can influence what our cube does. I happen to know that we can. And I happen to know that there are issues as we’ll soon see.
Codea Craft physics includes two methods that can be used to influence what a rigidbody
does, documented here. In essence, it goes like this:
- applyForce – applies a vector representing force components to be applied to the body in question. Force will be applied at the center of the object, or optionally at a specific “world point”. This will tend to move the object in the direction indicated.
- applyTorque - applies torque to the three axes of a provided force vector. This will tend to rotate the object around the net axis defined by the vector.
We’ll try both of these. We’ll start a duplicate with my current copy of CoCraTu-005, named CoCraTu-006. Here’s the starting program:
-- CoCraTu-006
function setup()
scene = craft.scene()
--scene.physics.gravity = vec3(0,0,0)
createFloor()
createBox()
scene.camera:add(OrbitViewer, vec3(0,0,0), 20, 1, 20)
angle = 0
end
function update(dt)
scene:update(dt)
end
-- Called automatically by codea
function draw()
update(DeltaTime)
scene:draw()
end
function createBox()
local box = scene:entity()
local body = box:add(craft.rigidbody, DYNAMIC, 1) -- mass
body.restitution = 0.8
box:add(craft.shape.box, vec3(1,1,1))
box.model = craft. model.cube(vec3(1,1,1))
box.material = craft.material(asset.builtin.Materials.Specular)
box.material.map = readImage(asset.builtin.Blocks.Missing)
-- DELETE box.eulerAngles = vec3(math.random(10) - 5, math.random(10) - 5, math.random(10) - 5)
box.y = 5
return box
end
function createFloor()
local floor = scene:entity()
local body = floor:add(craft.rigidbody, STATIC)
body.restitution = 0.9
floor:add(craft.shape.box, vec3(25, 0.01, 25))
floor.model = craft.model.cube(vec3(25, 0.1, 25))
floor.y = -1.05
floor.material = craft.material(asset.builtin.Materials.Specular)
floor.material.map = readImage(asset.builtin.Blocks.Brick_Grey)
floor.material.offsetRepeat = vec4(0,0,25,25)
return floor
end
I’ve turned off the initial random rotation that I had, because I want the falling object to start out dead flat. We’ll leave the restitution in so that it will bounce. It starts like this:
We need access to the rigidbody
of our cube, to apply our force and torque, so we’ll just set a global for learning purposes:
function createBox()
local box = scene:entity()
BoxBody = box:add(craft.rigidbody, DYNAMIC, 1) -- mass
BoxBody.restitution = 0.8
box:add(craft.shape.box, vec3(1,1,1))
box.model = craft. model.cube(vec3(1,1,1))
box.material = craft.material(asset.builtin.Materials.Specular)
box.material.map = readImage(asset.builtin.Blocks.Missing)
box.y = 5
return box
end
And let’s add a parameter:
function setup()
scene = craft.scene()
--scene.physics.gravity = vec3(0,0,0)
createFloor()
createBox()
scene.camera:add(OrbitViewer, vec3(0,0,0), 20, 1, 20)
angle = 0
parameter.action("Twist", twist)
end
function twist()
BoxBody:applyTorque(vec3(0,40,0))
end
The value 40 was picked by increasing the value until the box did what I wanted. There may be some more intelligent way to do it, involving moments or inertia or something.
Now we can tap the Twist button and the cube will rotate around its y (vertical) axis:
Did you notice that after the box stopped bouncing, it was no longer responding to my taps? This confused me for a very long time, until I realized that a rigidbody can apparently go to sleep. I learned how to display its status, so let’s do that:
function draw()
update(DeltaTime)
scene:draw()
if BoxBody.awake then
text("awake", 400,100)
else
text("asleep", 400,100)
end
end
Now we can see that after the box stops bouncing, it falls asleep. After that, it does not respond to Twist.
There’s also a flag–you’ll love this–named sleepingAllowed
. If we set it to false, we can find out that the object does not go to sleep. However, our display code now shows it as asleep all the time! Something is rotten here.
function createBox()
local box = scene:entity()
BoxBody = box:add(craft.rigidbody, DYNAMIC, 1) -- mass
BoxBody.restitution = 0.8
BoxBody.sleepingAllowed = false
box:add(craft.shape.box, vec3(1,1,1))
box.model = craft. model.cube(vec3(1,1,1))
box.material = craft.material(asset.builtin.Materials.Specular)
box.material.map = readImage(asset.builtin.Blocks.Missing)
box.y = 5
return box
end
Bizarre. We can tap our button and the cube usually moves, even though the awake flag now signals asleep. I suspect that there’s some kind of bug, and I’ve filed a report.
Meanwhile we can also try applying a force to see what happens:
function setup()
scene = craft.scene()
--scene.physics.gravity = vec3(0,0,0)
createFloor()
createBox()
scene.camera:add(OrbitViewer, vec3(0,0,0), 20, 1, 20)
angle = 0
parameter.action("Twist", twist)
parameter.action("Force", force)
end
function force()
BoxBody:applyForce(vec3(0,800,0))
end
function twist()
BoxBody:applyTorque(vec3(0,40,0))
end
Now, tapping the Force button tends to throw the cube upward. The effect seems a bit random, but of course the resultant motion is a function of both our upward force, and the other motion of the cube.
To really understand what’s going on here, we need to know the time interval over which the force or torque are applied, since the change in velocity is proportional to the time the force is applied. I’ve inquired of the Codea powers that be, and will report here what I find out.
For now, we’ve learned a bit, and we’ll think about how to “apply” this learning in upcoming sections. Let’s sum up.
Summary
Codea Craft allows us to applyForce
or applyTorque
to a rigidbody
, and the physics engine changes that object’s motion as a function of the force applied and the residual motion of the object. The effect appears to be somewhat random: we need information that does not seem to be available yet.
Craft also provides the sleepingAllowed
flag, and the awake
flag on rigidbody
. It appears that setting
body.sleepingAllowed = false
will cause the body never to sleep–but to report always that it is asleep. I feel pretty sure that that’s a bug.
It’s also apparently the case that when a body is asleep, applying force or torque will not wake it up. I’ve reported a bug on that as well.
We’ll see in future sessions what use we can make of these functions. I think they’ll be useful even if they are a bit random.
See you then, I hope!
-
Not to be confused with the early sixties Fiat Accompli, which was not well thought of in the automotive market. ↩