プチコンで遊ぼう! (はてなブログ版)

任天堂3DSのプチコンで遊ぼう! [twitter:@eida_s]

はてなダイアリーから移行しました。 はてなダイアリーのURLを開いても自動的にこちらにリダイレクトされますのでご了承ください。

calc84maniacさんのRaycastingライブラリ(その1:ライブラリ紹介)

calc84maniacさんが公開している「Spooky Maze」、DOOMのような画面を実現している。
最近ではバグタローさんの「GIVERS-P3D」にも驚いたが、この「Spooky Maze」もすごい。
(後々はP3Dライブラリの解説もしてみたいです。バグタローさんご本人が解説されなければ、ですが。)

ソースを読むとレイキャストアルゴリズムを使っているとあり、ゲーム本体と分離されたレイキャストライブラリが付いていた。
これは割と使うのが簡単そうなので、ライブラリのコメントを元に使い方を書いてみたい(自分で作ってみるのはまた今度)。

▼レイキャストアルゴリズムについて

レイキャストアルゴリズムとは、3次元視点を実現するアルゴリズムの一つで、簡単に言うと、二次元のマップ上に自分がいてどちらかを向いているとすると、その視点の方向にマップを検索していって、壁などにぶつかると、それが自分が視線上に見えるものである、という考え方で画面を描写するものだ。

プチコンでのレイキャストアルゴリズムに基づいた3次元のゲームは、実は初代プチコンの時代から存在したようだが、現在確認できるのはmkII用のもので、プチコンまとめwikiに掲載されている。
RAYCAST MAZE
もしプチコンmkIIを持っているようであればぜひQRコードを読み込んで遊んでみよう。当時からかなり高速な3次元視点ゲームが存在していたことに驚くと思う。

この作者(Hepebellさん)は丁寧にもレイキャストアルゴリズムの考え方を図で書いてくれている。非常に参考になる。
アルゴリズム解説

calc84maniacさんの実装も、アルゴリズムとしてはこの考え方に基づいているようだ。


▼calc84maniacさんのRaycastingライブラリ

まずはcalc84maniacさん作の「Spooky Maze」をダウンロードしてみよう。
Spooky Maze (Raycaster)

そして遊んでみよう。どんなものかわかったろうか。

これと同じような画面を付属のライブラリを使うと簡単に作れるようなのだ。
Raycastingライブラリは、ダウンロードされたプロジェクトの中で「RAYCAST.LIB」という名前で保存されている。
(現状はバージョン0.1.00 Alphaと書かれている。)

ライブラリ冒頭部分のコメントの意訳を以下に載せる。
(筆者には大した英語力はないので、その正確性は期待しないで自分で読んだ方がよい。)
なお、以下で「環境」という言葉で表されているのは壁などの静的なブロックで構成されたマップを表し、「オブジェクト」は猫やスケルトンなどのマップ上に位置するその他の物を表す。

概要:
 このライブラリは、2次元のマップ配列、テクスチャー、光源の色、カメラ位置/アングルといった情報に基づいて、ブロックで区切られた3次元環境を描画するものだ。
 また、この3次元世界にスプライトベースのオブジェクトを表示するユーティリティも含んでいる。

 その環境自体もスプライトによって構成されており、それらの描画処理は単純なCALL SPRITE命令を必要とする。
 描画速度と品質は、環境に配置されたスプライトの数に直接的に影響を受ける。
ライブラリの読み込み方:
 このライブラリはいずれかの好きなスロットにロードすることができる。
 例えば、スロット1に置いて使いたい場合は次のようにする:
  LOAD "PRG1:RAYCAST.LIB",FALSE
    USE 1
