🚨週年慶最後三日🚨全站課程 83 折、最後加碼 $899💰 再送激高 $1600 券❗️
天
時
分
全站 83 折🔥
登入/註冊
吳哲宇
2021/06/10
1,595

【直播精華筆記】用程式玩創作,讓小機器人動起來!哲宇的互動藝術體驗

不會寫 code,要如何 coding 來做藝術創作?哲宇老師帶你用 p5.js 製作互動機器人,不論你是否是工程師,都能透過這個程式語言來進行創作。

這次老闆(哲宇老師)跟 Hahow 好學校合作進行臉書直播活動,讓大家在這個嚴峻的疫情下,待在家就能創作生成式藝術。相信初學者聽到 creative coding 生成式藝術,一定會非常驚慌,想著自己不會寫 code,要如何 coding 來做藝術創作?今天老闆將會帶領你利用 p5.js 製作互動機器人,不論你是否是工程師,都能透過這個程式語言來進行創作。


用 p5.js 創作互動藝術,讓小機器人動起來



由於這次是體驗課程,主要讓大家在短時間內,完成生成式藝術的創作,如果想要了解更詳細的製作流程和其他創作內容,可以去支持老闆的《互動藝術程式創作入門》。

  • 想聽老闆與小幫手邊聊天邊寫程式的話,請到這邊觀賞直播影片,也附上這次作品的成品。

  • 想看看老闆作品集請往這邊走。

這份直播筆記會談到:

  • 如何利用線上工具 openprocessing 進行 Creative Coding 創作

  • 利用 p5.js 繪製不同大小顏色的方塊和圓形,組裝出基本的機器人

  • 結合滑鼠監測,讓機器人與使用者互動



動手做之前要先準備好…

