Newbie: Create a tendril/vine effect?

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?

.@mpilgrem nice! But it closes my Codea after some time…

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.