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

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

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

SpriteKitは、iOSやOS Xで使用できるApple社製の2Dゲーム開発フレームワークです。

Swift Playgrounds

Swift Playgroundsは、初心者・子ども向けのSwift学習アプリ。iPad/Mac用があり、コーディングの知識は不要です。Swiftの言語そのものを選択肢からタップしてコード入力できる点が特徴。段階的に学習を積み上げる初心者にも優しい設計ながらも、正統派のSwiftが学べます。

iPad

iPadは、Appleがデザインしたタブレット型コンピュータです。iPadアプリケーションは通常Xcode IDEのObjective-Cで書かれますが、iPadアプリケーションを組むためのほかのツールを使うことも可能です。

Swift

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

Q&A

解決済

1回答

2155閲覧

SwiftUI内の変数をSpriteKitに渡したい

momona

総合スコア2

SpriteKit

SpriteKitは、iOSやOS Xで使用できるApple社製の2Dゲーム開発フレームワークです。

Swift Playgrounds

Swift Playgroundsは、初心者・子ども向けのSwift学習アプリ。iPad/Mac用があり、コーディングの知識は不要です。Swiftの言語そのものを選択肢からタップしてコード入力できる点が特徴。段階的に学習を積み上げる初心者にも優しい設計ながらも、正統派のSwiftが学べます。

iPad

iPadは、Appleがデザインしたタブレット型コンピュータです。iPadアプリケーションは通常Xcode IDEのObjective-Cで書かれますが、iPadアプリケーションを組むためのほかのツールを使うことも可能です。

Swift

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

0グッド

0クリップ

投稿2021/09/21 05:52

編集2021/09/21 06:19

iPadのSwift Playgroundsで書いています。
(出来るならSwiftUIだけでやりたいのですが知識が決定的に無い為)
(下記コードでは省略していますが)ランダム機能やシークエンス機能を使いたいが為にSpriteKitをSwiftUIと併用して使っています。
質問事は、
SwiftUI(SecondView)のSegmentedControlで選ばれたタグナンバーに基づいてGameScene(SpriteKit)内のfuncのリピート時間を決定する為、
タグナンバー(もしくはそこから導き出された秒数)をGameScene内の変数に渡したいのですが、どうしたら可能でしょうか。

//iPadOS14、Swift5.3 import PlaygroundSupport import SwiftUI import SpriteKit final class ViewModel: ObservableObject { @Published var mins = 0 } //ややこしくてすみません。SegmentedControlが設置してあるのはContentViewではなく、SecondViewになります。 //ContentViewの記述は無視していただいても問題ない部分です。 struct ContentView: View { let timeText = ["05mins", "10mins", "60mins"] @State var showSecondView = false @ObservedObject var selectedIndex = ViewModel() var body: some View { let width = UIScreen.main.bounds.size.width let height = UIScreen.main.bounds.size.height ZStack { SpriteView(scene: scene) .frame(width: width, height: height) .ignoresSafeArea() VStack { HStack { Text("Exercise Time: (timeText[selectedIndex.mins])") .padding(.trailing, 180) .padding(.bottom, 480) Button(action: { self.showSecondView.toggle() }) { Image(systemName: "gearshape.fill") .font(.system(size: 30)) .padding(.bottom, 480) } } } .sheet(isPresented: self.$showSecondView) { SecondView(isPresent: self.$showSecondView, selectedIndex: self.selectedIndex) } } } } struct SecondView: View { @Binding var isPresent: Bool @ObservedObject var selectedIndex: ViewModel let timeText = ["05mins", "10mins", "60mins"] var body: some View { NavigationView { VStack { // ここのタグナンバー(もしくはそこからSwitch文等を使って秒数に変換した値が入っている変数)をGameScene(SpriteKit)に渡したいです。 Picker(selection: $selectedIndex.mins, label: Text("ExerciseTime")) { Text("05mins").tag(0) Text("10mins").tag(1) Text("60mins").tag(2) } .pickerStyle(SegmentedPickerStyle()) Text("Exercise Time: (timeText[selectedIndex.mins])") .navigationBarTitle("SecondView", displayMode: .inline) .navigationBarItems(trailing: Button(action: {self.isPresent = false}) { Text("Done") }) } } } } //長ったらしくて本当に申し訳ありません。 //質問はこの中のexcTimeにSecondViewのSegmentedControlで選ばれたタグナンバーに応じて秒数(Double型)を渡したいというものなので、それ以外の細かい部分は無視可能です。 var scene: SKScene { let scene = GameScene() let width = UIScreen.main.bounds.size.width let height = UIScreen.main.bounds.size.height scene.size = CGSize(width: width, height: height) scene.scaleMode = .aspectFit return scene } class GameScene: SKScene { var playMode = false let tones = ["C4"] // 本当はもっと長いリストです。コードを単純化する為にC4だけにしてあります。 var Icon: SKSpriteNode! var c4: SKAudioNode! @ObservedObject var selectedIndex = ViewModel() var excTime: Double = 10 // このexcTimeにSecondViewのSegmentedControlで選択した時間(秒数に変換したもの)を渡したいです。 var playIcon = "play.fill" let soundKey = "RemoveSoundKey" //"play.fill"と"pause.fill"の切り替えfunc func icon() { if playMode == false { playIcon = "play.fill" } else { playIcon = "pause.fill" } //長ったらしくてすみません。SF SymbolをSpriteKitで無理やり表示する為のコードなのでここは無視可能です。 let image = UIImage(systemName: playIcon)!.withTintColor(.white) let data = image.pngData() let newImage = UIImage(data: data!) let texture = SKTexture(image: newImage!) Icon = SKSpriteNode.init(texture: texture) Icon.position = CGPoint(x: self.frame.midX, y: 250) Icon.size = CGSize(width: 50, height: 50) Icon.name = "playModeIcon" self.addChild(Icon) } override public func didMove(to view: SKView) { //iPadで作っているのでカラーコードが変ですみません。無視可能な部分です。 backgroundColor = colorLiteral(red: 0.21568629145622253, green: 0.10196079313755035, blue: 0.5803921818733215, alpha: 1.0) icon() } override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?){ for touch: AnyObject in touches { let location = touch.location(in: self) let touchedNode = self.atPoint(location) if let name = touchedNode.name { if name == "playModeIcon" { if playMode == false { playMode = true touchedNode.removeFromParent() icon() //一定時間プレイしたらImage(SF Symbol)を変更して音を止めるfunc var prevTime = Date() func restart() { var currentTime = Date().timeIntervalSince(prevTime) if currentTime < excTime { // ここのexcTime変数にSecondViewのSegmentedControlで選択した時間(秒数に変換したもの)を渡したいです。今は仮として「10(秒)」が入っており、10秒固定ならプログラム自体はうまく作動しております。 print(currentTime) } else { playMode = false Icon.removeFromParent() removeAllActions() icon() } } //play c4 tone let c4URL = Bundle.main.url(forResource: tones[0], withExtension: "aiff")! c4 = SKAudioNode(url: c4URL) c4.autoplayLooped = false self.addChild(c4) //SKActions let c4Play = SKAction.run { [self] in c4.run(SKAction.play()) } let wait15 = SKAction.wait(forDuration: 1.5) let rfp = SKAction.removeFromParent() let reStart = SKAction.run(restart) let seq = SKAction.sequence([c4Play, rfp, wait15, reStart, wait15]) let rep = SKAction.repeatForever(seq) // ここのrepeatForeverを止める為の時間(秒)をSecondViewのSegmentedControlで選択する感じです。 self.run(rep, withKey: soundKey) } else { playMode = false touchedNode.removeFromParent() icon() self.removeAction(forKey: soundKey) } } } } } } PlaygroundPage.current.setLiveView(ContentView())

