Processing程式語言回顧_基本語法練習回顧_程式簡化_物件導向_Part1

最近剛結束當兵第一階段難熬的新訓

由於生病不舒服在家養病

剛當完兵回來

說實在的於軍中12天

腦袋早已秀斗、生鏽差不多了

迅速重新運作成進去前的智商


再次來使用 以前大學所學過的


P語言(Processing) /  based on JAVA

這門有趣易學的程式語言

溫故程式語法的手感吧!!!!
(在此使用之版本為3.3.5的Processing)

目標:
1.Processing 語法暖身
2.簡化程式練習
3.練習Ball設計藍圖(Class)與 物件實體建立、調用

===================================================================
一、基本語法練習回顧

先準備好  Processing 程式語言的主要架構




先準備好一個 寬640 高480大小   的畫布


準備完畫布之後  我們先來畫一個橢圓

我們沿著 畫布以最左上角座標(0,0)為起始位置
水平 x 200個單位(pixel)
垂直往下 100個單位(pixel)
繪製一個寬(width) 與 高(height) 皆為  80的橢圓(切記!! 非圓形)





接著我們希望這個橢圓能夠左右水平移動

我們宣告一個變數用來存取  x座標位置

一開始橢圓位在100
之後遞增1個單位


然後我們開始發現會出現畫圖的歷史移動軌跡
我們想消除這段彗星尾巴

因為我們於  draw()  區塊中只有針對  橢圓的x座標去做每次畫圖的更新

卻遺漏了背景每次也要跟著畫  跟著改變 再次上色

而且是在  橢圓移動繪製之前做

在 Processing  中我們使用  background 函數
做一些特定顏色的繪製

這裡設置0代表純黑色

那就會顯示像是    一顆白色雞蛋在夜空水瓶漂浮移動般



155則代表一半的濃度  也就是灰色

255則全白


當然我們也可指定   background(r ,g ,b);
依此類推



然後這個時候你又希望這個雞蛋能夠有彈性

會反彈回來

而不要只是一直向前移動


先過去

這裡也建議x所遞增的變量能夠在最上層宣告為全域變數
有個好處就是要改很好改
這裡我們將原本的 x+= 1 或 x++ 寫法
改寫在最外圍 上面部分 後續要改
若程式碼寫太長可以直接於開頭找尋並修改

(PS:  在此下方程式碼中  dx 其實講白話些  數值給愈大 雞蛋移動就愈快!!!
往後若要調整速度就只需要改一個地方即可)



只要x位置超出 整張畫布的寬就
再反方向彈回來



好  右邊雖然已經確實可以反彈但左邊又有超出不反彈的毛病了

我們再補上一道  if 判斷



這樣就模擬出一顆具有彈性的雞蛋了


使用 ScreenToGif
link : https://briian.com/15950/

但是依然有瑕疵
就是這顆雞蛋是以抵達  center的時候才反彈
不是照著其  perimeter 去偵測

我們是希望觸碰到邊界立即反彈

這裡算是 Processing程式語言中的一個規範潛藏的小問題
它只能得知這顆橢圓的中心位置

所以起始的X座標位置其實位在   這顆雞蛋的中心處
至於雞蛋總寬為  120  ,  一半則60
因此只要對X多加60即可獲得  靠右側的雞蛋邊界
靠左側 , 則反之



重新修正過後的Code


第一版.雞蛋左右反彈程式碼

int x = 100;
int dx = 5;
void setup(){
  size(640,480);
}

void draw(){
  background(255,0,0);
  ellipse(x,100,120,80);
  //x++;
  x +=dx;
  if(x+60 > width){
    dx = -5;
  }
  if(x-60 < 0){
    dx = 5;
  }
}


二、程式簡化

Optimise the Code 階段



我們其實會發現這裡寫了兩個  if statement
其實沒捨麼必要 重複性太高(做的事情相似)

當撞到邊界就反向
後續其實只需要用一個 if statement 包覆即可
將兩個條件合併
然後做某些事



當然啦   在 第一版.雞蛋左右反彈程式碼中
我們針對反彈主要都是做純方向性的遞增 or 遞減 位移

其實也可利用前面說到的   特別宣告出來的  dx 全域變數
來做 * -1 的反向狀態更改喔!!!!!


第二版.簡化過後的雞蛋左右反彈程式碼

從18行 變  15行 程式碼 降低Code 重複性



int x = 100;
int dx = 10;
void setup(){
  size(640,480);
}

void draw(){
  background(255,0,0);
  ellipse(x,100,120,80);
  //x++;
  x +=dx;
  if(x+60 > width || x-60 < 0){
    dx = dx*-1;
  }
}



但你後來又發現這樣的寫法
雖然很直觀
但假若我今天又要加另一顆球然後是上下反彈的話呢???