環境を描画するには:
 ライブラリの準備のために以下の4つのステップを必要とする:

 1. INITRAYLIB命令
   この命令は、スプライト管理番号の開始番号と終了番号を引数としてレンダラーに設定する。
   また表示するウィンドウの座標も引数とし、全てのスプライトが表示される範囲を限定する。
   (訳注:SPCLIPの座標を指定するということ。)

 2. SETTEX命令
   この命令は、ブロックのテクスチャーがあるスプライトページ上のU,V,W,H座標を格納した2次元配列が引数となる。
   配列は [テクスチャ数,4] で宣言する。
   この配列においてブロック#1のテクスチャーは0番からとなる。なぜなら、マップ上での 0(ゼロ) は何もない場所を表しているからだ。
   この配列の中身は、再度SETTEX命令を呼び出すことなく、修正してもよい。

 3. SETMAP命令
   この命令は、マップデータを格納した2次元配列と、配列の幅と高さを引数とする。
   マップデータは行が先に来る形式であることに注意する。すなわち、1次元目が行(y座標)を表し、2次元目が列(x座標)を表す。
   0以外の値はテクスチャー番号に対応するブロックを表す。0は何もない空間を表す。
   マップは、その外側が見えないように、ブロックで囲われていなければならない。
   この配列の中身は、再度SETMAP命令を呼び出すことなく、修正してもよい。

 4. SETCAM命令
   この命令は、カメラの(X,Y,Z)位置を引数とし、(X,Y)はマップ内の位置、Zは0〜1の範囲の値をとる。
   さらに、視点の角度(ラジアン指定)、視点の上下移動量(ピクセル指定)、視野角(ラジアン指定)も引数とする。
   カメラ位置を変更したフレームには、CALL SPRITEの前に、必ずこの命令を呼ぶ必要がある。

 最後に、これらのステップが実行された後に、描画のために CALL SPRITE を実行する。

(訳注:各命令を再度呼び出すことなく配列の値を更新してもよい、と書かれているのは、各命令では配列の値をコピーしているのではなく、配列の参照をコピーしているから。)
スプライトベースオブジェクトを描画するには:
 ライブラリは、カメラ位置に基づいた3次元空間上に、スプライトを位置設定・縮尺設定・ライティングするコールバック命令を提供する。

 スプライトの位置と縮尺を設定するには、SPOFSとSPSCALEを使う代わりに、SPVARでスプライト変数に設定する。
 変数0,1,2はX,Y,Z位置をそれぞれ表し、変数3はスプライトの高さ(1.0がマップ上のブロックのいっぱいの高さである)を表す。
 変数4〜7はあなたが自由に使える。

 スプライトのホーム位置は設定済のものが使われることに注意すること。
 2048番以降のスプライト定義は、センタリングされたホーム位置になっていることが多いので、それをそのまま使うか、あるいは自分でSPHOMEを使って設定すること。

 もしライティング処理を書き換えたい場合には、色の設定はSPANIMを使うとよい。

 管理番号がSP%であるスプライトにこの機能を適用するには、単に SPFUNC SP%,"SPWORLD" を実行すればよく、すると、CALL SPRITEが実行された時に自動的にスプライトの位置が再設定される。
 もしあなたがすでに自作のスプライト関数(訳注:スプライト関数=SPFUNCでスプライトに割り当てた関数)を使っている場合には、その関数の中で直接SPWORLDを呼び出すようにしてもよい。
上級者向け機能/命令について:
 * SETLIGHT命令
  この命令はRGB値を引数として、環境およびオブジェクトへカメラからの距離に応じた配色を行う。
  SPCOLORの制限によって、#WHITEを引数として送ったとしても、元の色よりも明るくすることはできない。

 * BEEP3DおよびSPBEEP3D命令
  カメラとの相対位置に応じた効果音を発生する。
  BEEP3D命令はX,Y,Z座標を引数とし、SPBEEP3D命令はスプライト座標を使用する。
  その他に、BEEP命令における効果音番号、周波数、ボリュームを引数とする。
  実際のボリュームとパンポットは、カメラから座標までの相対位置によって補正される。

 * MAPCHK関数
  この関数を使うと、out-of-boundsエラーを心配することなく、正方形領域とマップの衝突を簡単に検出できる。
  関数名、3D位置、正方形領域の半径(訳注:正方形の一辺の長さの半分のことだと思われる)を渡すと、マップ配列、整数化されたX/Y、およびZを引数として、必要な回数だけ関数を呼び出してくれる。
  このライブラリのサンプルに含む関数COLLIDEを使ってブロックとの衝突数をカウントするには、以下のように書く。
   COUNT=MAPCHK("COLLIDE",X,Y,Z,R)
  (訳注:この説明を読むより、ライブラリのMAPCHKの箇所を読む方が何をやりたいかわかりやすいと思う)

 * SETVIEWCALLBACK関数
  この命令は、マップ上のブロックが画面上に表示される度に呼び出したい命令名を引数として取る。
  この命令はマップ配列とX,Y座標もまた引数として取る。
  この命令を使うと、ミニマップを自動生成するのに便利である。
  ただし、この命令はフレーム毎に何度も呼び出されるので、呼び出される命令においてブロックを処理するのに時間がかかるようであれば、すでに見たブロックは無視するようすることをおすすめする。

 * RAYCAST命令
  この命令はこのライブラリの中核となるものである。
  X,Yおよび向きで与えられる位置から光線を発して、マップ上の非0であるブロックに当たるまで伸ばしていく。そして、衝突した点とブロック面の位置(左から右に向かって0〜1の値をとる)を返す。
  このスライスの戻り値は、テクスチャーマッピング以外の用途ではあまり有効ではないので、通常の使い方においては無視して構わない。

 * CANSEE関数
  RAYCASTを使った有用な例。
  この関数は、与えられた位置が、他のもう一つの位置から見えるかを、レイキャストに基づいて判定する。

