Skip to content

Latest commit

 

History

History
67 lines (51 loc) · 4.64 KB

README.md

File metadata and controls

67 lines (51 loc) · 4.64 KB

『逆転じゃんけん』

1. 概要・コンセプト

2. アプリの画面2つ

3. アプリの動作(Gif)

ユーザーの手を識別してじゃんけんをします。

4. ダウンロードリンク

App_Store_Badge_JP

5. 今後の改善予定

今後は手の認識の速度を向上させます。 現在、グーとパーの識別率はほぼ100%ですが、チョキの識別率が約90%なので、チョキの識別率を100%に近づけます。

6. MVVMの構成図

View

ファイル名 概要・解説
StageView キャラクターと難易度をScrolleViewで表示するViewです。
HandGestureView 手を検知してじゃんけんをするViewです。
ResultView じゃんけんの結果を表示するViewです。
BackgroundView StageViewの背景のViewです。GeometryReaderを用いてスクロールに応じて背景が動きます。

ViewModel

ファイル名 概要・解説
StageViewModel StageModelのインスタンス、ボタンを押した時の効果音メソッドを記述したクラス
HandGestureViewModel 勝率からゲーム結果を算出するメソッドや動画フレームから手のジェスチャーを検出するメソッドなどを記述したクラス

Model

ファイル名 概要・解説
StageModel キャラクターのレベルや初期勝率、逆転の有無などの情報を格納した構造体
SoundPlayer 効果音を再生するメソッドなどを記述したクラス
JankenTextModel じゃんけんの掛け声とタイミングのメソッドを記述したクラス
HandGestureModel ゲーム終了を判定するメソッド、HPを計算するメソッドなどを記述したクラス
HandGestureDetector 動画フレームからVisionを用いてジェスチャーを判別するメソッドなどを記述したクラス

7. 工夫したコード/設計

ポイント1 じゃんけんの手を判別

開発初期段階では、じゃんけんの手を3000枚撮影して、CreateMLを用いてモデルを作成して、CoreMLを用いて手を判別しました。しかし3度モデルを作り直しても判別の制度が良くならなかったのでVisionのみで手の判別をすることにしました。
Visionでは、手の骨格の座標情報が取得できます。そこで、以下のような手順で手の判別を行うことで精度を向上させました。

・手順1:Visionを用いて指先、第二関節、手首の座標を取得

// 指先
let indexTip = points[VNHumanHandPoseObservation.JointName.indexTip.rawValue]?.location ?? .zero
let middleTip = points[VNHumanHandPoseObservation.JointName.middleTip.rawValue]?.location ?? .zero
let ringTip = points[VNHumanHandPoseObservation.JointName.ringTip.rawValue]?.location ?? .zero
let littleTip = points[VNHumanHandPoseObservation.JointName.littleTip.rawValue]?.location ?? .zero
// 近位指節間(PIP)関節 = 第二関節のこと
let indexPIP = points[VNHumanHandPoseObservation.JointName.indexPIP.rawValue]?.location ?? .zero
let middlePIP = points[VNHumanHandPoseObservation.JointName.middlePIP.rawValue]?.location ?? .zero
let ringPIP = points[VNHumanHandPoseObservation.JointName.ringPIP.rawValue]?.location ?? .zero
let littlePIP = points[VNHumanHandPoseObservation.JointName.littlePIP.rawValue]?.location ?? .zero
// 手首
let wrist = points[VNHumanHandPoseObservation.JointName.wrist.rawValue]?.location ?? .zero

・手順2:三平方の定理より2点の距離を求めるメソッド作成

// 画面上の2点間の距離を三平方の定理より求める
private func distance(from: CGPoint, to: CGPoint) -> CGFloat {
return sqrt(pow(from.x - to.x, 2) + pow(from.y - to.y, 2))
}

・手順3:手首から親指以外の指までの距離を求める

// 手首から指先の長さ
let wristToIndexTip = distance(from: wrist, to: indexTip)
let wristToMiddleTip = distance(from: wrist, to: middleTip)
let wristToRingTip = distance(from: wrist, to: ringTip)
let wristToLittleTip = distance(from: wrist, to: littleTip)
// 手首から近位指節間(PIP)関節の長さ
let wristToIndexPIP = distance(from: wrist, to: indexPIP)
let wristToMiddlePIP = distance(from: wrist, to: middlePIP)
let wristToRingPIP = distance(from: wrist, to: ringPIP)
let wristToLittlePIP = distance(from: wrist, to: littlePIP)

・手順4:どの指が曲がっているかによってグーチョキパーを判別

// HandPoseの判定(どの指が曲がっているかでグーチョキパーを判定する)
if
wristToIndexTip > wristToIndexPIP &&
wristToMiddleTip > wristToMiddlePIP &&
wristToRingTip > wristToRingPIP &&
wristToLittleTip > wristToLittlePIP {
// 4本の指が曲がっていないのでぱー
currentGesture = .paper
} else if
wristToIndexTip < wristToIndexPIP &&
wristToMiddleTip < wristToMiddlePIP &&
wristToRingTip < wristToRingPIP &&
wristToLittleTip < wristToLittlePIP {
// 4本の指が曲がっているのでぐー
currentGesture = .rock
} else if
wristToIndexTip > wristToIndexPIP &&
wristToMiddleTip > wristToMiddlePIP {
// IndexとMiddleが曲がっていないのでちょき
currentGesture = .scissors
} else {
currentGesture = .unknown
}

8 その他リンク

アプリのホームページ
制作者Twitter