你說很簡單
那就直接再多畫一個球然後一樣設置兩個上下邊界超出 的判斷


簡化前版本

int x = 100;
int dx = 5;
int y = 50;
int dy = 5;
void setup() {
  size(640, 480);
}

void draw() {
  background(255, 0, 0);
  ellipse(x, 100, 120, 80);
  //x++;
  x +=dx;
  if (x+60 > width) {
    dx = -5;
  }
  if (x-60 < 0) {
    dx = 5;
  }
  ellipse(400, y, 50, 80);
  y += dy;
  if (y+40 > height) {
    dy = -5;
  }
  if (y-40 <0) {
    dy = 5;
  }
}


簡化後版本


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int x = 100;
int y = 50;
int dx = 5;
int dy = 5;
void setup() {
  size(640, 480);
}
void draw() {
  background(255, 0, 0);
  ellipse(x, 100, 120, 80);
  x +=dx;
  if(x+60 > width || x-60 < 0){
    dx = dx*-1;
  }
  ellipse(400, y, 50, 80);
  y += dy;
  if (y+40 > height || y-40 <0) {
    dy = dy*-1;
  }
}

所以這樣就簡化完了嗎????
仔細看你會發覺

有兩區塊程式碼其實有點相似



如果是如此
會發現
重複的程式碼一而再,再而三出現
每當我要再多畫一個會反彈的球或雞蛋的時候

就必須要重新在畫橢圓然後再給他一個 if 判斷
若我畫八個不同的圓
就會出現八組這樣的區塊  ---------------------->重複性極高!!!!


為此我們可以用  物件導向
來創建   球 的類別(物件之設計藍圖)

往後若有需要使用特定反彈球的動作
就直接建立一個物件模塊  後續再設定特定反彈參數即可

我們再對程式碼做重構優化它
(optimise the code)


三、物件導向

如何在  Processing中 創建一個 Class檔案並於主檔做調用

先 New Tab (Ctrl + Shift + N)


命名之


定義Ball類別 (檔案明即類別名 ------> 和Unity有點像)
相關private的屬性欄位
跟public 給別檔案用的動作執行方法

那這個時候我們就開始在想
一顆要能上下左右甚至斜邊移動的球 類別
要如何設計???
需要捨麼元素

目前我先想到的就是
至少要有
1.球自己的x , y 座標位置
2.然後就是對應的 dx ,dy 位移變化量
3.這顆球又是多大??  球的寬高大小   用  size 定義(我們目前暫時先探討正圓型的球)
4.接著這顆球必須要能顯示出來 void display()
5.此外還必須能移動 void move()

所以感覺先寫這樣好了


主檔區塊
先實體化 該顆球物件
之後依序調用display() 及 move()兩個方法

成果如下

由於我是使用默認原本類別的x,y位置
所以每次執行都是從左上角初始化垂直掉下來,
因為dx變化量也固定住設為0  dy位移速度也固定為默認的+5(往下)
此外球還不會反彈

因此這裡對於 Ball 類別我們再做些更新
1.我們需要可自己設定球的dx,dy移動速度


如此我們就能將許多複雜的底層運作封裝於
類別方法之中
在主檔案區塊簡單一行調用即可

2.需獲取x,y,dx,dy的量
(藉由公開的方法間接獲得並設定之)

我們再次模擬一次垂直降落的球物件實體
並探討如何解決球不會反彈之問題


在此我們先用同樣的判斷條件寫法
與剛才相同
只不過這裡是調用物件中屬性
兩個上下邊界條件包覆於同一判斷區塊做反彈


目前程式

主檔區塊

Ball ball = new Ball();
void setup() {
  size(640, 480);
}
void draw() {
  background(255, 0, 0);
  ball.display();
  ball.move();
  
  //ball.setDx(5);
  //ball.setDy(5);
  if(ball.getY()+25 > height 
        || ball.getY()-25<0){
    ball.setDy(ball.getDy()*-1);
  }
}



Ball類別區塊

public class Ball {
  private int y = 30;
  private int x = 30;
  private int dy = 5;
  private int dx = 0;
  private int size = 50;

  public void move() {
    y+=dy;
    x+=dx;
  }
  //have ability to change dx and dy
  public void setDx(int dx) {
    this.dx = dx;
  }
  public void setDy(int dy) {
    this.dy = dy;
  }
  //have ability to see where our ball located and the values of dx ,dy
  public int getX(){
    return this.x;
  }
  public int getY(){
    return this.y;
  }
  public int getDx() {
    return this.dx;
  }
  public int getDy() {
    return this.dy;
  }

  public void display() {
    ellipse(x, y, size, size);
  }
}








留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題