創作過程會使用到 p5.js 的 API 如下:

  • translate(x, y) 將畫筆移動到(x, y)

  • background(0) 背景色黑色

  • fill(‘#fff’) 將畫筆顏色換成 #fff

  • noStroke() 不使用邊線

  • rectMode(CENTER) 畫方塊的模式,傳入的參數可以是 CORNER、CORNERS、RADIUS、CENTER,預設為CORNER,連結

  • rect(x, y, width, height) 在(x, y)畫一個寬度 width 高度 height 的方形

  • ellipse(x, y, width, height) 在(x, y)畫一個寬度 width 高度 height 的橢圓形

  • push() 儲存目前畫布的樣式設定

  • pop() 恢復剛剛 push 所儲存的樣式設定

  • Object.assign() 用來複製一個或多個物件自身所有可數的屬性到另一個目標物件。連結


環境:

這次創作以線上工具 openprocessing 示範,大家也可以利用 codepen 進行開發,只要在 codepen 內引用 p5.js 即可。


現在就讓我們跟著哲宇一起開始囉!

進入 openporcessing 網頁並申請帳號後,就能開始創作。初始網頁就會提供大家最基礎的兩個 function (setup跟draw),如果要看效果,可以點選 Editor 來切換 Layout。
萬事起頭難,直接看成品就要創作,該如何下手?首先先將最終目標拆分成不同階段任務,第一步我們會先製作出第一隻機器人,接著再從一隻複製成很多隻,並賦予每隻機器人不同顏色與樣式,最後只要結合滑鼠互動,讓機器人跟著動起來,就大功告成了。


1. 做第一隻機器人


要做機器人的所有部位需要有顏色,所以第一步驟便是製作一個顏色陣列,來儲存所有我們會用到的顏色,這邊推薦使用配色工具:coolors https://coolors.co/。能夠將吸到的顏色製作成陣列,老闆利用這個工具抓顏色時,習慣將喜歡的顏色按 lock 鎖住後,按空白鍵來替換其他顏色。確定了整組顏色之後,可以在網址列上獲得一組用 “-” 符號分開的色碼,只要利用以下程式碼便能將色碼變成陣列。

// main.js
var colors = ('003049-d62828-f77f00-fcbf49-eae2b7-226f54-2d6a4f-276fbf').split("-").map(a => "#" + a)


  • (字串).split(“-“) 將整串字串用-符號做切割,分成整組陣列

  • [].map(a ⇒ “#” + a) 陣列中每個項目都稱作 a,將每個 a 都進行加工回傳 “#” + a


藉由前面列出的 API ,我們使用幾何圖形先製作出機器人的頭和五官,這邊大家可以自行調整每個部位的大小以及顏色。若是想要使用更多顏色讓畫面不單調,可以再透過colors工具產出新的色碼,增加到變數 colors 的字串中,只要記得每個顏色都要使用 “-” 分開就好。沒有程式經驗的同學也不用緊張,只要把使用這些 API 的過程想像成,使用一行程式語言告訴電腦,幫我換顏料顏色,或是畫出什麼圖型就好,透過不斷地嘗試顏色及大小,直到畫出自己滿意的狀態。

// main.js
function draw() {
  translate(width/2, height/2)
  rectMode(CENTER)
  background(0)

  noStroke() // 不要有邊框
  //  機器人頭
  fill(colors[1])
  rect(0, 0, 500, 300)

  // 左眼
  fill(colors[3])
  ellipse(-120, 0, 60, 60)

  // 右眼
  fill(colors[2])
  ellipse(100, 0, 60, 60)

  // 左眉毛
  fill(colors[3])
  rect(-90, -100, 150, 30)

  // 右眉毛
  fill(colors[4])
  rect(100, -100, 150, 30)

  // 嘴巴
  fill(colors[4])
  rect(0, 100, 200, 30)

  // 鼻子
  fill(colors[7])
  rect(0, 10, 30, 80)

  // 左耳
  fill(colors[6])
  rect(-250, 10, 50, 150)

  // 右耳
  fill(colors[7])
  rect(250, 10, 60, 150)
}


小機器人

畫好機器人的五官



2. 機器人動起來吧!


賦予機器人五官之後,接著來為機器人加上一些動態,賦予它情緒。在創作的過程,大家可以思考並結合生活中的經驗在作品中,例如機器人可以有胖瘦不同身材,或是不同的情緒。
我們為了讓眼睛能夠縮放,將眼睛的寬度高度加上一個隨機值。眉毛則隨著時間去旋轉,不過這邊要注意的是,旋轉會影響到整張畫布,讓眉毛旋轉前,要先將目前畫布狀態存起來 push(),旋轉完之後再使用 pop() 恢復畫布,如果忘記做這件事情,會發現後面的程式碼都被影響到了,大家可以嘗試把 push 和 pop 拿掉來看看失控的機器人。

// main.js
function draw() {
  ...
  // 左眼
  fill(colors[3])
  ellipse(-120, 0, 60 + random(-30, 30), 60 + random(-30, 30))

  ...
  // 左眉毛
  push()
    fill(colors[3])
    rotate(0.3 + sin(frameCount/30)/5)
    rect(-90, -100, 150, 30)
  pop()
  // 右眉毛
  push()
    fill(colors[4])
    rotate(-0.25 + sin(frameCount/50)/5)
    rect(100, -100, 150, 30)
  pop()
  ...
}


小技巧:openprocessing 有提供分支的功能,可以讓大家階段性儲存檔案,在之後的開發,隨時都能回到前一版所儲存的狀態。


有動態的機器人



3. 製作機器人分身


接著我們會將機器人包裝成 class,這可以想像成我們將機器人包裝成客製化的印章,並且每個印章都可以有自己的屬性(顏色大小等)。先在 openprocessing 上面開一個新的 tab,在裡面開始撰寫 class。openprocessing 很方便的地方在於,開不同 tab 撰寫程式碼時,不用在 main.js 中 import robot.js ,就會自動幫我們引入。

首先我們先來處理 robot.js, 可以將 class 內分三個區塊解說

  • constructor:初始化印章,可以指定數字或顏色給這個 robot 使用 這邊我們會在裡面預設一個參數,並使用 Object.assign 將 args 的值複製到預設參數中,再將預設參數中的值複製給本體。

  • draw:繪製相關的函數 將剛剛第一個機器人的相關內容複製進來後,在前後分別加入 push, pop ,避免每次畫完一個機器人,畫布狀態又被影響。 此外也要注意要將原本 translate(x, y) 的值在這裡改成使用 constructor 內的值

  • update:資料相關的更新


// robot.js
class Robot {
  // 初始化印章,可以指定數字或顏色給這個 robot 使用
  constructor(args){
    // 預設參數
    let def = {
      p: createVector(width/2, height/2)
    }
    Object.assign(def, args)
    Object.assign(this, def)
  }
  // 繪製相關的函數
  draw(){
    push()
    rectMode(CENTER)
    translate(this.p.x, this.p.y)

    ...

    pop()
  }
  // 資料相關的更新
  update(){}
}


完成 class 後,我們將 class 重複使用,製作出兩隻機器人。

main.js 中分為三個步驟:

  1. 創造一個新變數指定為空陣列 將每次創造的機器人使用 js 中的 push() 儲存到陣列中。要注意的是,這邊使用的 push () 是 js 語法,而我們先前在繪製機器人使用的 push 則是 p5.js 內儲存畫布狀態的語法。

  2. setup() 中,new 出不同的機器人,並傳入不同的位置

  3. draw() 中, 先將剛剛繪製機器人的函數移到 robot.js 內的 draw() 中,接著將 robots 中的所有機器人的資料更新後,並繪製出來


// main.js
var robots= []

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(100);
  robots.push(new Robot({
    p: createVector(width/3, height/2)
  }))
  robots.push(new Robot({
    p: createVector(width/1.5, height/2)
  }))
}

function draw() {
  background(0)

  robots.forEach(robot => robot.update())
  robots.forEach(robot => robot.draw())

}


複製出另一隻會動的機器人



兩隻機器人完成之後,該如何製作出更多機器人呢?一個一個創造嗎?我們可以使用 for 迴圈來產出機器人的 (x,y),每次迴圈跑完增加的 x 和 y 都可以慢慢去微調,並在 robot.js 中,在 draw() 加入 scale ,調出大家喜歡的樣子。

// main.js
function setup() {
  ...
  for(var x= 0; x < width + 200; x += 300) {
    for(var y = 0; y< height; y += 200) {
      robots.push(new Robot({
        p: createVector(x, y)
      }))
    }
  }
  ...
}


複製機器人

複製機器人



4. 讓每隻機器人都不一樣


創作後期會增加更多的色票,讓作品顏色更豐富,並微調樣式給每隻機器人有不同的外貌,例如:顏色、臉的大小、臉的圓角、耳朵高低、眉毛大小、眼睛大小…等。

在 robot.js 中的 constructor 內產生不一樣的 key 值,都能利用 random 來隨機產生不一樣的值,並將這些值組合進 draw 中。而左眼的眼眶則可以利用兩個圓形依序疊在一起來製作,大家可以利用這些不同的值來做調整,也能試試看新增其他變數來調整自己的機器人。

如果覺得每隻機器人變化的狀態都一樣,老闆也會多產生一個 randomId,以下稱之為亂數種子,亂數種子是在一開始隨機產生的數字,可以讓每隻機器人都能有不一樣的數值,結合到畫面中就能讓每隻機器人有所差異。

// robot.js
class Robot {
  constructor(args){
    let def = {
      p: createVector(width/2, height/2),
      colors: [random(colors), random(colors),
               random(colors), random(colors),
               random(colors), random(colors),
               random(colors), random(colors),
               random(colors), random(colors)
               ],
      // 亂數的尺寸大小
      size: createVector(
        random(400, 300),
        random(300, 200)
      ),
      borderRadius: random(50),
      eyeSize:createVector(
        random(10, 50),
        random(50, 100)
      ),
      scale: random(1),
      // 亂數種子
      randomId: random(1000000)
    }
     ...
  }
  // 繪製相關的函數
  draw(){
    push()

    rectMode(CENTER)
    translate(this.p.x, this.p.y)
    //頭旋轉,因每個機器人的 randomId 都不同,所以旋轉的角度都會不一樣
    rotate(sin(frameCount/50 + this.randomId)/2)
    scale(0.3)

    noStroke()
    // 臉
    fill(this.colors[1])
    //
    rect(0, 0, this.size.x, this.size.y, this.borderRadius)

    // 左眼眶
    fill(this.colors[3])
    circle(-120, 0, 60 + this.eyeSize.x)

    // 右眼
    fill(this.colors[2])
    circle(100, 0, this.eyeSize.y)

    // 左眼球
    fill(this.colors[5])
    circle(-120, 0, 20 + this.eyeSize.x)

    push()
      // 左眉毛
      fill(this.colors[3])
      rotate(0.3 + sin(frameCount/30)/5)
      rect(-90, -100, 150, 30)
    pop()

    push()
      // 右眉毛
      fill(this.colors[4])
      rotate(-0.25 + sin(frameCount/50)/5)
      rect(100, -100, 150, 30)
    pop()

    // 嘴巴
    fill(this.colors[4])
    rect(0, 100, 200, 30)

    // 鼻子
    fill(this.colors[7])
    rect(0, 10, 30, 80)

    // 左耳朵
    fill(this.colors[6])
    rect(-this.size.x/2, 10, 50, 150)

    // 右耳朵
    fill(this.colors[7])
    rect(this.size.x/2 + 20, 10, 60, 150)

    pop()
  }
  update(){}
}


不同樣式的機器人



5. 讓機器人跟滑鼠互動


老闆接下來示範如何讓機器人與滑鼠互動,透過滑鼠移動的 X 量 mouseX,來調整機器人與它的左眉毛,除以 50 是避免抖動的幅度過大,所以固定除以某個數字。此外也可以結合亂數種子,讓每隻機器人左眉毛挑動的時間都能不同。大家也可以試著使用 mouseX, mouseY, randomId 到不同位置,嘗試不同的互動方式,像是前面就有將亂數種子結合到整顆頭的旋轉,也可以試試看讓眼睛大小結合亂數種子或是滑鼠互動使用。

// roboyt.js
class Robot {
  constructor(args){...}
  draw(){
    ...
    // 開始繪製前先旋轉畫布
    rotate(sin(mouseX / 50 + this.randomId) / 5)
    ...
    // 左眉毛
    push()
      fill(this.colors[3])
      translate(0, 50 + sin(mouseX / 50 + this.randomId) * 20)
      rotate(0.3 + sin(frameCount/30)/5)
      rect(-90, -100, 150, 30)
    pop()
    ...
  }
  // 資料相關的更新
  update(){}
}


6. 加入天線

要幫機器人加上天線非常的容易,我們產出一個隨機的值 ll,讓每個機器人的天線長度都不同,結合前面製作旋轉的方式,讓天線隨著時間旋轉。

// robot.js
class Robot{
  constructor(args){
    let def = {
      ...
      ll: random(20,80),
      ...
    }
  }
  draw(){
    ...
    push()
      // 天線
      rotate(sin(frameCount/10+this.randomId)/5)
      fill(this.colors[8])
      rect(0,-200,30,this.ll)
    pop()
    ...
  }
  update(){
  }
}


7. 提升質感與輸出作品


作品完成後,老闆提供一個小方法,讓整個作品質感提升,通常老闆創作結束最後一個階段,會將作品疊上材質,讓原本數位感比較重的作品,多一層質感,使用的方法非常簡單,大家可以將老闆的 code 貼到以下位置即可使用。

(記得拿掉程式碼前的 > 喔!)
// main.js
let overAllTexture
function setup(){
  ...
  pixelDensity(2)
  overAllTexture=createGraphics(width,height)
  overAllTexture.loadPixels()
 > for(var i=0;i   for(var o=0;ooverAllTexture.set(i,o,color(100,noise(i/3,o/3,i*o/50)*random([0,50,100])))
    }
  }
  overAllTexture.updatePixels()
}
> function draw(){
>  push()
>    blendMode(MULTIPLY)
>    image(overAllTexture,0,0)
>  pop()
}


完成的作品當然要好好分享一波,讓大家看看你的作品。如果要匯出 gif ,可以使用 screenflow,不過需要付費。如果只是要輸出圖片,可以在 setup 中加入 pixelDensity(2) ,另存的圖片就會以兩倍像素密度呈現。


完成了小機器人動起來的作品



精華重點回顧


首先讓我們快速回顧一下,這次利用 p5.js 創作出互動機器人的流程:

  1. 透過不同大小顏色的方塊和圓形,組合出機器人雛型

  2. 利用 class 的方式,產出大量的機器人

  3. 藉由 class 內的 constructor ,讓每組機器人都能有不同的設定

  4. 除了讓機器人隨著時間變化外,結合使用者的滑鼠來讓機器人有更豐富的動態

  5. 匯出作品


這次跟 Hahow 好學校的合作,在臉書上利用直播跟大家聊聊程式的藝術創作,利用 p5.js 寫出可以和使用者互動的生成式藝術,大家也可以思考如何將這些方法拿來做出其他作品呢?
而在創作的過程中,不是一定要一次就到位,慢慢去調整作品,並思考生活中有什麼經驗或經歷能夠融入作品中,讓作品更精緻完整。

如果你因此對互動藝術程式創作產生興趣,歡迎加入老闆開的 Hahow 課程《互動藝術程式創作入門》,讓老闆跟你分享不同的創作!

互動藝術程式創作入門是為了不會程式的人設計的課程,課程中會帶你看看不一樣的作品,並從基礎引導大家一步步完成作品,透過每次的賞析、實作到修正作品,讓大家覺得寫 code 不是這麼難的事情,將這個過程想像成,拿一隻比較難的畫筆在進行創作,如果有機會使用它,便能夠做出和與眾不同的創作。

看完文章要用時卻忘光光?點選下方按鈕 收藏 此文章,我隨時都在這等你回來!



line-short

🎉 太棒了!你又學會了新知識!

▶ 用 5 分鐘再多學一點

▶ 推薦線上課程



(本文原載於 Creative Coding TW - 互動程式創作台灣站,原標題:用 p5.js玩創作,讓小機器人動起來!哲宇的互動藝術體驗(直播筆記)

1
給創作者來點掌聲!
1


吳哲宇

墨雨設計創辦人,紐約大學進修整合數位媒體碩士。互動設計師、藝術家、講師、全端工程師,引領超過 20,000 位學生進入互動網頁與生成藝術開發的大坑。

其他人也在看

更多文章