実際に使ってみるのはまた次回以降です!

文字列関係操作ライブラリ

だいぶ前に文字列関係操作ライブラリを作ってたんだけど、ツイートだけではてなの方には記事として上げてなかったので上げておきます。

えいだ on Twitter: "よく使いそうなものを実装した文字列操作ライブラリSTRLIBです。
とりあえず使い方はコメントをご覧ください。
ファイル名:STRLIB 公開キー: 4V4E34J
#petitcom #sbkey"

ファイル名: STRLIB

公開キー: 4V4E34J

公開日: 2015/01/12

バージョン: 1.00

v1.00 初公開

【ライブラリの使い方】

(1)スロット0〜3のいずれかにこのライブラリをロードする。
(2)呼び出し側プログラムの先頭に「USE 置いたスロット」を書く。
(3)関数毎の使用方法で呼び出す。

【簡易リファレンス】
READLINE テキストを1行読み込む
TRIM$ 文字列の先頭・末尾の空白を取り除く
LTRIM$ 文字列の先頭の空白を取り除く
RTRIM$ 文字列の末尾の空白を取り除く
UPPERCASE$ アルファベットの小文字を大文字にする
LOWERCASE$ アルファベットの大文字を小文字にする
SPLIT$ 空白で区切り文字として、分割して文字列配列にする
SPLIT2$ 指定の文字を区切り文字として、分割して文字列配列にする
REPLACE$ 指定の文字列を検索し、別の文字列に置き換える
CONSISTS 文字列が、指定の文字列からだけなるかを調べる
CHECKPROJECTNAME プロジェクト名が有効な名前か調べる
CHECKFILENAME ファイル名が有効な名前か調べる
VALIDFILENAME$ プロジェクト名/ファイル名 が有効な名前か調べる
FILENAMEPART$ プロジェクト名/ファイル名 のファイル名部分を取り出す

Petit-Z80ちょっとだけ進捗

Petit-Z80がちょっとだけ進捗。

■ツール類を作ってみた

デバッグ作業を楽にするため、いくつかツール類を作りはじめた。

メモリの中を確認するZ80_Mem_Viewを作った。

まだ基本的な機能しかないが、これでメモリの確認が少しだけ楽に。

また、テキストファイルから16進データをメモリにロード/セーブする、
Z80_Mem_Load、Z80_Mem_Saveを作った。
これはまだテストもしていない。


仮想マシンの対象を検討した

こちらが本題。
最初の仮想マシンはテスト用に適当に作ったもので、
テキストRAMに書きこまれたデータを、一定期間ごとに
全画面に書きだすだけのものだった。

現実のパソコンを実時間でエミュレートするつもりはないのだけど、
実際のプログラムを使わないとテストがしづらいのと、
それなりのものが動かないと達成感がないので、
現実のパソコンの仕様をすごく簡略化した仮想マシンを作って、
既存の実際のプログラムを動かしてみたいと考えた。

