processing

ProcessingでTextureを一切使わずにコードだけでポプ子を描画してみた

ポプテピピックの登場人物であるポプ子(短いほう)の頭部をProcessingで実装してみました。
表題にもある通り 「Textureを一切使わずにコードだけで実装」 と言う謎の縛りプレイ?をしている為に、実装の方は力技のバーゲンセールな感じかもしれません。
(力技の部分について参考になるかは不明ですが...)実装時のポイントなどを簡潔にメモしていければと思います。

※Processing 3.3.6で動作確認

なお、コード一式についてはGitHubにアップしております。
mao-test-h/PopukoDraw

追記

魔が差してピピ美の方も実装してしまいました。
実装時のテクは違えども、重複する点が多いのでGitHubのリンクだけ載せておきます。

mao-test-h/PipimiDraw

顔の実装

顔のパーツについて、基本的には2D Primitivesを用いる形で実装しております。
実装を全部載せると長くなるので割愛しますが、以下に例として鼻と眉の実装を記載します。

    • ellipseを任意の位置に描画して黒く塗りつぶしているだけです。
    • こちらはarc関数で円弧を2つ描画する形で口を成形しております。

Popuko.pde

  // 鼻の描画
  void drawNose()
  {
    noStroke();
    fill(0);

    PVector NosePos = new PVector(0, 44);
    PVector NoseSize = new PVector(5, 5);
    ellipse(NosePos.x, NosePos.y, NoseSize.x, NoseSize.y);
  }


  // 眉の描画
  void drawEyebrows(int sign)
  {
    noFill();
    strokeWeight(StrokeWeight);
    stroke(0);
    strokeCap(SQUARE);

    PVector EyebrowsPos = new PVector(65, -76);
    pushMatrix();
    translate((EyebrowsPos.x * sign), EyebrowsPos.y);

    PVector EyebrowsPartsPos = new PVector(0, 0);
    PVector EyebrowsPartsSize = new PVector(72, 41);
    float arcStart = -160;  // 円の開始する角度(deg)
    float arcEnd = -20; // 円の停止する角度(deg)
    arc(EyebrowsPartsPos.x, EyebrowsPartsPos.y, EyebrowsPartsSize.x, EyebrowsPartsSize.y, radians(arcStart), radians(arcEnd));

    popMatrix();
  }

目の実装

目の実装については以下の記事を参考にさせて頂きました。

一点変更点として、目を2つ描画する際に片方のまつ毛を反転させることで左右対称にする形に変更しております。

髪の実装

実装するのが一番アレだった箇所です。
こちらは単純に2D Primitivesだけでなく「Fillで塗りきれていない箇所は頂点を指定して塗りつぶし用の図形を描画」「curveや2D Primitivesを駆使してヘアスタイルや髪飾りを成形」と言った実装を行っております。

こちらも全体を載せると長くなるので割愛しますが、以下に例として横髪部分の実装を載せておきます。
コード全体につきましてはGitHubのコードを御覧ください。

  • 横髪について、1つのcurveだけでは描画しきれないので内側と外側に分けております。
    • その上でこちらも2回描画を行い、片方を反転させることで左右対称にしております。

Hair.pde

  // サイドへアー(外側)の描画
  void drawSideHairOutside(int sign)
  {
    fill(HairColor);
    stroke(0);

    PVector SideHairOutside_BeginningControlPoint = new PVector(-400 * sign, 322);
    PVector SideHairOutside_FirstPoint = new PVector(130 * sign, 40);
    PVector SideHairOutside_SecondPoint = new PVector(123 * sign, -208);
    PVector SideHairOutside_EndingControlPoint = new PVector(-140 * sign, -180);
    curve(
        SideHairOutside_BeginningControlPoint.x, SideHairOutside_BeginningControlPoint.y,
        SideHairOutside_FirstPoint.x, SideHairOutside_FirstPoint.y,
        SideHairOutside_SecondPoint.x, SideHairOutside_SecondPoint.y,
        SideHairOutside_EndingControlPoint.x, SideHairOutside_EndingControlPoint.y); 
  }

  // サイドへアー(内側)の描画
  void drawSideHairInside(int sign)
  {
    fill(SkinColor);
    stroke(0);
    PVector SideHairInside_BeginningControlPoint = new PVector(40 * sign, -369);
    PVector SideHairInside_FirstPoint = new PVector(123 * sign, -180);
    PVector SideHairInside_SecondPoint = new PVector(130 * sign, 40);
    PVector SideHairInside_EndingControlPoint = new PVector(66 * sign, 73);
    curve(
        SideHairInside_BeginningControlPoint.x, SideHairInside_BeginningControlPoint.y,
        SideHairInside_FirstPoint.x, SideHairInside_FirstPoint.y,
        SideHairInside_SecondPoint.x, SideHairInside_SecondPoint.y,
        SideHairInside_EndingControlPoint.x, SideHairInside_EndingControlPoint.y); 
  }

その他参考情報

Tweakモードについて

ProcessingにはTweakモードと言う機能があり、簡単に紹介すると実行中に値を動的に変更させる事が出来る機能があります。
(挙動としてはUnityのInspectorに近い印象です)
その上で変更した値については停止時に値を反映するかどうか選択することが出来ます。

kuso2.gif

注意点としてdraw関数内の値でないと変更値が反映されないと言った制約があるみたいなので変数の扱いについては注意する必要があります。とは言え、こちらの機能を概ね使いこなせるようになると試行錯誤が大分しやすくなるかと思われるのでオススメの機能となります。
※今回の実装についても大体のパラメータがdraw関数内にあるのはこちらが主な理由となります。

参考/関連資料