I’m a professional C#.Net programmer, but I’m brand new to game programming (except for some time spent playing with XNA) and just purchased Codea with a Christmas gift card. Exciting! I’m working through the examples and tutorials and am impressed with the ease and functionality of the Lua language and the Codea app in particular. All that said, I have some visions for an app and am not sure how to get started with creating some of the elements. Hopefully this question is not too generic but I’m hoping someone can provide some guidance on creating an effect that would simulate a growing flower. I imagine it uncoiling from the ground and reaching skyward eventually rooting itself in the ground with a thick base and a thin stem. I’m not looking for code exactly, since I enjoy the research involved in implementation but I need some basic direction on how this could be accomplished. Is something like this even possible in Codea? Since I don’t want a simple line I’m not sure if I’d have to use sprites? How do I animate something like this?
I am happy to post code once I begin this process and am glad to be part of this community!
Thanks!
Hi. You want it 2d or 3d?
Sorry for neglecting to mention. 2D. I’ve been looking at the concept of a mesh along with Bézier curves and think it may be possible to create it using these ideas, although I’m still working through the implementations I’ve found. They are pretty complex for a new games programmer.
Thanks!
You could look at my post KraizyCircles: i draw the path between cicles with a mesh because it is a lot of circles. And for other objects i use sprites because it is simpler. You should use mesh for the stem.
I “downloaded” it quickly and ran it. It looks promising. Thanks for the information. I’m still struggling with the concept of a mesh. I’ve read the inline documentation but the simplistic triangle description hasn’t clarified it much. Is there a better example or definition that you could provide?
Thanks again
I just found this nice tutorial on mesh usage at http://twolivesleft.com/Codea/Talk/discussion/1244/mesh-tutorial/p1 written by Vega. Looks promising!
How about the following? (Edit) Updated to use a faster mesh rather than lines. (Edit #2) Further updated Stem:grow(rate)
to place bud correctly when long stem splits in two.
--
-- Tendril
-- Version 2012.12.31.15.20
--
supportedOrientations(LANDSCAPE_ANY)
function setup()
local origin = vec2(WIDTH/2, HEIGHT/10)
local up = vec2(0, 1)
local sign = math.random(2) * 2 - 3
local curl = math.pi/2 * (math.random()/2 + 1/4) * sign
myStem = Stem(origin, 10, up, curl, 2)
timer = 0
m = mesh()
myStem:mesh(m)
end
function draw()
background(0)
stroke(88, 64, 32)
strokeWidth(10)
line(0, HEIGHT/10, WIDTH, HEIGHT/10)
m:draw()
timer = timer + DeltaTime
if timer > 0.05 then
timer = 0
myStem:grow(0.02)
myStem:new()
myStem:sprout()
m = mesh()
myStem:mesh(m)
end
end
Stem = class()
function Stem:init(loc, len, dir, curl, width)
self.loc = loc
self.len = len
self.dir = dir
self.curl = curl
self.width = width
self.next = nil
self.bud = nil
end
function Stem:mesh(m)
local tip = self.loc + self.len * self.dir
local tx, ty = tip.x, tip.y
if tx < 0 or ty < 0 or tx > WIDTH or ty > HEIGHT then
self.next = nil
self.bud = nil
end
local x = self.loc.x
local y = self.loc.y
local idx = m:addRect((x + tx)/2, (y + ty)/2, self.width, self.len,
math.atan2(-self.dir.x, self.dir.y))
local w = math.min(self.width, 20)
m:setRectColor(idx, color(88 - w * 2, 255 - w * 8, 32))
if self.next then self.next:mesh(m) end
if self.bud then self.bud:mesh(m) end
end
function Stem:grow(rate)
local maxLen = 20
local f = 1 + rate
self.len = self.len * f
self.width = self.width * math.sqrt(f)
if self.len > maxLen then
self.len = self.len/2/math.cos(self.curl/4)
self.dir = self.dir:rotate(-self.curl/4)
self.curl = self.curl/2
local tip = self.loc + self.len * self.dir
local newStem = Stem(tip, self.len,
self.dir:rotate(self.curl), self.curl/2, self.width)
newStem.next = self.next
newStem.bud = self.bud -- Reattach bud to newStem
self.next = newStem
self.bud = nil -- Clear bud from self
end
local nextStem = self.next
local tip = self.loc + self.len * self.dir
if nextStem then
nextStem.loc = tip
nextStem:grow(rate)
end
local bud = self.bud
if bud then
bud.loc = tip
bud:grow(rate)
end
end
function Stem:new()
local bud = self.bud
if bud then bud:new() end
local minTipLen = 5
local tipLen = self.len * 0.9
if tipLen < minTipLen then return end
local tipWidth = tipLen / 5
local nextStem = self.next
if nextStem then
nextStem:new()
return
end
local tip = self.loc + self.len * self.dir
local newStem = Stem(tip, tipLen,
self.dir:rotate(self.curl), self.curl, tipWidth)
self.next = newStem
end
function Stem:sprout()
local bud = self.bud
if bud then bud:sprout() end
local budWidth = 3
local nextStem = self.next
if self.width > budWidth and (not self.bud) and nextStem then
if math.random(50) == 1 then
local curl = math.pi/2 * (math.random()/2 + 1/4)
if self.curl > 0 then curl = -curl end
self.bud = Stem(nextStem.loc, 10,
self.dir:rotate(curl), curl, 2)
end
end
if nextStem then nextStem:sprout() end
end
Pretty cool! It’s got a very fractal feel to it. Really amazing that this was all done with meshes!? It completely changing my perception of what a mesh is capable of doing. Care to explain the code a bit?
Hello @mcarthey. I hope the following helps:
The tendril is composed of rectangular ‘stems’, each represented by a Codea class Stem
.
The stems have a location (the base) (loc
), a length (len
), a direction (dir
), a tendency to curl (curl
) and a width (width
). The direction is a vector of length 1
(it is normalised). The curl is expressed as an angle in radians.
Each stem may also have a following, next, stem (next
) and/or a bud (bud
).
Four things can happen to a stem:
mesh(m)
: this adds triangles to mesh m
, graphically representing the stem;
grow(rate)
: this increases the length of the stem by rate rate
;
new()
: if the stem is a tip, and certain conditions are met, this adds a new next stem to the tip; and
sprout()
: if the stem does not have a bud, and certain conditions are met, this adds a bud to the stem.
mesh
, grow
, new
and sprout
are recursive. That is, if the stem has a next stem or a bud, that is also called with the same function.
The location of the tip of a stem is calculated as the location of its base (loc
) plus its length (loc
) multiplied by its direction (dir
).
mesh(m)
uses Codea function addRect()
to add a rotated rectangle to mesh m
. The colour of the rectangle depends on the width of the stem, and is set with Codea function setRectColor()
.
mesh(m)
also does some pruning. If the tip of the stem falls outside of the Viewer region, the next stem and the bud is set to nil
. This pruning is necessary to stop over-large tendrils causing Codea to grind to a halt. (Even with pruning, a tendril will have several thousand stems after the code has been running a while.)
grow(rate)
checks to see if the length of a stem is greater than a certain length (maxLen
). If it is, the stem is replaced by two shorter stems. The growth factor of the width of a stem is less (math.sqrt(f)
) than that of its length (f
).
new()
will add a next stem to a tip stem that is 90% the length of tip, but only if the length of the next stem will be greater than a certain length (minTipLen
). The direction of the next stem will curl (be rotated, using the Codea function rotate()
) depending on the curl of the tip stem.
sprout()
will only add a bud if the width of the stem is greater than a certain width (budWidth
). If that condition is met, there is a 1-in-50 chance of a bud forming at the tip of the stem. The bud will have a random curl and will curl in the opposite direction to the curl of the stem.
The tendril starts (in setup()
) with a single stem (myStem
) located in the middle of the bottom of the Viewer, directed upwards, with a random curl. A new mesh is created (m = mesh()
) and populated with triangles (myStem:mesh(m)
).
Up to 60 times a second, Codea calls draw()
. The mesh m
is drawn (as well as a brown line representing the ground).
If 0.05 seconds have passed since the last event (timer
keeps track of that), myStem
is grown at a rate of 2% (grow(0.02)
), new tips are added (new()
) and buds are sprouted (sprout()
). A new mesh is recreated after these events.