この「現実のパソコン」にはいくつか条件がある。
(1)既存のプログラムが入手可能である
 これが目的なので、入手不可能な場合は意味がない。
 プログラムとしては、リアルタイム性がなく、それなりの画面を備えたゲーム、というのがよさそうなところだろう。

(2)非力すぎない
 作る意味がわからなくなるレベルものはやめておこう。

(3)プチコン3号で実現できる範囲
 これは(2)とは逆になるが、解像度が高くて物理的仕様が1対1対応できないとか、
 自分の実力で実現が難しいものとかはやめる。

この3つの条件で、かつ知名度や自分の勝手なイメージから、
「現実のパソコン」を3つに絞った。
PC-6001
MZ-700
MSX


さて、上記の条件に加えて、さらに重要な前提条件がもう一つある。

(4)「現実のパソコン」の仕様を自分が理解可能なレベルで解説された資料を入手できる
 上記の機種は、いずれも所有したことがなく、プログラミングの経験もない。
 はじめてプログラミングするのにやさしい(資料が揃っている)機種はどれなのか?

この(4)の条件を満たすのがどれかを探すのが、今回のお題である。

PC-6001

調べた資料

自分がブックマークしたURLは以下。
http://homepage2.nifty.com/akikawa/basic.html
http://p6ers.net/hashi/index.html
http://www.tiny-yarou.com/asmdev/asmdev.html
http://homepage2.nifty.com/akikawa/sword/index.html
http://papicom.net/p6v/index.html
http://sbeach.seesaa.net/
http://www.geocities.jp/submarine600/html/p6/tate.html
しかし、私のレベルでは基礎知識が不足しているのか、PC-6001の仕様を理解できた気がしない。
スクリーンモードそのものの仕様もわからなかった。

結果

残念ながら、自分の理解レベルでPC-6001のサブセットとなる仮想マシンを作るのは無理そう。

MZ-700

調べた資料

MZ-700マシン語プログラミング入門
http://www.maroon.dti.ne.jp/youkan/mz700/pg/index.html
I/Oマップ
http://www.maroon.dti.ne.jp/youkan/mz700/mzioframe.html
に極めて簡潔に記載されており、必要にして十分な情報量。

結果

マシンのもともとの仕様がシンプルなことも幸いしてか、非常に分かりやすい資料をすぐに見つけることができた。
また、マシンの仕様そのものも、今回の実装には都合がよさそうである。

MSX

調べた資料

テクハンwiki化計画
http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan
MSX2だが、紙のテクニカルハンドブックをそのままwiki化しようとしたものらしく、極めて貴重な資料。
図が非常に多いので、理解がしやすい。
画面の仕様は4部のあたり。

結果

MSXにはスクリーンモードがたくさんあって把握が大変だが、詳細に書かれたテクニカルハンドブックをそのまま参照することができたので、問題なく理解できた。
仕様はわかったが、MSX自体の仕様はスロット等があったりしてイメージとちがって結構ややこしい。

▼結論

MZ-700とする。
すぐにわかりやすい資料を見つけることができたのと、想像以上にマシンの仕様がシンプルなので、今回の目的には最も合う。
サンプルプログラムの入手性はMSXに劣るが、大きな問題はなさそう。
ビットマップが使えないのは少し残念だが。

次点はMSX
なんといってもテクニカルハンドブックをそのまんま(?)wiki化してるので、文化の継承の観点からいってもこれ以上はないだろう。(権利関係は心配だが)
MSXならば50年後でもプログラミング入門者を作り出せそう。
プログラムも、紙媒体・Webともに、軽いものから大作まで入手可能で他の2機種を大きく引き離している。
ただし、自分がイメージしたよりも仕様が複雑であり、今回はパスする。

最後はPC-6001
残念ながらスクリーンモードすら把握することができなかった。
がんばって互換モニタのソース(アセンブラ)を読むのにトライするも、Z80初心者かつスクリーンモードの内容すらわかっていない自分には難しかった。
PC-6001はウィザード達の領域になってしまっていて、それがゆえに基礎的な資料が少ないようだ。
これからPC-6001プログラミングに入門するには結構ハードルが高い。

