A couple of new entities lead to a design concern.

Begin today by making a copy of your CoCraTu-002 program. I cleverly named mine CoCraTu-003.

Our plan is to create a new entity, called Wanderer, who circles inside the Adventurer’s path, going the other direction at a different speed. We change `setup` and `update`:

``````function setup()
scene = craft.scene()

Wanderer = createWanderer()
createFloor()
angle = 0
end

function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond
Adventurer.eulerAngles = vec3(0, angle + 90, 0)
scene:update(dt)
end
``````

The above is pretty rote. The main difference is that I converted the angle to radians right rather than call `math.rad` so many times. We’ll deal with the asymmetry with Adventurer as we continue. I didn’t set Wanderer’s rotation, because I don’t know what it needs to be.

You’ll notice that I’ve not even written `createWanderer`. I don’t know the details, but I know that I want it. To write it, I of course copy Adventurer’s creation:

``````function createAdventurer()
end
``````

I get this:

``````function createWanderer()
local wanderer = scene:entity()
wanderer.model = craft.model(asset.builtin.CastleKit.knightRed_obj)
wanderer.scale = vec3(1,1,1)/8
wanderer.y = -1
wanderer.eulerAngles = vec3(0,0,0)
return wanderer
end
``````

That gives us a little square knight going around the other way, always facing us. We adjust his angle:

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond
Adventurer.eulerAngles = vec3(0, angle + 90, 0)
Wanderer.eulerAngles = vec3(0, 90-angle/2, 0)
scene:update(dt)
end
``````

That gives us what we wanted:

I am irritated by the fact that my clever trick to save calls to math rad forced me to compute `- angle/2` again. We’ll improve that later. For now, we have two movers, moving as intended.

Child Entity

An entity can have other entities as their parent. The position and rotation properties of the child are relative to the parent. We’ll give our adventurer a daydream.

``````function setup()
scene = craft.scene()

Wanderer = createWanderer()
createFloor()
angle = 0
end

function createDaydream(parent)
local daydream = scene:entity()
daydream.model = craft.model(asset.builtin.Watercraft.watercraftPack_018_obj)
daydream.y = 8*2.25
daydream.x = 8*0.5
daydream.parent = parent
return daydream
end
``````

That gives our Adventurer a dream of a boat a bit above his head:

Note the `8*2.25` and `8*0.5` above. Because our Adventurer is scaled down to 1/8, dimensions relative to him are only 1/8 of their “real” values. So to move the boat up above his head, we had to set it, not 2.25 units up, but eight times that.

I think the boat should move a bit as he walks, rather than just stay top left.

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond
Adventurer.eulerAngles = vec3(0, angle + 90, 0)
Wanderer.eulerAngles = vec3(0, 90-angle/2, 0)
scene:update(dt)
end
``````

We adjust the daydream’s x, relative to the adventurer, between -2 and 2 units, also relative to the adventurer. Now the boat moves back and forth above his head:

Done … or Are We?

We’ve accomplished today’s goal, to add a couple of moving objects to our scene, including one that behaves as if it is attached to the adventurer, and one that moves independently.

It should be clear that with some difficulty, we could create a model out of cubes (and spheres, and even other models) and move its parts around. We could, in principle, attach an arm to a robot, and calculate its angles to move it around. It should also be clear that that would be tedious, though not seriously challenging.

We’re not going to go there, at least not soon. To do a real job of creating creatures and animating them is beyond our scope.

Within our scope, however, is to consider the design of this program. Frankly, it’s getting messy and we just have a floor and three moving objects.

``````function setup()
scene = craft.scene()

Wanderer = createWanderer()
createFloor()
angle = 0
end

function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond
Adventurer.eulerAngles = vec3(0, angle + 90, 0)
Wanderer.eulerAngles = vec3(0, 90-angle/2, 0)
scene:update(dt)
end

-- Called automatically by codea
function draw()
update(DeltaTime)

-- Draw the scene
scene:draw()
end

end

function createDaydream(parent)
local daydream = scene:entity()
daydream.model = craft.model(asset.builtin.Watercraft.watercraftPack_018_obj)
daydream.y = 8*2.25
daydream.x = 8*0.5
daydream.parent = parent
return daydream
end

function createWanderer()
local wanderer = scene:entity()
wanderer.model = craft.model(asset.builtin.CastleKit.knightRed_obj)
wanderer.scale = vec3(1,1,1)/8
wanderer.y = -1
wanderer.eulerAngles = vec3(0,0,0)
return wanderer
end

