Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Newbie: Create a tendril/vine effect?

edited December 2012 in Questions Posts: 22

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!



  • Jmv38Jmv38 Mod
    Posts: 3,295

    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.

  • Jmv38Jmv38 Mod
    Posts: 3,295

    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 written by Vega. Looks promising!

  • edited December 2012 Posts: 489

    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. -- 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 = 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 = 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 then 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.bud = self.bud -- Reattach bud to newStem = newStem self.bud = nil -- Clear bud from self end local nextStem = 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 = 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) = newStem end function Stem:sprout() local bud = self.bud if bud then bud:sprout() end local budWidth = 3 local nextStem = 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?

  • Jmv38Jmv38 Mod
    Posts: 3,295

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

  • Posts: 489

    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.

Sign In or Register to comment.