2026年2月12日 星期四

[OTTO GO]動畫測試-張閉嘴





Arduino
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>

#define TFT_MOSI 19   // SDA (SPI MOSI)
#define TFT_SCLK 18   // SCL (SPI SCK)
#define TFT_CS    5
#define TFT_DC   16
#define TFT_RST  23

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

static inline uint16_t RGB565(uint8_t r, uint8_t g, uint8_t b) {
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

// 畫口鼻白毛區(固定底圖)
void drawMuzzleBase(int cx, int cy) {
  uint16_t furLight = RGB565(235, 228, 210);
  uint16_t shadow   = RGB565(160, 150, 135);

  tft.fillScreen(ST77XX_BLACK);

  // 白毛區
  tft.fillCircle(cx - 55, cy + 10, 55, furLight);
  tft.fillCircle(cx + 55, cy + 10, 55, furLight);
  tft.fillRoundRect(cx - 90, cy - 25, 180, 110, 28, furLight);

  // 下巴陰影
  tft.fillRoundRect(cx - 85, cy + 60, 170, 22, 12, shadow);
  tft.fillRoundRect(cx - 40, cy + 55, 80, 10, 5, RGB565(120,110,100));

  // 鬍鬚點
  uint16_t dotDark = RGB565(70, 70, 70);
  int dotY1 = cy + 6;
  int dotY2 = cy + 26;
  for (int k = 0; k < 3; k++) {
    tft.fillCircle(cx - 55 - k * 12, dotY1 + k * 2, 2, dotDark);
    tft.fillCircle(cx - 55 - k * 12, dotY2 + k * 2, 2, dotDark);
    tft.fillCircle(cx + 55 + k * 12, dotY1 + k * 2, 2, dotDark);
    tft.fillCircle(cx + 55 + k * 12, dotY2 + k * 2, 2, dotDark);
  }
}

// 畫「閉合」虎嘴
void drawMouthClosed(int cx, int cy) {
  uint16_t lineDark = RGB565(40, 40, 40);

  // 人中線
  tft.drawLine(cx,     cy - 5, cx,     cy + 20, lineDark);
  tft.drawLine(cx - 1, cy - 5, cx - 1, cy + 20, RGB565(80,80,80));

  // 上唇弧線
  int mouthY = cy + 18;
  for (int i = 0; i < 34; i++) {
    int xL = cx - i;
    int yL = mouthY + (i * i) / 95;
    tft.drawPixel(xL, yL, lineDark);
    tft.drawPixel(xL, yL + 1, lineDark);

    int xR = cx + i;
    int yR = mouthY + (i * i) / 95;
    tft.drawPixel(xR, yR, lineDark);
    tft.drawPixel(xR, yR + 1, lineDark);
  }

  // 嘴角加深
  tft.fillCircle(cx - 36, mouthY + 12, 3, lineDark);
  tft.fillCircle(cx + 36, mouthY + 12, 3, lineDark);

  // 嘴中間小暗線
  tft.fillRoundRect(cx - 10, mouthY + 20, 20, 4, 2, RGB565(90,80,70));
}

// 畫「張開」虎嘴
void drawMouthOpen(int cx, int cy) {
  uint16_t lineDark = RGB565(40, 40, 40);
  uint16_t mouthIn  = RGB565(15, 15, 15);      // 口腔黑
  uint16_t tongue   = RGB565(210, 120, 120);   // 舌頭粉

  // 人中線(到上唇)
  tft.drawLine(cx,     cy - 5, cx,     cy + 10, lineDark);
  tft.drawLine(cx - 1, cy - 5, cx - 1, cy + 10, RGB565(80,80,80));

  // 上唇弧線(稍微更彎一點)
  int mouthY = cy + 16;
  for (int i = 0; i < 36; i++) {
    int bend = (i * i) / 85;

    int xL = cx - i;
    int yL = mouthY + bend;
    tft.drawPixel(xL, yL, lineDark);
    tft.drawPixel(xL, yL + 1, lineDark);

    int xR = cx + i;
    int yR = mouthY + bend;
    tft.drawPixel(xR, yR, lineDark);
    tft.drawPixel(xR, yR + 1, lineDark);
  }

  // 嘴角
  tft.fillCircle(cx - 38, mouthY + 14, 3, lineDark);
  tft.fillCircle(cx + 38, mouthY + 14, 3, lineDark);

  // 口腔(張開的洞)
  // 用圓角矩形 + 下半圓做出比較像嘴巴的形狀
  int w = 90, h = 45;
  int x = cx - w/2;
  int y = cy + 28;
  tft.fillRoundRect(x, y, w, h, 18, mouthIn);
  tft.fillCircle(cx, y + h, 22, mouthIn);

  // 下唇線(口腔上緣加一條暗線)
  tft.drawRoundRect(x, y, w, h, 18, RGB565(60,50,45));

  // 舌頭
  tft.fillRoundRect(cx - 22, y + 18, 44, 20, 10, tongue);
  tft.fillCircle(cx, y + 36, 14, tongue);
}

void drawTigerMouthFrame(bool open) {
  int cx = tft.width() / 2;
  int cy = tft.height() / 2;

  drawMuzzleBase(cx, cy);

  if (open) drawMouthOpen(cx, cy);
  else      drawMouthClosed(cx, cy);
}

void setup() {
  SPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS);
  tft.init(240, 320);     // 若你確定是 170x320 再改成 tft.init(170, 320)
  tft.setRotation(1);

  drawTigerMouthFrame(false); // 先閉合
}

void loop() {
  // 2 秒後張開
  delay(2000);
  drawTigerMouthFrame(true);

  // 2 秒後閉合
  delay(2000);
  drawTigerMouthFrame(false);
}


沒有留言:

張貼留言