iPadのSwift Playgroundsからのスクショ2枚です。↓
イメージ説明

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

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

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

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

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

hoshi-takanori

2021/09/21 23:45

ContentView と GameScene の両方で ViewModel を作ってますが、どこか一箇所で作ってそのインスタンスを渡して共有すれば良いのでは。
momona

2021/09/22 05:13 編集

昼休みの時間を利用して、少しiPadをいじれたのですが、 GameScene内の「@ObservedObject var selectedIndex = ViewModel()」に頭に「//」を付けて無効にし、 「//@ObservedObject var selectedIndex = ViewModel()」 代わりにすぐその下に、適当な変数「aaa」にselectedindex.minsを代入してみたのですが、 「let aaa = selectedIndex.mins」 下記のエラーが出ました。 「Cannot find 'selectedIndex' in scope」 また夕刻に帰宅後に再チャレンジしてみますが、 hoshi-takanori さんのおっしゃった意味は、 GameScene内の「@ObservedObject var selectedIndex = ViewModel()」には「//」を付けて無効にし、 ContentView内の「@ObservedObject var selectedIndex = ViewModel()」の直後辺りに 「var aaa: Int = 0」 及び 「aaa = selectedIndex.mins」を書き加えれば、 GameScene内で変数aaaが利用出来るという意味だったでしょうか? もし違うようでしたら具体的に教えていただけると助かります。
momona

2021/09/22 04:45

すみません。ちょっと訂正です。 変数aaaはletではなく、varで宣言しないとダメですね。
hoshi-takanori

2021/09/22 04:46

違います。例えば ContentView と SecondView では同じ ViewModel のインスタンスを共有しているので、どちらかで selectedindex.mins を変更すれば、もう片方でもその変更に応じて表示が更新されます。 ただ、これが自動的に行われるのは ContentView も SecondView も SwiftUI の View だからで、そうでない場合は明示的に変更があったときに呼ばれるコールバックか何かを設定する必要があると思いますが、具体的な方法は知りません。
guest

回答1

0

自己解決

SwiftUIとSpriteKitでの変数の共有に成功しましたので報告させていただきます。
やった事は、まずhoshi-takanoriさんにご指摘いただいたGameScene内の@ObservedObject var selectedIndex = ViewModel() の頭に//を付けてこの1文を無効にしました。

次に、プログラム冒頭の
import SpriteKit
import SwiftUI の直下に
var timeIndex: Int = 0 を追加。

SecondView内、Button(action: { }内に
timeIndex = selectedIndex.mins を追加。

class GameScene: SKScene { の下に
func excTimeDouble() {
if timeIndex == 0 {
excTime = 300
}
else if timeIndex == 1 {
excTime = 600
}
else {
excTime = 3600
}
}
を追加。

最後に、func restart() { の下に、
excTimeDouble() を追加。

以上で問題なく、selectedIndex.minsの値をGameSceneに渡す事が出来ました。

投稿2021/09/22 13:42

編集2021/09/22 13:45
momona

総合スコア2

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問