【ITニュース解説】Using the gyroscope to detect wrist gestures with ArkTS
2025年09月22日に「Dev.to」が公開したITニュース「Using the gyroscope to detect wrist gestures with ArkTS」について初心者にもわかりやすく解説しています。
ITニュース概要
ArkTSとジャイロスコープセンサーを使い、手首のジェスチャーを検出する方法を解説する記事だ。センサーからのデータ取得、手首を2回ひねる動作で画面のON/OFFを切り替えるアプリ開発手順をコード例とともに紹介。初心者でもセンサーを活用したインタラクティブなアプリ作成を学べる。
ITニュース解説
この記事では、HarmonyOSのアプリケーション開発において、スマートフォンのジャイロスコープセンサーを利用して手首のジェスチャーを検出し、それに応じてアプリの表示状態を切り替える具体的な方法について解説する。システムエンジニアを目指す初心者でも理解しやすいように、基本的な概念から順に追っていく。
まず、今回のプロジェクトは実際のデバイス、つまりスマートフォンやスマートウォッチといった実機で動作確認する必要がある点に注意が必要だ。エミュレータではセンサーの動きを正確にシミュレートできないため、開発中に実機が手元にあるとよりスムーズに進められる。
アプリケーションの見た目、つまりユーザーインターフェース(UI)から見ていこう。今回は非常にシンプルなUIを作成する。具体的には、画面の中央に一つのボタンを配置し、このボタンが「開いている状態」と「閉じている状態」という二つの状態を持つようにする。UIのコードはArkTSという言語で記述される。ArkTSはHarmonyOSアプリ開発で主に使われるTypeScriptベースの言語で、宣言的なUI開発を特徴としている。
@Entryと@Componentは、このコードブロックがアプリケーションのエントリポイント(最初に実行される部分)であり、かつ一つのUIコンポーネントであることを示している。@State isOpen: boolean = false;という行は、isOpenという名前の真偽値(trueかfalse)の状態変数を用意し、初期値としてfalse(閉じている状態)を設定している。このisOpen変数の値が変わると、UIも自動的に再描画され、ボタンの見た目が変化する仕組みだ。
UIのコードでは、RelativeContainerの中にButtonを配置している。ボタンのアイコンはisOpenの状態によって太陽(開いている)か月(閉じている)に切り替わり、色や背景色も変化する。onClickイベントハンドラでは、ボタンがタップされるたびにthis.isOpen = !this.isOpenというコードが実行され、isOpenの状態がtrueとfalseの間で切り替わる。これは、通常の方法でボタンをタップした場合の動作だ。
ここからが本題のセンサー利用についてだ。ジャイロスコープは、デバイスの回転や角速度を検出するためのセンサーで、スマートフォンがどの方向にどれくらいの速さで回転しているかを把握できる。このセンサーをアプリケーションで利用するには、まずアプリがそのセンサーデータにアクセスする許可を得る必要がある。これはプライバシー保護とセキュリティの観点から非常に重要だ。アプリの構成ファイルであるmodule.json5に、ohos.permission.GYROSCOPEという許可(パーミッション)を追加することで、ジャイロスコープのデータを利用できるようになる。
次に、ArkTSのコード内でセンサー機能を使うための準備をする。import { sensor } from '@kit.SensorServiceKit';という行は、HarmonyOSが提供するSensorServiceKitというライブラリの中からsensorというオブジェクトをインポートしている。このsensorオブジェクトが、ジャイロスコープをはじめとする様々なセンサーを操作するための窓口となる。
センサーは必要な時にだけ起動し、不要になったら停止するのが良いプログラミング習慣だ。これはバッテリー消費を抑え、システムの負荷を軽減するためだ。
aboutToAppear()という関数は、コンポーネントが画面に表示される直前に自動的に呼び出されるライフサイクルメソッドだ。この中でsensor.on(sensor.SensorId.GYROSCOPE, async (res) => { ... });というコードを実行している。これは「ジャイロスコープ(SensorId.GYROSCOPE)からのデータ変化を監視し、データが更新されるたびに指定された処理(コールバック関数)を実行する」という意味だ。コールバック関数はresという引数を受け取る。このresにはジャイロスコープが検出したX、Y、Z軸それぞれの角速度データが含まれており、console.log(JSON.stringify(res))でログに出力することで、どのようなデータが取得できているかを確認できる。
一方、aboutToDisappear()という関数は、コンポーネントが画面から非表示になる直前に呼び出される。この中でsensor.off(sensor.SensorId.GYROSCOPE);を実行することで、ジャイロスコープの監視を停止し、リソースを解放する。
いよいよ、このジャイロスコープのデータを使って手首のジェスチャーを検出する部分の実装だ。目標は「ユーザーが手首を2回連続でねじったときに、アプリの開閉状態を切り替える」ことだ。
このジェスチャーを検出するために、私たちはデバイスのX軸方向の回転(ねじり)に注目する。手首をねじる動作は、ある方向への回転と、その逆方向への回転が連続して発生することで特徴づけられる。
そこで、この正負の動きを追跡するためのいくつかの変数を定義する。
private pos_x_history: number[] = new Array(4).fill(0)は、X軸方向の正の回転量(特定の方向へのねじり)を記録するための数値の配列だ。初期値として0が4つ格納されている。
private neg_x_history: number[] = new Array(4).fill(0)は、同様にX軸方向の負の回転量(逆方向へのねじり)を記録するための配列だ。
private twistHistory: boolean[] = new Array(5).fill(false)は、「ねじり動作」が検出されたかどうかを真偽値(true/false)で記録するための配列だ。これは直近のねじり発生履歴を保持する。
getPosX()とgetNegX()は、それぞれの履歴配列内の数値を合計するためのヘルパー関数だ。
次に、これらの履歴配列を管理するための関数を作成する。センサーデータは常に新しいものが生成されるため、古いデータは捨てて新しいデータを追加していく必要がある。これは「スライディングウィンドウ」や「キュー」と呼ばれるデータ構造の考え方だ。
_pushToPosX(val: number)関数は、pos_x_history配列の先頭の要素を削除し(shift())、新しい値valを配列の末尾に追加する(push())。_pushToNegXも同様の処理を行う。
_pushToHistory(val: boolean)関数も同様に、twistHistory配列の古い要素を削除し、新しい真偽値を追加する。これにより、常に直近の数回分のデータだけが履歴として保持されることになる。
そして、ジャイロスコープのセンサーリスナー(sensor.onのコールバック関数)を以下のように修正する。
センサーから新しいデータresが届くと、まずres.x(X軸方向の角速度)が0以上かどうかを判定する。
もしres.xが0以上であれば、それは正の方向への回転なので、_pushToPosX(res.x)でその値を正の履歴に追加し、同時に_pushToNegX(0)で負の履歴をリセットする。
逆にres.xが0未満であれば、負の方向への回転なので、_pushToNegX(res.x)で負の履歴に追加し、_pushToPosX(0)で正の履歴をリセットする。このようにすることで、常に一方向の動きだけを追跡できる。
次に、「ねじり」が発生したかどうかを判定するロジックだ。
if (this.getPosX() >= 13 && this.getNegX() <= -13)という条件文がある。これは、「直近の正の回転量の合計が13以上」であり、かつ「直近の負の回転量の合計が-13以下」であるかをチェックしている。この13や-13は、ねじり動作とみなすための閾値(しきいち)だ。この条件が同時に満たされるということは、短期間のうちに正の方向と負の方向の両方へ、それぞれ一定以上の速さでねじれたことを意味し、これを一つの「ねじり」動作と判定している。
もしこの条件がtrueになった場合、つまり一つのねじり動作が検出されたら、pos_x_historyとneg_x_historyをすべて0でリセットする。これは、検出されたねじりの動きが次のねじり検出に影響を与えないようにするためだ。そして、_pushToHistory(true)でtwistHistory配列にtrueを追加し、ねじりが発生したことを記録する。そうでなければ_pushToHistory(false)でfalseを追加する。
最後に、連続するねじり動作を検出してアプリの状態を切り替えるロジックだ。
if (this.twistHistory.filter((val) => val).length >= 2)という条件文は、twistHistory配列の中にtrue(ねじり動作)が2回以上記録されているかどうかを確認している。filter((val) => val)は配列の中からtrueの値だけを抽出し、.lengthでその数を数えている。
もしねじりが2回以上検出された場合、それはユーザーが手首を2回連続でねじったと判断できる。このとき、まずtwistHistory配列をすべてfalseでリセットする。これは、次のジェスチャー検出のために履歴をクリアするためだ。そして、this.isOpen = !this.isOpenを実行することで、アプリの開閉状態を反転させる。これにより、UIのボタンの見た目も切り替わるというわけだ。
以上の手順を経て、ジャイロスコープセンサーからの生データを利用して、手首の複雑なジェスチャーを認識し、アプリケーションの機能と連動させることが可能になる。この解説はジャイロスコープセンサーの基本的な使い方と、それを用いたジェスチャー検出の一例を示したものだ。より高度なセンサー利用や他のセンサーについては、HarmonyOSの公式ドキュメントでさらに詳しく学ぶことができる。システムエンジニアを目指す上では、このようなセンサーとUIの連携は非常に重要な技術要素となるだろう。