本記事は、株式会社 函館ラボラトリが運営する「Bラボ」における、大人向け「看板アプリ(デジタルサイネージ)を作るコース」用テキストです。
一からコードを書くわけではなく、あらかじめ用意したコードの一部を改変しながら、デジタルサイネージを作っていきましょう。
テンプレートプログラムは、下記GitHubリポジトリのLongTerm/Part3_Start
ディレクトリに入っています。
(12/16時点、本テキストは執筆中です)
前回のおさらい
Part2では、DateModuleやProgressBarModuleなど、時間の経過によって変化するモジュールを作りました。
ここからは、スクリーンショットが表示されているだけの状態であるRModuleを、実際のデータに連動するようにします。
必要なもの
- Part1で配置した電子部品
- OpenWeatherのアカウント
- API Key
- Googleアカウント
- バス時刻表APIのURL
- ごみカレンダーAPIのURL
- Twitterアカウント(Developer Accountとして、Twitter APIへの申請が承認されているもの)
- Consumer Key
- Consumer Key Secret
- Access Token
- Access Token Secret
OpenWeatherのAPI Keyを取得する方法については、下記記事「必要な変数の定義」を参照してください。
バス時刻表API、ごみカレンダーAPIを用意する方法については、下記2記事の手順を参照してください。
Twitter APIのキー4つを取得する手順については、下記記事「キーの取得」を参照してください。
RModuleの背景を作る
Part1でスクリーンショットだけ表示されるようにしたRModuleですが、このあとはスクリーンショットを置き換えていきます。
手始めに、各RModuleの背景を生成できるようにしましょう。
背景画像の生成には、PGraphicsの関数mask
というものを使います。
下記記事の「pgraphicsのmask関数」を参照してください。
mask関数で使う「型紙」にあたる部分を、関数で生成できるようにします。
// RModuleの背景画像をマスクするための図形(角丸の四角)を返す。
PGraphics sizeToModuleMask(Size size) {
int w = moduleWidth(size);
int h = moduleHeight(size);
PGraphics moduleBackgroundMask = createGraphics(w, h);
moduleBackgroundMask.beginDraw();
moduleBackgroundMask.noStroke();
moduleBackgroundMask.fill(255);
moduleBackgroundMask.rect(0, 0, w, h, RMODULE_RECT_ROUND);
moduleBackgroundMask.endDraw();
return moduleBackgroundMask;
}
初期化の段階で、initializeRModuleBackground()
という関数を作り、RModuleの背景を一括生成します。
// 各RModuleの背景を作成。
void initializeRModuleBackground() {
RModule module;
int w;
int h;
PImage back;
module = RModule.Weather;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
back = loadImage(WEATHER_PATH + "background.jpg");
weatherBackground = createGraphics(w, h);
weatherBackground.beginDraw();
weatherBackground.colorMode(HSB, 360, 100, 100, 100);
weatherBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
weatherBackground.fill(0, 0, 0, 40);
weatherBackground.noStroke();
weatherBackground.rect(0, 0, w, h);
weatherBackground.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
weatherBackground.mask( sizeToModuleMask( moduleSize(module) ) );
module = RModule.Bus;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
back = loadImage(BUS_PATH + "background.jpg");
busBackground = createGraphics(w, h);
busBackground.beginDraw();
busBackground.colorMode(HSB, 360, 100, 100, 100);
busBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
busBackground.fill(0, 0, 0, 40);
busBackground.noStroke();
busBackground.rect(0, 0, w, h);
busBackground.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
busBackground.mask( sizeToModuleMask( moduleSize(module) ) );
module = RModule.Gomi;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
back = loadImage(GOMI_PATH + "background.jpg");
gomiBackground = createGraphics(w, h);
gomiBackground.beginDraw();
gomiBackground.colorMode(HSB, 360, 100, 100, 100);
gomiBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
gomiBackground.fill(0, 0, 0, 40);
gomiBackground.noStroke();
gomiBackground.rect(0, 0, w, h);
gomiBackground.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
gomiBackground.mask( sizeToModuleMask( moduleSize(module) ) );
module = RModule.Twitter;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
twitterBackground = createGraphics(w, h);
twitterBackground.beginDraw();
twitterBackground.colorMode(HSB, 360, 100, 100, 100);
PImage twitterLogo = loadImage(TWITTER_PATH + "2021 Twitter logo - blue.png");
twitterBackground.background(0, 0, 100);
// Twitterのロゴを縮小し、右上に表示。
final float logoRatio = twitterLogo.height / float(twitterLogo.width); // 縦横比
final float logoWidth = 80.0;
final float logoHeight = logoWidth * logoRatio;
twitterBackground.image(twitterLogo, w-logoWidth-50, 50, logoWidth, logoHeight);
twitterBackground.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
twitterBackground.mask( sizeToModuleMask( moduleSize(module) ) );
module = RModule.OpenClose;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
back = loadImage(OPENCLOSE_PATH + "background_open.jpg");
openCloseBackgroundOpen = createGraphics(w, h);
openCloseBackgroundOpen.beginDraw();
openCloseBackgroundOpen.colorMode(HSB, 360, 100, 100, 100);
openCloseBackgroundOpen.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
openCloseBackgroundOpen.fill(0, 0, 0, 40);
openCloseBackgroundOpen.noStroke();
openCloseBackgroundOpen.rect(0, 0, w, h);
openCloseBackgroundOpen.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
openCloseBackgroundOpen.mask( sizeToModuleMask( moduleSize(module) ) );
back = loadImage(OPENCLOSE_PATH + "background_close.jpg");
openCloseBackgroundClose = createGraphics(w, h);
openCloseBackgroundClose.beginDraw();
openCloseBackgroundClose.colorMode(HSB, 360, 100, 100, 100);
openCloseBackgroundClose.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
openCloseBackgroundClose.fill(0, 0, 0, 40);
openCloseBackgroundClose.noStroke();
openCloseBackgroundClose.rect(0, 0, w, h);
openCloseBackgroundClose.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
openCloseBackgroundClose.mask( sizeToModuleMask( moduleSize(module) ) );
module = RModule.Temperature;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
back = loadImage(TEMPERATURE_PATH + "background.jpg");
temperatureBackground = createGraphics(w, h);
temperatureBackground.beginDraw();
temperatureBackground.colorMode(HSB, 360, 100, 100, 100);
temperatureBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
temperatureBackground.fill(0, 0, 0, 40);
temperatureBackground.noStroke();
temperatureBackground.rect(0, 0, w, h);
temperatureBackground.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
temperatureBackground.mask( sizeToModuleMask( moduleSize(module) ) );
module = RModule.Brightness;
w = moduleWidth( moduleSize(module) );
h = moduleHeight( moduleSize(module) );
back = loadImage(BRIGHTNESS_PATH + "background_bright.jpg");
brightnessBackgroundBright = createGraphics(w, h);
brightnessBackgroundBright.beginDraw();
brightnessBackgroundBright.colorMode(HSB, 360, 100, 100, 100);
brightnessBackgroundBright.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
brightnessBackgroundBright.fill(0, 0, 0, 40);
brightnessBackgroundBright.noStroke();
brightnessBackgroundBright.rect(0, 0, w, h);
brightnessBackgroundBright.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
brightnessBackgroundBright.mask( sizeToModuleMask( moduleSize(module) ) );
back = loadImage(BRIGHTNESS_PATH + "background_not_bright.jpg");
brightnessBackgroundNotBright = createGraphics(w, h);
brightnessBackgroundNotBright.beginDraw();
brightnessBackgroundNotBright.colorMode(HSB, 360, 100, 100, 100);
brightnessBackgroundNotBright.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
brightnessBackgroundNotBright.fill(0, 0, 0, 40);
brightnessBackgroundNotBright.noStroke();
brightnessBackgroundNotBright.rect(0, 0, w, h);
brightnessBackgroundNotBright.endDraw();
// 背景画像を角丸四角の形に合わせてくり抜く。
brightnessBackgroundNotBright.mask( sizeToModuleMask( moduleSize(module) ) );
}
関数initialize()
の中で呼び出すようにします。
void initialize() {
initializeImage();
initializeRModuleBackground(); // 追加
updateNowPageID(true);
}
各RModuleの実装
ここからは、スクリーンショットだけ表示されているRModuleを、APIやセンサなどと連動できるようにします。
このあとの実装のために、現在RModuleの描画に使っている7関数の中で、image
関数が使われている記述を削除(またはコメントアウト)してください。
void drawWeatherRModule(Area area) {
RModule module = RModule.Brightness;
Size size = moduleSize(module);
int x = layoutGuideX(area);
int y = layoutGuideY(area);
int w = moduleWidth(size);
int h = moduleHeight(size);
//image(loadImage(WEATHER_PATH + "sample.jpg"), x, y); // 削除かコメントアウト
}
下記記事の解説を参照しながら進めてください。
起動画面を表示できるようにする
起動画面については、下記記事で解説しています。
起動時に、「どの項目まで読み込みが完了したか」を示す変数を、順番にtrue
にしていきます。
「どの項目まで読み込みが完了したか」を示す変数は以下です。
// 起動画面用の初期化済フラグ
boolean isInitializedImages = false;
boolean isInitializedDates = false;
boolean isInitializedWeather = false;
boolean isInitializedBus = false;
boolean isInitializedGomi = false;
boolean isInitializedTwitter = false;
初期化用関数を下記のように書き換えます。
void initialize() {
initializeDate();
isInitializedDates = true;
initializeImage();
initializeRModuleBackground();
isInitializedImages = true;
isUpdatedWeather = updateWeather();
isInitializedWeather = true;
isUpdatedBus = updateBus();
isInitializedBus = true;
isUpdatedGomi = updateGomi();
isInitializedGomi = true;
isUpdatedTwitter = updateTwitter();
isInitializedTwitter = true;
isUpdatedOpenClose = updateOpenClose();
isUpdatedTemperature = updateTemperature();
isUpdatedBrightness = updateBrightness();
}
起動画面描画用の関数を作成します。
void drawLaunchingScreenModule() {
drawFullImageModule(background);
drawText(CENTER, BASELINE, GREEN_COLOR, 148, "DIGITAL SIGNAGE", width/2, height/2-380);
drawText(CENTER, BASELINE, BLACK_COLOR, 48, LOCATION, width/2, height/2-180);
stroke(BLACK_COLOR);
strokeWeight(5);
noFill();
line(300, height/2-200, width-300, height/2-200);
/* ここから追加 */
if (isInitializedDates) {
drawText(CENTER, BASELINE, GREEN_COLOR, 36, "カレンダー取得", width/2, height/2+40);
} else {
drawText(CENTER, BASELINE, BLACK_COLOR, 36, "カレンダー取得", width/2, height/2+40);
}
if (isInitializedImages) {
drawText(CENTER, BASELINE, GREEN_COLOR, 36, "画像取得", width/2, height/2+100);
} else {
drawText(CENTER, BASELINE, BLACK_COLOR, 36, "画像取得", width/2, height/2+100);
}
if (isInitializedWeather) {
drawText(CENTER, BASELINE, GREEN_COLOR, 36, "天気情報取得", width/2, height/2+160);
} else {
drawText(CENTER, BASELINE, BLACK_COLOR, 36, "天気情報取得", width/2, height/2+160);
}
if (isInitializedBus) {
drawText(CENTER, BASELINE, GREEN_COLOR, 36, "バス情報取得", width/2, height/2+220);
} else {
drawText(CENTER, BASELINE, BLACK_COLOR, 36, "バス情報取得", width/2, height/2+220);
}
if (isInitializedGomi) {
drawText(CENTER, BASELINE, GREEN_COLOR, 36, "ごみカレンダー取得", width/2, height/2+280);
} else {
drawText(CENTER, BASELINE, BLACK_COLOR, 36, "ごみカレンダー取得", width/2, height/2+280);
}
if (isInitializedTwitter) {
drawText(CENTER, BASELINE, GREEN_COLOR, 36, "ツイート取得", width/2, height/2+340);
} else {
drawText(CENTER, BASELINE, BLACK_COLOR, 36, "ツイート取得", width/2, height/2+340);
}
/* ここまで追加 */
}
関数draw()
内で、起動画面を表示できるようにします。
void draw() {
if (nowPageID == -1) {
// データの初期化が完了するまで、起動画面を表示する。
drawLaunchingScreenModule(); // 追加
} else {
// データを更新したあと、各モジュールを描画する。
updateDatas();
drawModules();
}
}
これで起動画面が表示されるようになります。
RModuleの影を表示する
実装してきたRModuleを立体的に見せるため、影をつくります。
下記記事の通りに進めてください。
最後に
これでデジタルサイネージの実装は完了です。
もし余力があれば、各モジュールをカスタマイズしてみてください。
お疲れ様でした!