--- tags: 程式設計,第八章,學生版,互動藝術程式創作入門,Creative Coding --- # ch8 函數與物件互動 - 建構無法預測的小世界_學生版 使用ellipseMode(CORNER)可以設定以左上角為座標點 --- ![](https://i.imgur.com/rjsmAQM.png) ```javascript= function setup() { createCanvas(windowWidth, windowHeight); background(100); } function draw() { translate(width/2,height/2); //讓圓心在視窗的中心點 ellipse(0, 0, 50); //目前此圓,仍以圓心為座標點 ellipseMode(CORNER) // 設定以左上角為座標點上的座標 ellipse(30, -20, 100, 30); // 設定以左上角為座標點,做一個花瓣 } ``` --- 利用push(),pop()產生另外一個圖 ![](https://i.imgur.com/H0ncUn3.png) ```javascript= function setup() { createCanvas(windowWidth, windowHeight); background(100); } function draw() { translate(width/2,height/2); //讓圓心在視窗的中心點 push() ellipse(0, 0, 50); //目前此圓,仍以圓心為座標點 ellipseMode(CORNER) // 設定以左上角為座標點上的座標 ellipse(30, -15, 100, 30); // 設定以左上角為座標點,做一個花瓣 pop() } ``` --- 做一個旋轉 rotate(PI/8) //180度產生八片,360度產生16片 ![](https://i.imgur.com/bPFWVWY.png) --- ```javascript= function setup() { createCanvas(windowWidth, windowHeight); background(100); } function draw() { translate(width/2,height/2); //讓圓心在視窗的中心點 push() ellipse(0, 0, 50); //目前此圓,仍以圓心為座標點 ellipseMode(CORNER) // 設定以左上角為座標點上的座標 ellipse(30, -20, 100, 30); // 設定以左上角為座標點,做一個花瓣 rotate(PI/8) //180度產生八片,360度產生16片 ellipse(30, -20, 100, 30); // 設定以左上角為座標點,做一個花瓣 rotate(PI/8) //180度產生八片,360度產生16片 ellipse(30, -20, 100, 30); // 設定以左上角為座標點,做一個花瓣 rotate(PI/8) //180度產生八片,360度產生16片 ellipse(30,-20, 100, 30); // 設定以左上角為座標點,做一個花瓣 rotate(PI/8) //180度產生八片,360度產生16片 ellipse(30,-20, 100, 30); // 設定以左上角為座標點,做一個花瓣 pop() } ``` --- 利用for產生滿版的圓 ![](https://i.imgur.com/n3jQyqI.png) --- ```javascript=function setup() { createCanvas(windowWidth, windowHeight); } function draw() { background(220); translate(width/2,height/2)//原點一道視窗中心 push() fill(255,211,33) ellipse(0,0,50) ellipseMode(CORNER) fill(255,90,61) for(var i = 0;i<16;i++){ ellipse(30,-20,100,30); line(50,-5,110,-5) rotate(PI/8) } pop() } ``` --- 可以改變程式碼,讓花辮的寬與x座標隨mouseX改變 ellipse(30, -20, 100, 30);改為以下變化 * ellipse(mouseX, -20, 100, 30); * ellipse(30, -20, mouseX, 30); * ellipse(mouseX, -20, mouseX/2, 30); --- 改變橢圓長度,並畫一條線 ```javascript= ``` --- 利用函數設定花朵 ![](https://i.imgur.com/IsDB45R.png) --- ```javascript= function setup() { createCanvas(windowWidth, windowHeight); background(100); } function drawFlower(){ } function draw() { translate(width/2,height/2); //讓圓心在視窗的中心點 drawFlower() } ``` --- 改變顏色 ![](https://i.imgur.com/LRYM7Tz.png) --- ```javascript= var colors = "ff99c8-fcf6bd-d0f4de-a9def9-e4c1f9".split("-").map(a=>"#"+a) var clr function setup() { createCanvas(windowWidth, windowHeight); push() translate(width/2,height/2)//原點一道視窗中心 clr=colors[int(random(colors.length))] drawFlower(clr) pop() push() translate(width/4,height/2) clr=colors[int(random(colors.length))] drawFlower(clr) pop() push() translate(width*3/4,height/2) clr=colors[int(random(colors.length))] drawFlower(clr) pop() push() translate(100,100) clr=colors[int(random(colors.length))] drawFlower(clr) pop() } function draw() { // background(220); } function drawFlower(clr){ push() fill(clr) ellipse(0,0,50) ellipseMode(CORNER) fill(clr) for(var i = 0;i<16;i++){ ellipse(30,-20,100,30); line(50,-5,110,-5) rotate(PI/8) } pop() } ``` --- 改變大小,依照滑鼠移動改變花瓣大小 ![](https://i.imgur.com/iC0lDW9.gif) --- ```javascript= function setup() { createCanvas(windowWidth, windowHeight); background(100); } function drawFlower(clr,size=1){ } function draw() { background(0); //加一個背景 } ``` --- 一排的花朵 ![](https://i.imgur.com/PtpQZQo.gif) --- ```javascript= ``` --- 可以加上scale(0.7)改變花朵縮小0.7 ![](https://i.imgur.com/KQ6JjUZ.gif) --- ```javascript= push() translate(i,j); drawFlower("#ff5000",map(mouseX,0,width,0,1)) pop() ``` 加入 ```javascript= push() translate(i,j); scale(0.7) drawFlower("#ff5000",map(mouseX,0,width,0,1)) pop() ``` --- 旋轉花朵 利用frameCount坐改變 ![](https://i.imgur.com/CvV81lq.gif) --- ```javascript= push() translate(i,j); scale(0.7) rotate(frameCount/20) drawFlower("#ff5000",map(mouseX,0,width,0,1)) pop() ``` --- 兩個顏色的花朵 ![](https://i.imgur.com/LsknZ8R.gif) ```javascript= ``` --- 陣列 色票的運用 ```javascript= var colors= ['red', 'orange', 'yellow', 'green', 'blue', 'purple']; ``` --- 或使用色票 https://coolors.co/064789-427aa1-ebf2fa-679436-a5be00 ![](https://i.imgur.com/cCACLD5.png) --- ```javascript= var colors= ["#064789","#427aa1","#ebf2fa","#679436","#a5be00"]; ``` --- ![](https://i.imgur.com/TSTtUQJ.gif) --- ```javascript= ``` --- 以上方式可能會因為解析度的不一樣,造成這樣的畫面 (因為顏色只有五個,超過五個花後,就變成原本預設的顏色) ![](https://i.imgur.com/r9Gyr9l.gif) --- 解決方法 也可以利用count變數 ![](https://i.imgur.com/6x07cdS.gif) ```javascript= ``` --- 依照上面方式,可以設定不同size的值 ![](https://i.imgur.com/a6HEj6x.gif) --- ```javascript= ``` --- 利用陣列直接放在某個地方 ![](https://i.imgur.com/v89OWdT.png) --- ```javascript= ``` --- 讓他有掉下來的感覺 ![](https://i.imgur.com/Opu5Y7B.gif) --- ```javascript= ``` --- 碰到邊邊往上或往下 ![](https://i.imgur.com/f50r8eL.gif) --- ```javascript= ``` --- 陣列新增元素 按下滑鼠後,產生一個新的花 --- ![](https://i.imgur.com/HKMtsye.gif) --- ```javascript= ``` --- 碰到頂的時候,消失不見 把list的元素刪除 ![](https://i.imgur.com/p3XVEsc.gif) --- ```javascript= ``` --- --- ## 單元介紹 ### 目標 * 瞭解建構大型互動系統的基礎 * 瞭解如何使用函數打包重複的動作 * 瞭解陣列與物件的基礎 * 能夠結合陣列與物件 * 使用函數操作/更改物件 * 讓物件之間彼此互動 --- ### 課程重點 #### function 函數 * 為什麼要用函數:函數可以把需要重複執行的行為打包,在需要使用的時候直接使用函數就不需要再一次一步一步的執行每個步驟了。像是大家熟悉的 setup() 與 draw() 都是函數喔!其他函數的優點: * 解決重複的程式 * 分拆抽象概念 * 提高程式可讀性 * 函數的結構 ```javascript= function nameOfFunction(param1, param2 ...){ //裡面的參數 params 不是必要 ... // 要重複做的事情,這邊可以使用上面傳入的參數,如 param1,param2... 等 ... return dataToReturn // 要回傳的資料,非必要 } ``` * 函數的命名方式:跟變數一樣,用駝峰式命名。如果是要執行某個行為的話,第一個字會是動詞像是 drawFlower()。 * 傳入參數:如果函示在建立的時候可以傳入參數,那在使用的時候傳入當下不同的狀態,例如我要畫多個花,大小都不一樣,那只要改變 drawFlower(posX, posY, size) 裡面的 size 參數就好。 * 預設參數 drawFlower(posX, posY, size=10){...} 這樣可以預設 size 的大小。 * 動態的使用參數,在函式內加上 if 的判斷,像是 if(size){scale(size)},可以動態決定要不要使用參數。 * 函數內使用參數:像是在 48:55 傳入參數時,函示外部傳入的變數是 myFlower 但是在 function 裡面要用 flower 才能取得,因為 myFlower 是在函式外部的變數,但是到了函數內都要用定義時的名稱才能使用喔!(這邊暫時不考慮全域變數等狀況) * 回傳資料:定義函數時可以選擇要不要回傳值,如果回傳的話可以使用 return 你要回傳的東西,附帶一提,不論有沒有實際回傳的東西,函數遇到 return 就會終止喔! * 函數的使用:直接用函數名稱 nameOfFunction(param1, param2 ...) 就會執行了,如果有回傳值的話也可以賦予到新的變數上面,像是如果函式長這樣: ```javascript= function sayHi(yourName){ return 'Hi '+ yourName +'!'; } // 那我們就可以透過變數把這個回傳的問候存起來 let message = sayHi('John') print(message) // 會顯示 'Hi John !' ``` * 範例 * 一般我們執行重複的行為會這樣寫: ```javascript= push() ellipse(mouseX, mouseY, 50) pop() ``` * 包成函數的話,就會變成這樣: ```javascript= function drawCircle(positionX, positionY, r){ push() ellipse(positionX, positionY, r) pop() } // 要使用的時候,可以恣意的填入當下想要的參數,例如: drawCircle(mouseX, mouseY, 50) ``` ## 陣列:一系列形式相同的資料 * 為什麼要用陣列? 使用陣列我們可以把相同類型的資料儲存起來,在需要用到的時候遍歷裡面的每個元素,也可以動態的增減,讓繪製多個類似圖形的效率大大提高。 * 陣列的結構:let datas = [數值1,數值2,...] * 取值的方法: datas[0] //[] 裡面指的是要取第 n 個值,要注意 JavaScript 是從0 開始計數,所以要取第一個項目的話,是datas[0],而不是 datas[1] 喔 * 遍歷整個陣列的方式: * 用for loop ```javascript= let datas = [d1,d2,d3...] (let i =0;i<datas.length;){ let data = //第 i 個資料 } ``` * 用 forEach * 用 for of * 操作陣列裡面的元素 ```javascript= let datas = [1,2,3,4,5] datas[0] += 1 // 第 1 個元素加一 print(datas) // [2,2,3,4,5] 第 1 個元素變成 2 了! ``` * 陣列的操作: * push():增加一個元素到陣列的最後。 * pop():移除掉陣列的最後一個元素。 * slice(x, n):從第 x 項移除 n 個元素。 ### 物件:有多個屬性的資料 * 結構: ```javascript= let obj = { property1: value1, property2: value2, ... } // 像是 let flower = { x: 300, y: 200, size: 2, color: "#2499f2" ... } ``` * 物件的取值:用物件名稱.屬性名稱 就可以拿到資料,像是 print(flower.size) // 2 * 物件的修改:直接賦予新的值,或是直接做計算。像是:flower.y += 1 或是 flower.size *= 0.99 * 物件內部的方法:把繪圖放到花的物件裡面 * this ### 結合物件、陣列與函式 ```javascript= let flowers = [] //先設定一個空的陣列來存放花朵的物件們 function setup(){ ... for(var i=0;i<100;i++){ // 用 push() 塞入 100 個花的物件 flowers.push( generateRandomFlower(random(width),random(height)) // 這邊會收到 generateRandomFlower() 函式回傳的花朵物件 ) } ... } function generateRandomFlower(x,y){ return { x: x, y: y, r: random(50,120), color: random(colors) } } ``` ### 物件之間的互動 * 用 for loop 遍歷陣列裡面的物件,再使用 dist() 判斷兩個物件的距離。 ```javascript= let bee = { x = mouseX, y = mouseY } for(let i =0; i<flowers.length; i++){ let flower = flowers[i] //宣告一個變數設定當下這個回圈裡面的花朵,方便接下來計算使用 if(dist(bee.x,bee.y, flower.x, flower.y) < 100){ // 開花 }else{ // 關花 } } ``` * 有多個蜜蜂物件也歹就補,做兩層迴圈就ok ```javascript= for(let i =0; i<flowers.length; i++){ for(let j =0; j<bees.length; j++){ let flower = flowers[i] let bee = bees[j] let hasBee = false // 每個花都設定自己的變數,以免被已經經過的蜜蜂影響 if(dist(bee.x,bee.y, flower.x, flower.y) < 100){ hasBee = true } if(hasBee){ // 開花 } } } ``` ## 範例練習 函數 Demo 版本 ![](https://i.imgur.com/g9tELGz.gif) --- 物件互動改良版 ![](https://i.imgur.com/3i5jMVl.gif) --- 老師作品版 ![](https://i.imgur.com/eR7AFF8.png) --- 其他參考 - 夾壽司! ![](https://i.imgur.com/jgRPFYa.png) --- ## 內容回顧 ### 小技巧: * 在程式開始時就產生很多花 * 用 lerp 產生漸變的變化(如花朵大小的漸變、蜜蜂飛向滑鼠位置的速度) ```javascript= if(花朵變大的條件){ lerp(myFlower.size, 2, 0.05) // 趨近於 2 ,變得比較快 }else{ lerp(myFlower.size, 0, 0.01) // 趨近於 0,變得比較慢 } ``` * 加入旋轉會讓花更生動 * 讓多個蜜蜂生動地跟著滑鼠移動 ```javascript= for(let i ){ let bee = bees[i] bee.x = lerp(bee.x, mouseX, random(0.01,0.1)) bee.y = lerp(bee.y, mouseY, random(0.01,0.1)) } ``` --- ## 課後問題 --- ## 範例觀摩 ![](https://i.imgur.com/hi1kZPZ.png) https://www.flickr.com/photos/kmasback/page6 ![](https://i.imgur.com/KkM0bIg.jpg) https://borderless.teamlab.art/ ![](https://i.imgur.com/ZKg2gdP.jpg) https://www.teamlab.art/zh-hant/w/aquarium/ ![](https://i.imgur.com/b10vFBL.jpg) https://www.teamlab.art/zh-hant/w/whatloving/ ![](https://i.imgur.com/sC8bN1j.png) https://www.openprocessing.org/sketch/891619/ ![](https://i.imgur.com/Oe2IiSv.png) https://www.openprocessing.org/sketch/892128 https://www.openprocessing.org/sketch/887969/