iT邦幫忙

2021 iThome 鐵人賽

DAY 16
1
Modern Web

從零開始打造網頁遊戲-造輪子你也辦的到!系列 第 16

Chpater3 今天來學習畫一棵樹(II)以有規律的隨機畫出擬真的樹枝 原來畫一顆樹不難嘛!

此篇接續第一篇:https://ithelp.ithome.com.tw/articles/10269980
接下來我們把造樹的步驟拆分成骨幹、畫樹枝、樹葉,而到目前為止只完成了一個很陽春的骨幹。

更加真實的骨幹

在這個步驟中,可以加入一些隨機元素,思考邏輯如下:

  1. 當樹枝進行一節節生長時,需要不斷向上輸送營養,過程會有損耗
  2. 當樹枝主幹要進行分岔時,營養會分別被兩個子樹枝給瓜分

按照這個邏輯,我們依序設計以下變數並添加到昨天的建構式:

  1. shrink表示樹枝隨著一節節越來越短 (約0.55 - 0.65之間)
  2. diff表示兩個子樹枝瓜分營養後,導致的長短差異 (約正負0.15之間)
if(times > 0){
    let endX = x + r * Math.cos(theta / 180 * Math.PI);
    let endY = y + r * Math.sin(theta / 180 * Math.PI);
    let shrink = 0.65 + random(0.1); // from 0.55 to 0.65
    let diff = random(0.3) - 0.15;  // +-0.15
    this.son = 
         [new Tree(this, endX, endY, r * (shrink - diff), theta+30, times-1),
          new Tree(this, endX, endY, r * (shrink + diff), theta-30, times-1)];
}

diff 越少看起來越完美,越多則越容易有枝葉營養不良
shrink 越少整個樹越小

這樣一來,就可以看到現在樹枝的分岔更加的寫實跟蜿蜒曲折了:
https://ithelp.ithome.com.tw/upload/images/20210923/20135197dV98Y6DAZG.png

為了未來添加風力的效果,今天一邊研究一邊設計了兩個參數a和b,原本的分枝角度各為正負30度,
a: 若讓兩個分枝都增加角度,就能讓分枝逆時針綣曲,反之順時針
b: 若讓兩個分枝的正負角度更大,能讓分枝更加擴散,反之則是集中

並且為此製作一個函式,可以根據ab值計算座標:

let Tree = function(father, x, y, r, theta, times){
  // ......
  // 省略
  // ......
  this.Transform = function(index){
    if(this.father){
      this.theta = this.father.theta + 30 * ((index == 0)?(a+b+1):(a-b-1));
      this.startX = this.father.endX;
      this.startY = this.father.endY;
      this.endX = this.startX + this.r * Math.cos(this.theta / 180 * Math.PI);
      this.endY = this.startY + this.r * Math.sin(this.theta / 180 * Math.PI);
    }
    // 我是遞迴唷!還記得我嗎?
    if(this.son) this.son.forEach((branch, index) => branch.Transform(index));
  };
}

都是用上一根樹枝來一節節往下算,用this.father的末端座標當原點來計算(r, theta)極座標位置

也因此一開始造樹的時候第一枝要傳入undefined給father

canvas.addEventListener('click', Recreate);
function Recreate(e){
    let Rect = canvas.getBoundingClientRect();
    myTree = new Tree(undefined, WIDTH/2, HEIGHT, HEIGHT/6, -90, 10);
    // 或者,讓它從滑鼠的位置長出來
    // let x = (e.pageX - Rect.left) * RATIO;
    // let y = (e.pageY - Rect.top) * RATIO;
    // myTree = new Tree(undefined, x, y, HEIGHT/6, -90, 10);
}

每次點擊滑鼠時重新產生樹

接著再加上滑鼠的移動事件監聽,讓ab值隨著滑鼠移動改變:

canvas.addEventListener('mousemove', GetMouse);
function GetMouse(e) {
    let Rect = canvas.getBoundingClientRect();
    a = ((e.pageX - Rect.left) * RATIO - WIDTH/2) / (WIDTH/2);
    b = ((e.pageY - Rect.top) * RATIO - HEIGHT/2) / (HEIGHT/2);
    myTree.Transform(); // 呼叫改變樹形狀的方法
}

如此一來,便能看到樹的形狀豐富的改變,是很棒的研究材料呢!
(到這邊本來有一個demo,不過我加了一些有的沒得進去,就爆炸了ww,等我修好明天再一起po)
(因為沒有備份,然後又想加入第二章的緩衝函式,製作風力效果,然後就出事了阿北,拍謝拉~)

樹枝和樹葉

有了真實的骨幹終於能開始進一步囉!明天會講到:Y型樹枝、樹枝的顏色、樹的成長、樹葉

我們離Classes又更近了

此時大家有沒有發現一件事:Draw和Transform這兩個函式不只一組!我們來算一下初始條件設10,總共會有2^10=1024個樹枝,那麼,按照我們這個物件的寫法,就會有1024組Draw、Transform函式,天哪!太多了吧,摁確實就是這麼多,翻開每一個樹枝的物件,都可以看到裡面紮紮實實的擁有這兩組函式,雖然跟其他人都長得不一樣。

那麼,其實有一個方式可以解決,用類別函式Classes來造樹枝!(大約就在...第四章節一邊做遊戲會一邊說明)


上一篇
Chapter3 今天來學習畫一棵樹(I)學學人家DOM 自己用遞迴做一個樹狀圖結構
下一篇
Chpater3 今天來學習畫一棵樹(III)終於讓樹搖擺起來囉!原來遞迴與碎形可以塑造大自然之美
系列文
從零開始打造網頁遊戲-造輪子你也辦的到!31

尚未有邦友留言

立即登入留言