【直播精華筆記】用程式玩創作,讓小機器人動起來!哲宇的互動藝術體驗
不會寫 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 中分為三個步驟:
-
創造一個新變數指定為空陣列 將每次創造的機器人使用 js 中的 push() 儲存到陣列中。要注意的是,這邊使用的 push () 是 js 語法,而我們先前在繪製機器人使用的 push 則是 p5.js 內儲存畫布狀態的語法。
-
setup() 中,new 出不同的機器人,並傳入不同的位置
-
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 創作出互動機器人的流程:
-
透過不同大小顏色的方塊和圓形,組合出機器人雛型
-
利用 class 的方式,產出大量的機器人
-
藉由 class 內的 constructor ,讓每組機器人都能有不同的設定
-
除了讓機器人隨著時間變化外,結合使用者的滑鼠來讓機器人有更豐富的動態
-
匯出作品
這次跟 Hahow 好學校的合作,在臉書上利用直播跟大家聊聊程式的藝術創作,利用 p5.js 寫出可以和使用者互動的生成式藝術,大家也可以思考如何將這些方法拿來做出其他作品呢?
而在創作的過程中,不是一定要一次就到位,慢慢去調整作品,並思考生活中有什麼經驗或經歷能夠融入作品中,讓作品更精緻完整。
如果你因此對互動藝術程式創作產生興趣,歡迎加入老闆開的 Hahow 課程《互動藝術程式創作入門》,讓老闆跟你分享不同的創作!
互動藝術程式創作入門是為了不會程式的人設計的課程,課程中會帶你看看不一樣的作品,並從基礎引導大家一步步完成作品,透過每次的賞析、實作到修正作品,讓大家覺得寫 code 不是這麼難的事情,將這個過程想像成,拿一隻比較難的畫筆在進行創作,如果有機會使用它,便能夠做出和與眾不同的創作。
看完文章要用時卻忘光光?點選下方按鈕
收藏此文章,我隨時都在這等你回來!
🎉 太棒了!你又學會了新知識!
▶ 用 5 分鐘再多學一點
▶ 推薦線上課程
(本文原載於 Creative Coding TW - 互動程式創作台灣站,原標題:用 p5.js玩創作,讓小機器人動起來!哲宇的互動藝術體驗(直播筆記)