Codea Game - 2
ZOK, my little game-to-be can draw a couple of lines, and keep them more or less inside the frame. I think the scaling is a bit naff, and it certainly doesn't stop a wild line from drawing itself outside the box, but it's good enough for now.
Wait, you might wonder. Why is this "good enough"? It's not totally general, not entirely bulletproof, certainly not near perfect. Why is it "good enough"?
My reasoning is this: the issues are rather well encapsulated. The code is not perfect, but it is all in one place. This means that as my needs change, I'll only have to improve things in one place. I won't be faced with an expanding need to fix things all over.
I was tempted to say "it's early days, and I can leave it for now". But that's not my thinking. Even in early days my main focus is getting things in the right places. If its early days and the code isn't organized as well as I can see how to organize it, I keep pushing toward better organization.
That doesn't mean I create whole hierarchies of objects of surpassing generality. I'm just trying to see what the code is telling me, and to respond to it.
And the code is telling me something now. Inside my Frame object, I'm just drawing a couple of lines. What should happen in the long run, surely, is that the draw() in Frame will call draw() on some kind of subordinate object or objects. The Frame is doing things at two levels now. It explicitly draws itself, and it explicitly draws this subordinate thing, represented right now by the line. (It has other flaws as well. For example there are a few chunks of code in it, doing separate ideas. This calls for refactoring too. Let's leave that till later, but try not to forget.)
For now, I plan to invent the polyline. A polyline is an object that draws many lines. Typically it has an ordered collection of points and it draws from the first to the second, from second to third, and so on. The first question to me is always "how will I use this", not how will I build it. I'll build it once, and use it many times, so I want its usage to be easy. Here's my proposed usage:
local poly1 = Polyline({0,0}, {75,100}, {400,300})
local poly2 = Polyline({0,0}, {75,75}, {400,250})
stroke(0, 255, 4, 255)
poly1:draw()
stroke(255, 0, 0, 255)
poly2:draw()
<p>Create a couple of polylines, change their stroke color, have them draw themselves. Easy enough.</p>
Even here, though, let's be aware of some issues. In the long run, the polylines will probably not be created in draw(). Not only is that inefficient, it probably doesn't make sense in the overall design. The things we draw will be part of a simulation that is running, so they will probably have independent existence. I don't know how that's going to happen, so other than noticing, I'm not going to do anything about it. In addition, the Frame is deciding what color the lines will be. That may or may not be a good thing. Again, we'll wait and see.
Then there's the polyline creation itself. I've decided to use an arbitrary number of pairs of numbers. The pairs, in Codea, are little tables. In my mind, though, they represent points, 2D vectors. I am losing a bit of abstraction by just using tables, when the "real idea" is a vector.
I actually tried vectors first. Codea has an object, Vec2, that could do the job. But the creation call looks like this if I use them:
Polyline(Vec2(0,0), Vec2(75,10), Vec2(400,300))
<p>That’s a pain to type, and error-prone. I decided my constructor should be more convenient. Thus the tables.</p>
By the way, I really do try to think about this stuff all the time. I try to remain conscious of the decisions I'm making, and to make my choices consciously. You might think this would slow me down and make the work tedious. On the contrary, it keeps me interested, it helps me learn what my program is, and it keepsmth code clean. Thinking more speeds me up. I hope that putting it that way makes it clear that thinking while programming is better than not thinking.
OK, let's look at Polyline:
Polyline = class()
function Polyline:init(...)
self.polyline = arg
end
function Polyline:draw()
local p = self.polyline
for i = 2, #p do
local start, finish = p[i-1], p[i]
line(start[1], start[2], finish[1], finish[2])
end
end
<p>To do this, I tried the Lua “…” notation for a variable argument list. You put that in the definition, and then args is a table of the arguments. In our case, a table of tables of two elements each.</p>
We save the table in our member variable polyline. Then in draw(), we draw the lines. We loop starting with the second point to the end. In each iteration, we set start to the preceding point, and finish to the current. Then we call line to draw that line segment.
A slight irritation is that line() wants four variables, not two tables. I looked in vain for a Lua way to unfold two tables into a single table and decided just to do it this way. There are table libraries with map and reduce and all that, but come on! we're just drawing a line. Subscripting seems simpler. Especially since we aren't even sure if Polyline is a keeper. It's just the foil we used to start moving subordinate drawable objects out of Frame.
And it does work. It looks like this:
Sweet. That's enough for now. With a few lines of code, we have moved the design in the direction of a Frame containing other things to draw. Right now, the Frame still knows too much: it knows these are polylines. Maybe next time we'll try to make it a bit more indifferent to what it's drawing. Or maybe we'll do something different, when we listen to the code.