質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

1回答

1439閲覧

画像をドラッグした方向へ加速度がつくようにしたいです

magiee

総合スコア28

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

1クリップ

投稿2020/04/12 07:03

現状のswiftの動き

①タップすると画像が現れる。
②傾けると加速度センサによって画像が動く。
③ドラッグすると、加速度センサが無効になりドラッグする方向へ画像がついてくる。
④ドラッグを解除すると、加速度が有効になる。      ←今回修正したい部分
④再度タップすると画像が消える。

実現したいこと

ドラッグを解除した時に、ドラッグした方向へ加速度を付けたいです。

例)
iPhoneを下に傾けた状態(画像が下に移動する状態)で、
上の方向へドラッグし、指を離した時、上の方向へ加速度がつき、
その後、下に画像が移動するようにしたいです。

おそらく、
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
startGame()
}
の、タップを解除した時に、ドラッグした方向へ加速度がつく。と言うような
コードを入力すれば良いと思うのですが、考え方はあってますでしょうか?

該当のソースコード

import UIKit import CoreMotion class ViewController: UIViewController { var coin = UIImageView() var coinCount = 0 @IBAction func addImage(_ sender: UITapGestureRecognizer) { if coinCount == 0 { coin = UIImageView(image: UIImage(named: "daiya")) coin.frame = CGRect(x: 0, y: 0, width: 110, height: 110) coin.center = sender.location(in: self.view) coin.contentMode = .scaleToFill coin.clipsToBounds = true coin.center = sender.location(in: self.view) UIView.animate(withDuration: 0.10, animations: { }) { (Bool) -> Void in } self.view.addSubview(coin) } else if coinCount == 1 { coin.removeFromSuperview() } coinCount = coinCount + 1 coinCount = coinCount % 2 startGame() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { cmManager.stopDeviceMotionUpdates() } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { startGame() } @IBAction override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { print("touchesMoved") // タッチイベントを取得. let aTouch = touches.first! // 移動した先の座標を取得. let location = aTouch.location(in: coin) // 移動する前の座標を取得. let prevLocation = aTouch.previousLocation(in: coin) // CGRect生成. var myFrame: CGRect = coin.frame // ドラッグで移動したx, y距離をとる. let deltaX: CGFloat = location.x - prevLocation.x let deltaY: CGFloat = location.y - prevLocation.y // 移動した分の距離をmyFrameの座標にプラスする. myFrame.origin.x += deltaX myFrame.origin.y += deltaY // frameにmyFrameを追加. coin.frame = myFrame } let cmManager = CMMotionManager() let scrSize: CGSize = UIScreen.main.bounds.size let mag: Double = 1.0 var vx, vy: Double? override func viewDidLoad() { super.viewDidLoad() vx = 0.0 vy = 0.0 startGame() } func startGame() { cmManager.deviceMotionUpdateInterval = 0.03 let handler: CMDeviceMotionHandler = { (motionData: CMDeviceMotion?, error: Error?) -> Void in self.stepGame(motionData: motionData, error: error) } cmManager.startDeviceMotionUpdates(to: OperationQueue.main, withHandler: handler) } func stepGame(motionData: CMDeviceMotion?, error: Error?) { var xMin, xMax, yMin, yMax: Int xMin = Int(coin.frame.width / 2) xMax = Int(scrSize.width) - xMin yMin = Int(coin.frame.height / 2) yMax = Int(scrSize.height) - yMin if let motion = motionData { let gravity = motion.gravity vx = vx! + gravity.x * mag vy = vy! - gravity.y * mag var x: Int = Int(Double(coin.center.x) + vx!) var y: Int = Int(Double(coin.center.y) + vy!) if (x < xMin) { x = xMin; vx = 0.0 } else if (x > xMax) { x = xMax; vx = 0.0 } if (y < yMin) { y = yMin; vy = 0.0 } else if (y > yMax) { y = yMax; vy = 0.0 } coin.center = CGPoint(x: x,y: y) } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }

使用している言語

swift
Xvode 11.4

ご教授のほど、よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

おそらく、
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

startGame()

の、タップを解除した時に、ドラッグした方向へ加速度がつく。と言うような
コードを入力すれば良いと思うのですが、考え方はあってますでしょうか?

基本的にそういう考え方でいいと思います。

ただ、この時はドラッグ直前まで累積させていた加速度成分vx, vyが残っていますので、これをリセットするなどの処理が必要でしょうし、ドラッグを解除した瞬間の加速度をどのようにして計算するのかなど考える必要がありそうです。

この辺りは、個々のメソッドの使い方というよりも、むしろ得られている重力加速度などの値をいかに加工するのかといったテクニックの見せ所のようにも思えます(物理法則に従わせることよりも、見せ方かも)。

#追記

たとえば、こんな感じでコードを変更すれば、それなりにそれっぽい処理ができます。
必要なところだけ抜粋しているので、処理の流れをよく考えてオリジナルに追加してみてください。

swift

1import UIKit 2import CoreMotion 3 4class ViewController: UIViewController { 5 // 略 6 7 // ここから追加 8 // リングバッファ 9 // 過去10回分のドラッグ履歴を残す 10 var index = 0 11 var maxIndex = 10 12 var dX: [CGFloat] = [] 13 var dY: [CGFloat] = [] 14 // ここまで 15 16 // 値を書き換えるので、let -> var に変更 17 var mag: Double = 20.0 18 19 override func viewDidLoad() { 20 super.viewDidLoad() 21 22 // ここから追加 23 // 配列の初期化 24 // dX = [0.0, 0.0, ..(全部で10回繰り返し), 0.0] と同じ 25 dX = Array(repeating: CGFloat(0.0), count: maxIndex) 26 dY = Array(repeating: CGFloat(0.0), count: maxIndex 27 // ここまで。 28 29 // 以下略 30 } 31 32 // 略 33 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 34 // dx, dy を表示 -> デバッグのため。本質的な処理ではない 35 for i in 0..<dX.count { 36 print("dx[(i)]: (dX[i]), dy[(i)]: (dY[i])") 37 } 38 39 // ここからが本質的な処理 40 // 新しい加速度(vx, vy)を計算 41 // ドラッグした値の変化からドラッグ後の加速度を計算する 42 // 過去10回分の変位(dX, dY)から加速度を求める -> 実際は過去10回分の平均を求めているだけ 43 let amp = 5.0 // 倍率。適切に決める 44 // 簡潔に書くため reduce を使っているが、計算は for 文を使って配列全ての要素を足して、最後に要素数で割る処理と同じ 45 vx = Double(dX.reduce(0.0) { $0 + $1 } / CGFloat(dX.count)) * amp 46 vy = Double(dY.reduce(0.0) { $0 + $1 } / CGFloat(dY.count)) * amp 47 48 print("vx: ", vx!, "vy: ", vy!) 49 // ここまで 50 startGame() 51 } 52 53 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 54 print("touchesMoved", touches.count) 55 // タッチイベントを取得. 56 // 途中略 57 58 // ドラッグで移動したx, y距離をとる. 59 let deltaX: CGFloat = location.x - prevLocation.x 60 let deltaY: CGFloat = location.y - prevLocation.y 61 62 // ここから追加 63 // deltaX, deltaY をリングバッファに保存 64 dX[index] = deltaX 65 dY[index] = deltaY 66 // リングバッファの現在のインデックス(配列の要素)を一つ進める 67 index += 1 68 // インデックスが maxIndex を超えた場合には 0 に戻す(剰余の計算) 69 index %= maxIndex 70 // ここまで追加 71 72 // 移動した分の距離をmyFrameの座標にプラスする. 73 myFrame.origin.x += deltaX 74 myFrame.origin.y += deltaY 75 // 以下略 76 } 77 78 // 以下略 79}

投稿2020/04/12 14:31

編集2020/04/13 10:59
TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

magiee

2020/04/12 15:58

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { vx = 0.0 vy = 0.0 startGame() } と、する事で、ドラッグ直前まで累積させていた加速度分はリセットする事ができました。 そこから、さらにドラッグした方向への加速度をつけたいのですが、やはりまだ分からないです。 下記のリンクが参考になりそうが気がするのですが、どこの部分を参考にして良いのか分からないです。。。 https://lepetit-prince.net/ios/?p=2155
TsukubaDepot

2020/04/12 16:12

「ドラッグした方向への加速度をつける」ためには、たとえばどういうデータが必要になると考えていらっしゃいますか? そのアイデアが明確であれば、使うべきメソッドも明確になってくると思いますが、そこが不十分であれば見つかりにくいと思います。
magiee

2020/04/12 16:25

どのようなメソッドになるのか、まだ把握できておりませんが、「おはじき」のようなアプリを参考にすれば、それに近いコードがあるのでは、と考えています。 参考になるようなコードがあれば、教えて頂きたいです。
TsukubaDepot

2020/04/12 16:38

「メソッド」はあくまでも実現の手段なので、まずはに取得できそうなデータをどのように加工すべきか考えるべきかとおもいます。 たとえば、ドラッグをやめる直前のデータ、たとえばある一定回数分の座標列が得られれば、ある一定時間においてどのような方向に、どのような速度で移動したか計算できそうに思います。 まずは、「具体的に」どのように実現したいのかを考えてみてはいかがでしょうか。 この部分を私が考えてしまうと、それはもはやmagieeさんが考えたアプリではなくなってしまいます。 magieeさんは、magieeさんなりのアイデアと味付けでアプリを作りたいはずですから、そこはやはりご本人で考えてみるべきではないでしょうか。 ちなみに、ピンボールやおはじきの例を出されていますが、すくなくともリンク先に示されているnine ballのアプリ例は直接参考にはならないとおもいます。Objetive-cやそこで使われているSpriteKitは私が充分に理解できていませんが、ボールの動きなどはSpriteKitの物理運動シミュレータかなにかでやっているようで、今回考えているような「ドラッグ量」に応じた物理量の変化は使っていないように読めます。 nine ballのアプリではスライダを使っていますが、そのスライダの移動量がボールを打つ強さになっている上、スライダを動かした位置はスライダのドラッグをやめた瞬間に得られるので、継続的な移動量を計算するという方法とは異なってくると思います。
magiee

2020/04/13 09:31

回答して頂きありがとうございます。 現状、考えているのは、ドラッグ終了直前の速度を算出し、その速度を徐々に落としていく計算式を当てはめる。 ドラッグ終了直後の速度=画像が移動する速度で、そこから摩擦係数のような数値を当てはめていけば、徐々にスピードが落ちていくことはできると思うのですが、その計算式が思いつきません。 物理の方程式を当てはめていけばいいのでしょうが、いまいちピントきていないのが現状です。
TsukubaDepot

2020/04/13 10:40

真面目に計算するとなると摩擦係数まで考えないといけないですが、たぶんそこまで必要ないのかな、と思います。 touchesMoved()の中で計算しているdeltaX, deltaY が移動ごとの移動距離に相当するわけですが、これをたとえば直近10回分記録しておき、touchesEnded()で平均(=加速度成分のベクトル)を計算して、それを次のvx, vyに当てはめればいいとおもいます。
TsukubaDepot

2020/04/13 11:00

本文にサンプルを載せてみたので、適宜追加してみて実行してみてください。 ドラッグしていた速度に応じて、その方向に進むようになってます。
magiee

2020/04/16 10:45

回答ありがとうございます! 問題なく実行する事ができました。 let amp = 1.0  が個人的に好きな数値となりました。 自身が思い描いている物を文字としておこして頂けることに、本当に感謝しています。ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問