ビットマップを使いたかったので、本当はMSXPC-6001がよかったのだが、自分の能力から考えると結果的にMZ-700のシンプルさが良さそうだ。

3号プログラミング覚え書き(5) 画面編

・解像度:上画面は400x240ドット、下画面は320x240ドット

・コンソール:上画面50x30文字、下画面40x30文字

・画面モードの切り替えはXSCREENで行う
XSCREEN 4を指定した時は、上下がつながって320x480(上240、下240)ドットになる(上画面の左右40ドットずつは使えない)。

・上画面・下画面から描画したい先を選ぶ時はDISPLAYを使う
上画面 DISPLAY 0
下画面 DISPLAY 1
を指定してから、描画命令(グラフィック、スプライト、BG、コンソール)を実行する。
(XSCREEN 4の時はDISPLAY 0のみ)

GRPリソースのサイズは512x512ドット
GRPリソースは、単なる512x512ドットに対応したメモリリソースで、画面上ではその一部分が見えているだけだと思えばよい(見えない部分も存在していて、グラフィック描画命令で実際に描くことができる)。

GRPは0〜3がグラフィック、4がスプライト用、5がBG用
GRP4はスプライト用、GRP5はBG用の画像元データの置き場として使われる。
GRP0〜3はグラフィック用で、デフォルトの状態では上画面にGRP0、下画面にGRP1が割り当てられている。

・表示・操作したいGRPを変更するにはGPAGEを使う

GPAGE 表示ページ,操作ページ

のように指定する。

・表示ページは表示画面とGRPリソースの関連付けを表す
表示ページは、GRPリソース(単なるメモリリソース)と画面の関連付けを行う。
つまり、下画面にGRP3を表示したい時は以下のようにする。

DISPLAY 1
GPAGE 3,3

・操作ページはグラフィック描画命令の適用対象を表す
操作ページは、グラフィック描画命令(LINE、PAINTなど)が適用される対象のGRPリソースを指定する。

このようにプチコン3号では、表示ページと操作ページは全く異なる概念となっている。

加速度センサーを使ったサンプル

昨日調べた加速度センサーの動作にもとづいて、本体を傾けるとボールが移動するサンプルを作ってみた。

ファイル名: ACCELBALL

公開キー: BK7NV3AJ

公開日: 2015/09/22

バージョン: 1.00

動画
https://vine.co/v/ePnXD9gEF2X


なお、本体の傾きはGYROAでよさそうな気がするが、実際に使ってみると、しばらくすると本来の傾きとずれてくる。
(「3号プログラミング覚え書き(4) センサー編」に書いたとおり)

プチコン3号公式でも本体の傾きをとるにはACCELを使うとよい、と書かれているのでACCELを使う。
http://smileboom.com/special/ptcm3/reference/#input

3号プログラミング覚え書き(4) センサー編

ジャイロセンサーで本体の向きを取ろうとしても実際の向きとは合わないことがある

GYROA命令で本体の向きを取ることができるが、しばらくすると実際の向きと合わなくなる。
おそらくGYROVでとれる回転角速度の累積値を、GYROAの傾き角度にしていると思われるが、誤差の累積で実際の向きと合わなくなる。

本体の傾きをとるよりよい方法は、ACCEL命令を使うこと。
ACCEL OUT X,Y,Z
とする。
水平な場所に置いた状態では、X,Y,Zの値は、概ね0.00、0.00、-1.00である。
本体を右に90度傾けるとXの値はおよそ1.00、左に90度傾けるとXの値はおよそ-1.00となる。
同様に、本体を奥に90度傾けるとYの値はおよそ-1.20、手前に90度傾けるとYの値はおよそ0.80となる。(Yについては個体による違いがあるかもしれない)

角度については、45度くらいに傾けた時に0.50程度になるので、傾き角度に比例すると考えてよさそう。

Pebble Time 覚え書き5 加速度センサーをエミュレータで動かす

エミュレータで加速度センサーとかどうすんのかな?と思ってたら、ものすごく簡単にできました。
ものすごすぎて色々興奮w

1.エミュレータを起動
まずはエミュレータを起動します。
エミュレータの歯車ボタンをタップしてSETTINGSを開いたら、「SENSORS」をタップします。