function createFloor()
local floor = scene:entity()
floor.model = craft.model.cube(vec3(6, 0.1, 6))
floor.y = -1.05
floor.material = craft.material(asset.builtin.Materials.Specular)
return floor
end
``````

We see lots of duplication in the code above, and duplication almost always represents an opportunity for better code. We also see a growing blob of code, and it seems that it would grow more and more as we add entities and behavior:

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond
Adventurer.eulerAngles = vec3(0, angle + 90, 0)
Wanderer.eulerAngles = vec3(0, 90-angle/2, 0)
scene:update(dt)
end
``````

If you’re like me, you might resort to some spacing to make that more clear:

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond

Adventurer.eulerAngles = vec3(0, angle + 90, 0)

Wanderer.eulerAngles = vec3(0, 90-angle/2, 0)

scene:update(dt)
end
``````

You might notice that the angle handling is done two different ways and decide to do it in just one way, to avoid confusion later:

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond

Adventurer.eulerAngles = vec3(0, angle + 90, 0)

local wAngle = -angle/2
Wanderer.eulerAngles = vec3(0, wAngle, 0)

scene:update(dt)
end
``````

We can see now that we have three functions here, waiting to be born:

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond

updateWanderer(-angle/2)
updateDaydream(angle)

scene:update(dt)
end

Adventurer.eulerAngles = vec3(0, angle + 90, 0)
end

function updateDaydream(angle)
end

function updateWanderer(angle)
Wanderer.eulerAngles = vec3(0, 90+angle, 0)
end
``````

Note that I had to change the eulerAngle for the wanderer, since I negated the angle in the call.

These functions refer to the globals Adventurer, Daydream, and Wanderer. It would be better if they didn’t. We can pass them in as arguments:

``````    updateAdventurer(Adventurer, angle)
updateWanderer(Wanderer, -angle/2)
updateDaydream(Daydream, angle)

adventurer.eulerAngles = vec3(0, angle + 90, 0)
end

function updateDaydream(daydream, angle)
end

function updateWanderer(wanderer, angle)
wanderer.eulerAngles = vec3(0, 90+angle, 0)
end
``````

The `updateAdventurer` and `updateWanderer` are almost identical. We could make them identical if we were to pass in the radius to each.

``````function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond

updateWanderer(Wanderer, 1, -angle/2)
updateDaydream(Daydream, angle)

scene:update(dt)
end

adventurer.eulerAngles = vec3(0, 90 + angle, 0)
end

wanderer.eulerAngles = vec3(0, 90 + angle, 0)
end
``````

Now, since they’re the same, we can remove one of those updates and rename the other:

``````    updateCircleMover(Adventurer, 2, angle)
updateCircleMover(Wanderer, 1, -angle/2)
updateDaydream(Daydream, angle)

entity.eulerAngles = vec3(0, 90 + angle, 0)
end
``````

With a few simple transformations, we’ve eliminated some code, improved clarity, and even provided a potentially useful new function that moves things in circles. It’s already used twice, so it’s a righteous change.

There’s more that we could do, and in a future tutorial we probably will. The creation functions look rather similar as well. There might be some value to be found there.

But this time we’ve added two new objects while adding only a bit of code. We’ve made the program more clear than it was, and therefore better able to be modified to have more capability.

Here, for reference, is the whole program.

``````-- CoCraTu-003

function setup()
scene = craft.scene()

Wanderer = createWanderer()
createFloor()
angle = 0
end

function update(dt)
local degreesPerSecond = 360/5
angle = angle + dt*degreesPerSecond

updateCircleMover(Wanderer, 1, -angle/2)
updateDaydream(Daydream, angle)

scene:update(dt)
end

-- Called automatically by codea
function draw()
update(DeltaTime)

-- Draw the scene
scene:draw()
end

end

entity.eulerAngles = vec3(0, 90 + angle, 0)
end

function createDaydream(parent)
local daydream = scene:entity()
daydream.model = craft.model(asset.builtin.Watercraft.watercraftPack_018_obj)
daydream.y = 8*2.25
daydream.x = 8*0.5
daydream.parent = parent
return daydream
end

function updateDaydream(daydream, angle)
end

function createWanderer()
local wanderer = scene:entity()
wanderer.model = craft.model(asset.builtin.CastleKit.knightRed_obj)
wanderer.scale = vec3(1,1,1)/8
wanderer.y = -1
wanderer.eulerAngles = vec3(0,0,0)
return wanderer
end

function createFloor()
local floor = scene:entity()
floor.model = craft.model.cube(vec3(6, 0.1, 6))
floor.y = -1.05
floor.material = craft.material(asset.builtin.Materials.Specular)