GoodpatchDay 22

プログラミング初心者がPebble Time Watchfaceを作ってみた

More than 3 years have passed since last update.

こんにちは、この記事はGoodpatchのエンジニア※がお送りするGoodpatch Advent Calendar 2015の22日目の記事です。

先日はAndroidエンジニアの @u-16 MaterialDesignと戯れてみて思ったこととか でした。

私はProttでプロジェクトマネージャーをやっています。今回は、プログラミング未経験でPebble TimeのWatchfaceを開発してみた話を非エンジニア枠でひっそり書いてみようと思います。


Pebble Timeって?

Pebble TimeはスマートウォッチのひとつでApple WatchやAndroid Wearと違う点は


  • OS:独自のPebble OS

  • ペアリング:AndroidとiOSの両方とペアリングが可能!

  • 操作方法:タップではなく、左右についているボタンのみ

です。他、Pebble Time Roundはびっくりするくらい薄くて、普通の時計をつけている感覚でつけていられます。

アニメーションがいい感じで使っていてすごい楽しいです。Watchfaceはおそらく非公式であろうキャラクターものや、アナログ、デジタル共にたくさんあります。

 

Pebble Time for Androidはこんなかんじ。


Watchfaceを作ってみる


まずはじめに


開発環境

Cloud Pebbleという、オンラインの開発環境で今回は開発をしました。


言語

基本的にC言語での開発になりますが、Simply.jsを利用することで Pebble.jsを利用することで JavaScript のみの開発もできるとのこと。

https://developer.getpebble.com/

英語ですが、公式できちんとドキュメントを用意してくれています。

image


画面サイズとか

Pebble Timeはシリーズで現在までで、3種類出てるので画面サイズなどはそれぞれ、下記で用意が必要です。

pic 2015-12-22 1.24.46.png

DEVELOPER GUIDES

pic 2015-12-21 19.39.43.png

COLOR PICKER

Pebble内では上記の色のみ使用可能なので、デザインするときは上記を参考にしながら作ります。

BENEFITS OF DESIGN GUIDELINES

デザインガイドラインも用意してあります。

上記に目を通してみて、初めてのプログラミングは、まず簡単にこんな感じに作ってみよう!っていうことで開発をスタート。

pic 2015-12-21 19.37.50.png


作ってみた

とりあえずチュートリアルとか、サンプルコードを引っ張ってきて、理解しながらやってみました。初めてのプログラミングでも、コードをちゃんと読めば理解できるので、いいかんじ。


main.c


#include <pebble.h>

static Window *s_main_window;

static GBitmap *s_bitmap;
static BitmapLayer *s_bitmap_layer;

static TextLayer *s_time_layer, *s_date_layer;

static void update_time() {
// Get a tm structure
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);

// Write the current hours and minutes into a buffer
static char s_buffer[8];
strftime(s_buffer, sizeof(s_buffer), clock_is_24h_style() ?
"%H:%M" : "%I:%M", tick_time);

// Display this time on the TextLayer
text_layer_set_text(s_time_layer, s_buffer);

// Copy date into buffer from tm structure
static char date_buffer[16];
strftime(date_buffer, sizeof(date_buffer), "%b %d (%a)", tick_time);

// Show the date
text_layer_set_text(s_date_layer, date_buffer);

}

static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
update_time();
}

static void main_window_load(Window *window) {
// Get information about the Window
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);

// Create the TextLayer with specific bounds
s_time_layer = text_layer_create(
GRect(0, PBL_IF_ROUND_ELSE(48, 52), bounds.size.w, 50));

// Create time TextLayer
text_layer_set_background_color(s_time_layer, GColorClear);
text_layer_set_text_color(s_time_layer, GColorBlack);
text_layer_set_text(s_time_layer, "00:00");
text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_MEDIUM_NUMBERS));
text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);

// Add it as a child layer to the Window's root layer
layer_add_child(window_layer, text_layer_get_layer(s_time_layer));

// Create date TextLayer
s_date_layer = text_layer_create(GRect(0, 93, 180, 40));
text_layer_set_text_color(s_date_layer, GColorBlack);
text_layer_set_background_color(s_date_layer, GColorClear);
text_layer_set_font(s_date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter);

// Add to Window
layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_date_layer));

s_bitmap = gbitmap_create_with_resource(RESOURCE_ID_DOG);
s_bitmap_layer = bitmap_layer_create(bounds);
bitmap_layer_set_bitmap(s_bitmap_layer, s_bitmap);
bitmap_layer_set_compositing_mode(s_bitmap_layer, GCompOpSet);
layer_add_child(window_layer, bitmap_layer_get_layer(s_bitmap_layer));
}