2.サイトを開く
すると、「cpbl.io」というURLをスマホに入力して、下の数字を入力しろ、との指示が出ます。

cpbl.io」というURLをスマホに入力して、数字を入力します。


3.スマホを動かすとエミュレータに加速度情報が転送される
すると、スマホを動かすとエミュレータの加速度センサーが連動して動く!

あまりの簡単さにビビりましたw

Pebble Time 覚え書き4 実機で動かす

せっかくウォッチフェイスを自分で作ってみたので、実機で動かしてみる。

まずPebble Timeと連動しているスマホの設定。(画面はAndroid)
右上のメニューから「Settings」を選びます。
項目の中から「Developer Connection」をタップします。
次にConnectedを「ON」にします。

次にCloudPebbleです。
左メニューの「COMPILATION」で、通常は「EMULATOR」になっているところを「PHONE」に変更し、「INSTALL AND RUN」をクリックします。

これだけ! かんたん!
実機のウォッチフェイスが変わりました。

インストールしたウォッチフェイスは、普通に追加したウォッチフェイスと同じ扱いでそのまま動いているので、いらなくなったら通常の方法でウォッチフェイスを切り替えます。

Pebble Time 覚え書き3 ウォッチフェイスの改良(ちょっとだけ)

ひとつ前のエントリ(http://d.hatena.ne.jp/eidaht/20150920/1442732674)で、ウォッチフェイスを作ってみるチュートリアルを終えた。

こっから少しずつ勝手に改良してみる。

時分だけじゃなくて秒も表示してみる。
まずソースを眺めると、TickTimerServiceへの登録がMINUTE_UNITとなっているので、ここをSECOND_UNITにしたら1秒ごとに更新できそう。
以下のように直してみる。

  tick_timer_service_subscribe(SECOND_UNIT, tick_handler);

SECOND_UNITも定数として認識されたのでこれでよさそう。

次にupdate_time()の中の記述を直す。

  // Create a long-lived buffer
  static char buffer[] = "00:00:00";

  // Write the current hours and minutes into the buffer
  if(clock_is_24h_style() == true) {
    //Use 2h hour format
    strftime(buffer, sizeof("00:00:00"), "%H:%M:%S", tick_time);
  } else {
    //Use 12 hour format
    strftime(buffer, sizeof("00:00:00"), "%I:%M:%S", tick_time);
  }

%Sでいいのかわからないけどこれでいいんじゃね?

と実行してみると...、実行できたけどフォントがでかすぎて横がはみ出る。

フォントの設定はmain_window_load()の中に記述がある。

  text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));

この42がフォントのポイントっぽいので、30とかに変更したらよさそう。
でビルドすると、ビルドエラーになる。

../src/main.c:39:59: error: 'FONT_KEY_BITHAM_30_BOLD' undeclared (first use in this function)

宣言されてないって。
もとの定数はpebble.hで定義されてるんだろうけど、CloudPebbleからだとpebble.hの中身見えないんだけどw

チュートリアルの次の章はカスタムフォントを使うことになってるんだけど、カスタムフォント使うとかちょっとめんどい。

いくつか数値を試してみるも通らずw

観念して「pebble system fonts」でググるとありました。
USING PEBBLE SYSTEM FONTS

ていうことで、Bitham 30 Blackにしてみます。

  text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK));

ビルドできました。

実行すると、プロポーショナルフォントなので秒が変わるたびにガタガタしちゃうのがあんまりカッコよくない。
この辺にこだわるとカスタムフォントしかないのかな...。

カスタムフォントはまた次回。

Pebble Time 覚え書き2 最初のウォッチフェイス

C SDKでウォッチフェイスを作ってみる。
http://developer.getpebble.com/tutorials/watchface-tutorial/part1


1.CloudPebbleからログイン
https://cloudpebble.net/ide/
からCloudPebbleにログインする。

2.Projectの作成
PROJECTSから「CREATE」。
「CREATE NEW PROJECT」と出たら、
PROJECT NAME : プロジェクト名を入力する
PROJECT TYPE : Pebble C SDK
SDK VERSION : SDK 3
TEMPLATE : Empty project
を入力して「CREATE」。
要は、プロジェクト名だけ入力して「CREATE」。