static void main_window_unload(Window *window) {
// Destroy TextLayer
text_layer_destroy(s_time_layer);
bitmap_layer_destroy(s_bitmap_layer);
gbitmap_destroy(s_bitmap);

}

static void init() {
// Create main Window element and assign to pointer
s_main_window = window_create();

// Set handlers to manage the elements inside the Window
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = main_window_load,
.unload = main_window_unload
});

// Show the Window on the watch, with animated=true
window_stack_push(s_main_window, true);

// Make sure the time is displayed from the start
update_time();

// Register with TickTimerService
tick_timer_service_subscribe(MINUTE_UNIT, tick_handler);
}

static void deinit() {
// Destroy Window
window_destroy(s_main_window);
}

int main(void) {
init();
app_event_loop();
deinit();
}


基礎もなにも勉強してないので、最初によくわからなかったポイントは表示位置の変更ルールでした。

日にちを表示させる

s_date_layer = text_layer_create(GRect(0, 93, 180, 40));

ここの(GRect(0, 93, 180, 40));数字を動かしてあれやこれややってみて、表示したい位置にしてみたけど本当は

(x,y,w,h)できちんと計算してやらなければいけないとのこと。


画像のアップロード

RESOURCESのADD NEWからファイルがアップロードできます。

IDENTIFITERでコード内で指定する名前に設定。

pic 2015-12-22 0.15.22.png


フォント

システムフォントが何種類か用意されてます。もちろん、カスタムフォントも使えます。今回はシステムフォントを使っています。

https://developer.getpebble.com/guides/pebble-apps/display-and-animations/ux-fonts/


動作確認

実機でもエミュレーターでもできます。COMPILATIONからどっちで表示させるか変更する。

pic 2015-12-22 0.26.28.png


CloudPebbleでの動作確認

pic 2015-12-22 0.13.33.png

右上の緑の再生ボタンを押すと確認できます。こんな感じ。


Pebble実機での確認

Pebble TimeアプリをDeveloper Connectionで接続すると、確認できるようになります

作ったものが実機ですぐ確認できます。最初はベースの色を黒ではなく、グレーにしてたんだけどすごく薄く見えたので、黒に変更したりしました。


とりあえず完成!

一旦、上記の流れで

Watchface内で画像を表示させる、時間を表示させる、日にちを表示させるということをやってみました。

全部含めて、2時間もかかりませんでした!


もっと動きがあるもの作りたい...

Pebble Watchfaceを見ていると、動きがあってかわいいものが多い。ということで、やってみました。


Animation png

Pebbleはgif animationが使えないので、apngを作ります。使ったサービスは下記。

普通のpng表示と違うコードが必要になります。

https://developer.getpebble.com/guides/pebble-apps/resources/animated-pngs/#using-apngs-in-code


あれ...動かない。

いろいろやってみたけど、apngまわりでとっても苦戦しました。下記を何度も確認して、やっと動くようになりました...


Raw binary blobでアップロードしているかどうか

ファイルアップロードする際に、pngとか他のものでアップロードすると動いてくれません。


ファイルサイズが大きすぎていないか

あまり大きすぎると、Pebble上で動いてくれません。20KB以下くらいがよさそう。10KB以下がベスト。


コードをみてみる

Pebble公式のANIMATED PNGS 内のコードで動かしてみたけど、動かず。

HOW-TO replace with your own animated .gif

ここを見てやってみたら、動くようになりました。(まだちゃんと確認できていないけど)


一応動いた!

image

こんなかんじ。だけど、なぜか実機で確認ができない...謎です。まだ解決できていません。。


一通りやってみて

たのしい。だけど、ちょっと難しいことをやろうとすると全然理解できないので、もっと精進します...

一応簡単なWatchfaceを作れるようになったので、もうちょっとブラッシュアップして、アプリのストアに出してみようと思います!

ŧ‹"ŧ‹"ŧ‹"ŧ‹"(๑´ㅂ`๑)ŧ‹"ŧ‹"ŧ‹"ŧ‹"


明日は @migi さんです。おたのしみに!


追記:2016/04/21

スクリーンショット 2016-04-12 9.22.16.png

ひっそり、初めてWatchfaceリリースしてみました。(上記のものとは違うけど) Appstoreへのアップ方法など追ってまとめたいと思います!

https://apps.getpebble.com/en_US/application/570b8e4d5523d02f2e000021