3.Projectの設定
左にあるメニューの「SETTINGS」をクリック。
「APP KIND」という項目を探して、「Watchface」に変更する。

4.ソースの追加
左メニューの「SOURCE FILES」の横にある「ADD NEW」をクリック。
「CREATE NEW FILE」と出たら、
FILE TYPE : C file
FILENAME : ファイル名を入力する。ここでは「main.c」と入力
TARGET : App / Watchface
CREATE HEADER : チェックしない
を入力して「CREATE」。
要は、ファイル名だけ入力して「CREATE」。

5.ソースを入力 (空のウィンドウを表示)
main.cには以下の1行だけが最初から入力されている。

#include 

続いて以下をソースに入力する。

static Window *s_main_window;

static void main_window_load(Window *window) {

}

static void main_window_unload(Window *window) {

}

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);
}

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

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

Pebble SDKにおける標準的な記述方法はこのようにする。
init()でメモリの確保、deinit()でメモリの開放を行う処理を記述する。
また、main()の中にイベントループとしてapp_event_loop()の呼び出しを記述する。

画面を表示するにはWindowを作成しなければならないので、init()の中の2行目の処理でWindowを作成している。
その後で、Windowのloadとunload時のイベントで呼ばれる関数を関数ポインタとしてWindowHandlersに渡している。
window_stack_pushでWindowを表示している。

これで、何も表示しない、というか一面白色になるウィンドウを表示するウォッチフェイスが作成できた。

6.実行 (空のウィンドウを表示)
画面右上のプレイボタンみたいなボタン(ポイントすると「Save, build, install and run」と表示されるボタン。以後、実行ボタンと呼ぶ)をクリックすると、ビルドが始まり、自動的にエミュレータが起動してウォッチフェイスがインストールされ、ウォッチフェイスが起動するところまで行われる。
画面左上にエミュレータが表示され、白い画面が表示されたら成功。


7.ソースの修正 (何かテキストを表示させる)
先ほどのmain.cを再び開く。
文字を表示するにはTextLayerを使って描画する。
先ほど空だった、main_windows_loadの中に、TextLayerを作成して、テキストを描画する処理を追加する。
また、main_windows_unloadの中にTextLayerの開放処理を追加する。

static void main_window_load(Window *window) {
  // Create time TextLayer
  s_time_layer = text_layer_create(GRect(0, 55, 144, 50));
  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");

  // Improve the layout to be more like a watchface
  text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
  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_get_root_layer(window), text_layer_get_layer(s_time_layer));
}

static void main_window_unload(Window *window) {
    // Destroy TextLayer
    text_layer_destroy(s_time_layer);
}

再び実行ボタンで実行。
画面左上のエミュレータに、「00:00」と表示されたら成功。


8.ソースの修正 (時計を表示させる)
再度main.cを開く。
時計を表示するには、イベントループの中で、一定時間ごとに処理が呼び出されるようにする必要がある。
このためには、TickTimerServiceに対して一定時間ごとに呼び出される処理を登録する。
TickTimerServiceから直接呼び出される処理は以下のようになる。

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

また、init()の最後に、TickTimerServiceに1分ごとにtick_handlerを呼び出すように登録する処理を付け加える。

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

そして、先ほどのtick_handlerから呼び出される、update_time(時計を描画する処理)
を追加する。

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

  // Create a long-lived buffer
  static char buffer[] = "00:00";

  // Write the current hours and minutes into the buffer
  if(clock_is_24h_style() == true) {
    //Use 2h hour format
    strftime(buffer, sizeof("00:00"), "%H:%M", tick_time);
  } else {
    //Use 12 hour format
    strftime(buffer, sizeof("00:00"), "%I:%M", tick_time);
  }

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

また念のため、tick_handlerが最初に呼び出されるのがすぐとは限らないので、00:00を眺め続けることにならないように、main_window_unloadの最後にupdate_timeの呼び出しを追加しておく。

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

再び実行ボタンで実行。
画面左上のエミュレータに、現在の時刻が表示され、一分後に時計の表示が変われば成功。

最初の時計はこれで完成!