├── .gitignore ├── Newton's Notebook.swiftpm ├── .swiftpm │ └── xcode │ │ └── package.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── ContentView.swift ├── ContentViewModel.swift ├── GameSceneViewController.swift ├── GameSceneViewRepresentable.swift ├── Item.swift ├── MyApp.swift ├── NodeType.swift ├── Package.swift ├── Resources │ ├── Media.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ └── スクリーンショット 2023-04-20 3.45.10.png │ │ ├── Contents.json │ │ ├── ball.imageset │ │ │ ├── Contents.json │ │ │ └── ball2.png │ │ ├── basket.imageset │ │ │ ├── Contents.json │ │ │ └── 名称未設定のアートワーク 2.png │ │ ├── basketWithApple.imageset │ │ │ ├── Contents.json │ │ │ └── 名称未設定のアートワーク 7.png │ │ ├── fire.imageset │ │ │ ├── Contents.json │ │ │ └── fire.png │ │ ├── items │ │ │ ├── Contents.json │ │ │ ├── astronomy.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 8.png │ │ │ ├── atom.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 7.png │ │ │ ├── electricity.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 6.png │ │ │ ├── fluid.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 3.png │ │ │ ├── gravity.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 5.png │ │ │ ├── magnet.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 10.png │ │ │ ├── mechanic.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 2.png │ │ │ └── wave.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── アセット 9.png │ │ ├── note.imageset │ │ │ ├── Contents.json │ │ │ └── note.jpg │ │ ├── sounds │ │ │ ├── Contents.json │ │ │ ├── burnSound.dataset │ │ │ │ ├── Contents.json │ │ │ │ └── open_bottle_gas_2-43620.mp3 │ │ │ ├── drawingSound.dataset │ │ │ │ ├── Contents.json │ │ │ │ └── scribble_short-104286.mp3 │ │ │ ├── goalSound.dataset │ │ │ │ ├── Contents.json │ │ │ │ └── success_bell-6776.mp3 │ │ │ └── itemSound.dataset │ │ │ │ ├── Contents.json │ │ │ │ └── item.mp3 │ │ ├── stroke.imageset │ │ │ ├── Contents.json │ │ │ └── stroke.png │ │ └── tree.imageset │ │ │ ├── Contents.json │ │ │ └── 名称未設定のアートワーク 5.png │ ├── bounceSound.m4a │ ├── opening_11.sks │ ├── opening_12_9.sks │ ├── stage1_11.sks │ ├── stage1_12_9.sks │ ├── stage2_11.sks │ ├── stage2_12_9.sks │ ├── stage3_11.sks │ ├── stage3_12_9.sks │ ├── stage4_11.sks │ └── stage4_12_9.sks ├── ResultView.swift ├── RotationModifier.swift ├── SKNodeExtension.swift ├── Stage.swift ├── TopView.swift └── VisualEffectView.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | .DS_Store -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import UIKit 3 | 4 | struct ContentView: View { 5 | 6 | @ObservedObject var viewModel: ContentViewModel 7 | @State var orientation = UIDeviceOrientation.unknown 8 | 9 | var body: some View { 10 | NavigationView { 11 | ZStack { 12 | NavigationLink("", destination: ResultView(collectedItems: viewModel.collectedItems), isActive: $viewModel.showResult) 13 | .opacity(0) 14 | GameSceneViewRepresentable(contentViewModel: viewModel) 15 | .ignoresSafeArea() 16 | .layoutPriority(2) 17 | VStack { 18 | HStack { 19 | Spacer() 20 | .frame(maxWidth: 16) 21 | if viewModel.isRightHanded { 22 | playRetryButton 23 | Spacer() 24 | collectedItemView 25 | } else { 26 | collectedItemView 27 | Spacer() 28 | playRetryButton 29 | } 30 | Spacer() 31 | .frame(maxWidth: 16) 32 | } 33 | Spacer() 34 | } 35 | HStack { 36 | Button { 37 | viewModel.showGoalConfirm = false 38 | viewModel.didTapPlay() 39 | } label: { 40 | Image(systemName: "arrow.clockwise") 41 | .resizable() 42 | .aspectRatio(contentMode: .fit) 43 | .frame(width: 80, height: 80) 44 | .fontWeight(.heavy) 45 | .tint(.black) 46 | } 47 | Spacer() 48 | .frame(maxWidth: 40) 49 | Button { 50 | viewModel.showGoalConfirm = false 51 | viewModel.didTapGoNext() 52 | } label: { 53 | Image(systemName: "forward.fill") 54 | .resizable() 55 | .aspectRatio(contentMode: .fit) 56 | .frame(width: 80, height: 80) 57 | .fontWeight(.heavy) 58 | .tint(.black) 59 | } 60 | } 61 | .frame(maxWidth: .infinity, maxHeight: .infinity) 62 | .background { 63 | VisualEffectView(effect: UIBlurEffect(style: .regular)) 64 | .ignoresSafeArea() 65 | } 66 | .opacity(viewModel.showGoalConfirm ? 1 : 0) 67 | VStack { 68 | Text("Play this game in landscape mode") 69 | .font(.largeTitle) 70 | Image(systemName: "rotate.right.fill") 71 | .resizable() 72 | .aspectRatio(contentMode: .fit) 73 | .frame(width: 200, height: 200) 74 | } 75 | .frame(maxWidth: .infinity, maxHeight: .infinity) 76 | .background { 77 | VisualEffectView(effect: UIBlurEffect(style: .regular)) 78 | } 79 | .opacity(orientation == .landscapeLeft || orientation == .landscapeRight ? 0 : 1) 80 | } 81 | } 82 | .navigationBarBackButtonHidden(true) 83 | .navigationViewStyle(.stack) 84 | .onAppear { 85 | orientation = (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape)! ? .landscapeLeft : .portrait 86 | } 87 | .onRotate { isLandscape in 88 | orientation = isLandscape ? .landscapeLeft : .portrait 89 | } 90 | .preferredColorScheme(.light) 91 | } 92 | 93 | var playRetryButton: some View { 94 | Button { 95 | viewModel.didTapPlay() 96 | } label: { 97 | Image(systemName: viewModel.isReadyToPlay ? "play.fill" : "arrow.clockwise") 98 | .resizable() 99 | .aspectRatio(contentMode: .fill) 100 | .frame(width: 60, height: 60) 101 | .fontWeight(.heavy) 102 | .tint(.black) 103 | .padding() 104 | } 105 | .opacity((viewModel.showGoalConfirm || viewModel.isPlayButtonHidden) ? 0 : 1) 106 | } 107 | 108 | var collectedItemView: some View { 109 | HStack { 110 | ForEach(Item.firstFourItems + Item.lastFourItems, id: \.self) { item in 111 | ZStack { 112 | Image(item.rawValue) 113 | .resizable() 114 | .aspectRatio(contentMode: .fit) 115 | .frame(width: 40, height: 40) 116 | .opacity(viewModel.collectedItems.contains(item) ? 1 : 0) 117 | Image(item.rawValue) 118 | .resizable() 119 | .aspectRatio(contentMode: .fit) 120 | .frame(width: 40, height: 40) 121 | .opacity(0.2) 122 | } 123 | } 124 | } 125 | .padding(16) 126 | .background( 127 | RoundedRectangle(cornerRadius: 16) 128 | .fill(Color.accentColor.opacity(0.2)) 129 | ) 130 | } 131 | } 132 | 133 | struct ContentView_Previews: PreviewProvider { 134 | static var previews: some View { 135 | ContentView(viewModel: .init(isRightHanded: true)) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/ContentViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewModel.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/14. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class ContentViewModel: ObservableObject { 12 | @Published var isRightHanded: Bool 13 | @Published var isReadyToPlay = true 14 | @Published var isPlayButtonHidden = false 15 | @Published var showResult = false 16 | @Published var showGoalConfirm = false 17 | let retryAction = PassthroughSubject() 18 | let playAction = PassthroughSubject() 19 | let goNextAction = PassthroughSubject() 20 | 21 | @Published var collectedItems = [Item]() 22 | 23 | init(isRightHanded: Bool) { 24 | self.isRightHanded = isRightHanded 25 | } 26 | 27 | func didTapPlay() { 28 | if isReadyToPlay { 29 | playAction.send() 30 | } else { 31 | retryAction.send() 32 | } 33 | isReadyToPlay.toggle() 34 | } 35 | 36 | func didTapGoNext() { 37 | isReadyToPlay = true 38 | goNextAction.send() 39 | } 40 | 41 | func showResultView() { 42 | showResult = true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/GameSceneViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SketchPuzzle 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/12. 6 | // 7 | 8 | import AVFoundation 9 | import UIKit 10 | import PencilKit 11 | import SpriteKit 12 | 13 | class GameSceneViewController: UIViewController, UIPencilInteractionDelegate { 14 | 15 | var coordinator: GameSceneViewRepresentable.Coordinator? 16 | 17 | var canvasView: PKCanvasView! 18 | var skView: SKView! 19 | var currentStage: Stage = .opening 20 | var scene: SKScene! 21 | 22 | var pencilAudioPlayer: AVAudioPlayer! 23 | var itemAudioPlayer: AVAudioPlayer! 24 | var goalAudioPlayer: AVAudioPlayer! 25 | var bounceAudioPlayer: AVAudioPlayer! 26 | var burnAudioPlayer: AVAudioPlayer! 27 | 28 | let blackInk = PKInkingTool(ink: PKInk(.pen, color: .gray), width: 3) 29 | 30 | let bounceAudioNode = SKAudioNode(url: Bundle.main.url(forResource: "bounceSound", withExtension: "m4a")!) 31 | var isPlayingAudio = false 32 | var collectedItems = [Item]() 33 | var newlyCollectedItems = [Item]() 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | setupSpriteKitView() 38 | setupCanvasView() 39 | setupAudioPlayer() 40 | setupScene(stage: .opening) 41 | } 42 | 43 | func setupAudioPlayer() { 44 | if let pencilSoundDataAsset = NSDataAsset(name: "drawingSound") { 45 | do { 46 | pencilAudioPlayer = try AVAudioPlayer(data: pencilSoundDataAsset.data) 47 | pencilAudioPlayer.numberOfLoops = -1 48 | pencilAudioPlayer.volume = 0.2 49 | pencilAudioPlayer.prepareToPlay() 50 | } catch { 51 | print("Error loading audio file: \(error.localizedDescription)") 52 | } 53 | } 54 | 55 | if let itemSoundDataAsset = NSDataAsset(name: "itemSound") { 56 | do { 57 | itemAudioPlayer = try AVAudioPlayer(data: itemSoundDataAsset.data) 58 | itemAudioPlayer.volume = 0.2 59 | itemAudioPlayer.prepareToPlay() 60 | } catch { 61 | print("Error loading audio file: \(error.localizedDescription)") 62 | } 63 | } 64 | 65 | if let goalSoundDataAsset = NSDataAsset(name: "goalSound") { 66 | do { 67 | goalAudioPlayer = try AVAudioPlayer(data: goalSoundDataAsset.data) 68 | goalAudioPlayer.volume = 0.2 69 | goalAudioPlayer.prepareToPlay() 70 | } catch { 71 | print("Error loading audio file: \(error.localizedDescription)") 72 | } 73 | } 74 | 75 | if let bounceSoundDataAsset = NSDataAsset(name: "bounceSound") { 76 | do { 77 | bounceAudioPlayer = try AVAudioPlayer(data: bounceSoundDataAsset.data) 78 | bounceAudioPlayer.volume = 0.2 79 | bounceAudioPlayer.prepareToPlay() 80 | } catch { 81 | print("Error loading audio file: \(error.localizedDescription)") 82 | } 83 | } 84 | 85 | if let burnSoundDataAsset = NSDataAsset(name: "burnSound") { 86 | do { 87 | burnAudioPlayer = try AVAudioPlayer(data: burnSoundDataAsset.data) 88 | burnAudioPlayer.volume = 0.2 89 | burnAudioPlayer.prepareToPlay() 90 | } catch { 91 | print("Error loading audio file: \(error.localizedDescription)") 92 | } 93 | } 94 | } 95 | 96 | func setupCanvasView() { 97 | canvasView = PKCanvasView(frame: view.bounds) 98 | canvasView.backgroundColor = .clear 99 | canvasView.delegate = self 100 | canvasView.tool = blackInk 101 | canvasView.drawingPolicy = .pencilOnly 102 | canvasView.isUserInteractionEnabled = false 103 | let pencilInteraction = UIPencilInteraction() 104 | pencilInteraction.delegate = self 105 | view.addInteraction(pencilInteraction) 106 | view.addSubview(canvasView) 107 | } 108 | 109 | func setupSpriteKitView() { 110 | skView = SKView(frame: view.bounds) 111 | print(view.frame) 112 | skView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 113 | skView.preferredFramesPerSecond = 120 114 | skView.ignoresSiblingOrder = true 115 | view.addSubview(skView) 116 | } 117 | 118 | func setupScene(stage: Stage, isRetry: Bool = false) { 119 | if scene != nil { 120 | scene.removeFromParent() 121 | } 122 | currentStage = stage 123 | scene = stage.scene(screenFrame: UIScreen.main.nativeBounds) 124 | print(UIScreen.main.nativeBounds) 125 | scene.size = view.bounds.size 126 | scene.scaleMode = .aspectFit 127 | scene.physicsWorld.gravity = CGVector(dx: 0, dy: 0) 128 | scene.delegate = self 129 | scene.physicsWorld.contactDelegate = self 130 | scene.childNode(withName: "background")?.zPosition = -1 131 | [NodeType.fire, NodeType.basket].forEach { nodeType in 132 | scene.enumerateChildNodes(withName: nodeType.name) { node, _ in 133 | guard let texture = (node as? SKSpriteNode)?.texture else { return } 134 | node.physicsBody = SKPhysicsBody(texture: texture, size: node.frame.size) 135 | node.physicsBody?.affectedByGravity = false 136 | node.physicsBody?.isDynamic = false 137 | node.zPosition = 1 138 | node.setup(with: nodeType) 139 | } 140 | } 141 | Item.allCases.forEach { item in 142 | scene.enumerateChildNodes(withName: item.name) { node, _ in 143 | guard let texture = (node as? SKSpriteNode)?.texture else { return } 144 | node.physicsBody = SKPhysicsBody(texture: texture, size: node.frame.size) 145 | node.physicsBody?.affectedByGravity = false 146 | node.physicsBody?.isDynamic = false 147 | node.zPosition = 1 148 | node.setup(with: .item) 149 | let fadeInAction = SKAction.fadeAlpha(to: 0.3, duration: 1.0) 150 | let fadeOutAction = SKAction.fadeAlpha(to: 1.0, duration: 1.0) 151 | let fadeSequence = SKAction.sequence([fadeInAction, fadeOutAction]) 152 | let repeatFadeAction = SKAction.repeat(fadeSequence, count: 1) 153 | node.run(repeatFadeAction) 154 | 155 | } 156 | } 157 | setupBall() 158 | setupBounceAudio() 159 | if stage == .opening || isRetry { 160 | skView.presentScene(scene) 161 | } else { 162 | skView.presentScene(scene, transition: .push(with: .up, duration: 2)) 163 | coordinator?.setPlayButtonHidden(hidden: true) 164 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 165 | self.canvasView.isUserInteractionEnabled = true 166 | self.coordinator?.setPlayButtonHidden(hidden: false) 167 | } 168 | } 169 | } 170 | 171 | func setupBall() { 172 | guard let ballNode = scene.childNode(withName: NodeType.ball.name) else { return } 173 | ballNode.physicsBody = SKPhysicsBody(circleOfRadius: 20) 174 | ballNode.physicsBody?.affectedByGravity = true 175 | ballNode.physicsBody?.isDynamic = true 176 | ballNode.setup(with: .ball) 177 | ballNode.zPosition = 2 178 | } 179 | 180 | func setupBounceAudio() { 181 | bounceAudioNode.removeFromParent() 182 | bounceAudioNode.autoplayLooped = false 183 | scene.addChild(bounceAudioNode) 184 | } 185 | 186 | func setGravity(enabled: Bool) { 187 | scene.physicsWorld.gravity = CGVector(dx: 0, dy: enabled ? -9.8 : 0) 188 | if currentStage == .opening { 189 | coordinator?.setPlayButtonHidden(hidden: true) 190 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { 191 | self.setupNextScene() 192 | self.coordinator?.setReadyToPlay(isReady: true) 193 | } 194 | } 195 | } 196 | 197 | func retry() { 198 | setupScene(stage: currentStage, isRetry: true) 199 | newlyCollectedItems = [] 200 | coordinator?.updateCollectedItems(collectedItems: collectedItems + newlyCollectedItems) 201 | print("Collected Items: \(collectedItems)") 202 | canvasView.drawing = PKDrawing() 203 | } 204 | } 205 | 206 | extension GameSceneViewController: SKPhysicsContactDelegate, SKSceneDelegate { 207 | func didBegin(_ contact: SKPhysicsContact) { 208 | guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else { return } 209 | switch nodeA.name { 210 | case NodeType.line.name: 211 | if !isPlayingAudio { 212 | bounceAudioNode.run(.play()) 213 | isPlayingAudio = true 214 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { 215 | self.isPlayingAudio = false 216 | } 217 | } 218 | case NodeType.fire.name: 219 | DispatchQueue.global(qos: . userInitiated).async { 220 | self.burnAudioPlayer.currentTime = 0 221 | self.burnAudioPlayer.play() 222 | } 223 | removeBall() 224 | case NodeType.basket.name: 225 | if currentStage == .opening { 226 | nodeB.removeFromParent() 227 | } 228 | goal() 229 | default: 230 | if Item.allCases.map({ $0.name }).contains(nodeA.name) { 231 | getItem(node: nodeA) 232 | } 233 | } 234 | } 235 | 236 | func getItem(node: SKNode) { 237 | node.removeFromParent() 238 | guard let item = Item(rawValue: node.name ?? "") else { return } 239 | newlyCollectedItems.append(item) 240 | coordinator?.updateCollectedItems(collectedItems: collectedItems + newlyCollectedItems) 241 | DispatchQueue.global(qos: .userInitiated).async { 242 | self.itemAudioPlayer.currentTime = 0 243 | self.itemAudioPlayer.play() 244 | } 245 | } 246 | 247 | func goal() { 248 | removeBall() 249 | DispatchQueue.global(qos: .background).async { 250 | self.goalAudioPlayer.currentTime = 0 251 | self.goalAudioPlayer.play() 252 | } 253 | if currentStage != .opening { 254 | coordinator?.showGoalConfirm() 255 | } 256 | } 257 | 258 | func removeBall() { 259 | scene.childNode(withName: NodeType.ball.name)?.removeFromParent() 260 | } 261 | 262 | func setupNextScene() { 263 | collectedItems += newlyCollectedItems 264 | newlyCollectedItems = [] 265 | bounceAudioNode.removeFromParent() 266 | guard let nextStage = currentStage.next else { 267 | showResultView() 268 | return 269 | } 270 | canvasView.isUserInteractionEnabled = false 271 | setupScene(stage: nextStage) 272 | } 273 | 274 | func showResultView() { 275 | coordinator?.showResultView() 276 | } 277 | } 278 | 279 | extension GameSceneViewController: PKCanvasViewDelegate { 280 | func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) { 281 | let lastStrokeIndex = canvasView.drawing.strokes.count - 1 282 | guard lastStrokeIndex >= 0 else { return } 283 | guard let lastStroke = canvasView.drawing.strokes.last else { return } 284 | let canvasViewBounds = canvasView.bounds 285 | DispatchQueue.global(qos: .userInitiated).async { 286 | let image = canvasView.drawing.image(from: canvasViewBounds, scale: UIScreen.main.scale) 287 | let texture = SKTexture(image: image) 288 | let lineNode = SKSpriteNode(texture: texture) 289 | lineNode.position = CGPoint(x: self.scene.frame.midX, y: self.scene.frame.midY) 290 | lineNode.size = canvasViewBounds.size 291 | lineNode.physicsBody = SKPhysicsBody(texture: texture, size: lineNode.size) 292 | lineNode.physicsBody?.isDynamic = false 293 | lineNode.physicsBody?.affectedByGravity = false 294 | lineNode.setup(with: .line) 295 | lineNode.physicsBody?.restitution = self.calculateRestitution(from: lastStroke) 296 | lineNode.zPosition = 1 297 | 298 | DispatchQueue.main.async { 299 | self.scene.addChild(lineNode) 300 | var updatedDrawing = canvasView.drawing 301 | updatedDrawing.strokes.removeAll() 302 | canvasView.drawing = updatedDrawing 303 | self.scene.physicsWorld.speed = 1 304 | } 305 | } 306 | } 307 | 308 | func calculateRestitution(from stroke: PKStroke) -> CGFloat { 309 | let path = stroke.path 310 | let average = path.map({ $0.force }).reduce(0, +) / CGFloat(path.count) 311 | if average > 1.5 { 312 | return 1.5 313 | } else { 314 | return average 315 | } 316 | } 317 | 318 | func canvasViewDidEndUsingTool(_ canvasView: PKCanvasView) { 319 | pencilAudioPlayer.stop() 320 | } 321 | 322 | func canvasViewDidBeginUsingTool(_ canvasView: PKCanvasView) { 323 | self.scene.physicsWorld.speed = 0.3 324 | DispatchQueue.global(qos: .userInitiated).async { 325 | self.pencilAudioPlayer.play() 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/GameSceneViewRepresentable.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Combine 3 | 4 | struct GameSceneViewRepresentable: UIViewControllerRepresentable { 5 | typealias UIViewControllerType = GameSceneViewController 6 | 7 | @ObservedObject var contentViewModel: ContentViewModel 8 | 9 | func makeUIViewController(context: Context) -> GameSceneViewController { 10 | return context.coordinator.viewController 11 | } 12 | 13 | func updateUIViewController(_ uiViewController: GameSceneViewController, context: Context) { 14 | // 何もしない 15 | } 16 | 17 | func makeCoordinator() -> Coordinator { 18 | let viewController = GameSceneViewController() 19 | return Coordinator(contentViewModel: contentViewModel, viewController: viewController) 20 | } 21 | 22 | class Coordinator: NSObject { 23 | 24 | var cancellables = Set() 25 | 26 | var contentViewModel: ContentViewModel 27 | var viewController: GameSceneViewController 28 | 29 | init(contentViewModel: ContentViewModel, viewController: GameSceneViewController) { 30 | self.contentViewModel = contentViewModel 31 | self.viewController = viewController 32 | super.init() 33 | viewController.coordinator = self 34 | subscribeToContentViewModel() 35 | } 36 | 37 | func subscribeToContentViewModel() { 38 | contentViewModel.retryAction.sink { [weak self] _ in 39 | self?.retry() 40 | }.store(in: &cancellables) 41 | 42 | contentViewModel.playAction.sink { [weak self] _ in 43 | self?.applyGravity() 44 | }.store(in: &cancellables) 45 | 46 | contentViewModel.goNextAction.sink { [weak self] _ in 47 | self?.goNext() 48 | }.store(in: &cancellables) 49 | } 50 | 51 | func retry() { 52 | viewController.retry() 53 | } 54 | 55 | func applyGravity() { 56 | viewController.setGravity(enabled: true) 57 | } 58 | 59 | func goNext() { 60 | viewController.setupNextScene() 61 | } 62 | 63 | func showResultView() { 64 | contentViewModel.showResultView() 65 | } 66 | 67 | func showGoalConfirm() { 68 | contentViewModel.showGoalConfirm = true 69 | } 70 | 71 | func setPlayButtonHidden(hidden: Bool) { 72 | contentViewModel.isPlayButtonHidden = hidden 73 | } 74 | 75 | func setReadyToPlay(isReady: Bool) { 76 | contentViewModel.isReadyToPlay = isReady 77 | } 78 | 79 | func updateCollectedItems(collectedItems: [Item]) { 80 | contentViewModel.collectedItems = collectedItems 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Item.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Item.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/17. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Item: String, CaseIterable { 11 | case fluid 12 | case magnet 13 | case mechanic 14 | case gravity 15 | case wave 16 | case electricity 17 | case atom 18 | case astronomy 19 | 20 | var name: String { 21 | rawValue 22 | } 23 | 24 | var displayName: String { 25 | switch self { 26 | case .fluid: 27 | return "Fluids" 28 | case .magnet: 29 | return "Magnetism" 30 | case .mechanic: 31 | return "Mechanics" 32 | case .gravity: 33 | return "Gravity" 34 | case .wave: 35 | return "Waves" 36 | case .electricity: 37 | return "Electricity" 38 | case .atom: 39 | return "Atoms" 40 | case .astronomy: 41 | return "Astronomy" 42 | } 43 | } 44 | 45 | var description: String { 46 | switch self { 47 | case .fluid: 48 | return "Fluids, like water or air, can flow and change their shape to fill up any container.\n They are an important part of physics that studies how liquids and gases behave." 49 | case .magnet: 50 | return "Have you ever played with a pair of magnets and felt them attract or repel each other?\nMagnetism is the force behind that, and it's a fascinating area of physics that deals with the behavior of magnets and magnetic materials." 51 | case .mechanic: 52 | return "When you ride a bicycle, mechanics is the part of physics that explains how the wheels, pedals, and gears work together to make you move easily.\nIt's all about understanding motion and the forces involved." 53 | case .gravity: 54 | return " Imagine dropping a ball, and it falls to the ground.\nGravity is the force that pulls the ball and everything else towards the Earth. It's just one aspect of physics that how things interact." 55 | case .wave: 56 | return "Imagine the sound you hear when someone plays a guitar or the way your radio receives signals.\nWaves are a way that energy moves through things like sound, electromagnetic waves (including radio waves), and light.\nThey are an important part of physics that helps us understand how energy travels and interacts with our world." 57 | case .electricity: 58 | return "When you turn on a light or charge your iPhone, electricity powers these devices.\nThis aspect of physics explores the movement of tiny particles called electrons and how they create the energy we use every day." 59 | case .atom: 60 | return "Everything you see and touch is made up of tiny building blocks called atoms.\nThey are part of the branch of physics called atomic physics, which helps us understand how these tiny particles come together to create the world around us." 61 | case .astronomy: 62 | return "When you look at the stars in the night sky, you're seeing just a tiny part of our vast universe.\nAstronomy is the branch of physics that studies outer space and celestial objects, helping us learn more about the cosmos." 63 | } 64 | } 65 | 66 | static var firstFourItems: [Item] { 67 | [.astronomy, .gravity, .magnet, .wave] 68 | } 69 | 70 | static var lastFourItems: [Item] { 71 | [.mechanic, .fluid, .atom, .electricity] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/MyApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import UIKit 3 | import AVFoundation 4 | 5 | @main 6 | struct MyApp: App { 7 | 8 | @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 9 | 10 | var body: some Scene { 11 | WindowGroup { 12 | TopView() 13 | } 14 | } 15 | } 16 | 17 | 18 | class AppDelegate: NSObject, UIApplicationDelegate { 19 | 20 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 21 | try! AVAudioSession.sharedInstance().setCategory(.ambient) 22 | try! AVAudioSession.sharedInstance().setActive(true) 23 | return true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/NodeType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NodeType.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/14. 6 | // 7 | 8 | import Foundation 9 | import SpriteKit 10 | 11 | enum NodeType: UInt32 { 12 | case ball = 1 13 | case line = 2 14 | case basket = 4 15 | case fire = 8 16 | case item = 16 17 | 18 | var name: String { 19 | switch self { 20 | case .ball: 21 | return "ball" 22 | case .line: 23 | return "line" 24 | case .basket: 25 | return "basket" 26 | case .fire: 27 | return "fire" 28 | case .item: 29 | return "" 30 | } 31 | } 32 | 33 | var categoryBitMask: UInt32 { 34 | return rawValue 35 | } 36 | 37 | var collisionBitMask: UInt32 { 38 | switch self { 39 | case .ball: 40 | return NodeType.line.categoryBitMask 41 | case .line: 42 | return NodeType.ball.categoryBitMask 43 | case .fire, .basket, .item: 44 | return 0 45 | } 46 | } 47 | 48 | var contactTestBitMask: UInt32 { 49 | switch self { 50 | case .ball: 51 | return NodeType.line.categoryBitMask | NodeType.fire.categoryBitMask | NodeType.basket.categoryBitMask 52 | case .line, .basket, .fire, .item: 53 | return NodeType.ball.categoryBitMask 54 | } 55 | } 56 | 57 | var restitution: CGFloat { 58 | switch self { 59 | case .line: 60 | return 1.0 61 | case .ball: 62 | return 0.2 63 | default: 64 | return 0.2 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.6 2 | 3 | // WARNING: 4 | // This file is automatically generated. 5 | // Do not edit it by hand because the contents will be replaced. 6 | 7 | import PackageDescription 8 | import AppleProductTypes 9 | 10 | let package = Package( 11 | name: "Newton's Notebook", 12 | platforms: [ 13 | .iOS("16.0") 14 | ], 15 | products: [ 16 | .iOSApplication( 17 | name: "Newton's Notebook", 18 | targets: ["AppModule"], 19 | bundleIdentifier: "com.kyoya.SketchPuzzle", 20 | teamIdentifier: "3X7LEN654Y", 21 | displayVersion: "1.0", 22 | bundleVersion: "1", 23 | appIcon: .asset("AppIcon"), 24 | accentColor: .presetColor(.indigo), 25 | supportedDeviceFamilies: [ 26 | .pad 27 | ], 28 | supportedInterfaceOrientations: [ 29 | .portrait, 30 | .landscapeRight, 31 | .landscapeLeft, 32 | .portraitUpsideDown(.when(deviceFamilies: [.pad])) 33 | ] 34 | ) 35 | ], 36 | targets: [ 37 | .executableTarget( 38 | name: "AppModule", 39 | path: ".", 40 | resources: [ 41 | .process("Resources") 42 | ] 43 | ) 44 | ] 45 | ) -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "スクリーンショット 2023-04-20 3.45.10.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/AppIcon.appiconset/スクリーンショット 2023-04-20 3.45.10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/AppIcon.appiconset/スクリーンショット 2023-04-20 3.45.10.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/ball.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ball2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/ball.imageset/ball2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/ball.imageset/ball2.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/basket.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "名称未設定のアートワーク 2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/basket.imageset/名称未設定のアートワーク 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/basket.imageset/名称未設定のアートワーク 2.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/basketWithApple.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "名称未設定のアートワーク 7.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/basketWithApple.imageset/名称未設定のアートワーク 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/basketWithApple.imageset/名称未設定のアートワーク 7.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/fire.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "fire.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/fire.imageset/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/fire.imageset/fire.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/astronomy.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 8.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/astronomy.imageset/アセット 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/astronomy.imageset/アセット 8.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/atom.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 7.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/atom.imageset/アセット 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/atom.imageset/アセット 7.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/electricity.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 6.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/electricity.imageset/アセット 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/electricity.imageset/アセット 6.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/fluid.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 3.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/fluid.imageset/アセット 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/fluid.imageset/アセット 3.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/gravity.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 5.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/gravity.imageset/アセット 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/gravity.imageset/アセット 5.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/magnet.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 10.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/magnet.imageset/アセット 10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/magnet.imageset/アセット 10.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/mechanic.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/mechanic.imageset/アセット 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/mechanic.imageset/アセット 2.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/wave.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "アセット 9.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/items/wave.imageset/アセット 9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/items/wave.imageset/アセット 9.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/note.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "note.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/note.imageset/note.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/note.imageset/note.jpg -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/burnSound.dataset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "data" : [ 3 | { 4 | "filename" : "open_bottle_gas_2-43620.mp3", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/burnSound.dataset/open_bottle_gas_2-43620.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/burnSound.dataset/open_bottle_gas_2-43620.mp3 -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/drawingSound.dataset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "data" : [ 3 | { 4 | "filename" : "scribble_short-104286.mp3", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/drawingSound.dataset/scribble_short-104286.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/drawingSound.dataset/scribble_short-104286.mp3 -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/goalSound.dataset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "data" : [ 3 | { 4 | "filename" : "success_bell-6776.mp3", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/goalSound.dataset/success_bell-6776.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/goalSound.dataset/success_bell-6776.mp3 -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/itemSound.dataset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "data" : [ 3 | { 4 | "filename" : "item.mp3", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/itemSound.dataset/item.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/sounds/itemSound.dataset/item.mp3 -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/stroke.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "stroke.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/stroke.imageset/stroke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/stroke.imageset/stroke.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/tree.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "名称未設定のアートワーク 5.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/Media.xcassets/tree.imageset/名称未設定のアートワーク 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/Media.xcassets/tree.imageset/名称未設定のアートワーク 5.png -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/bounceSound.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/bounceSound.m4a -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/opening_11.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/opening_11.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/opening_12_9.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/opening_12_9.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage1_11.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage1_11.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage1_12_9.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage1_12_9.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage2_11.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage2_11.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage2_12_9.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage2_12_9.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage3_11.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage3_11.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage3_12_9.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage3_12_9.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage4_11.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage4_11.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Resources/stage4_12_9.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyoya1123/Newtons-Notebook/e7815dc257e702fd6582b48177eba7a3baa9cac1/Newton's Notebook.swiftpm/Resources/stage4_12_9.sks -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/ResultView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResultView.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/18. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | struct ResultView: View { 12 | 13 | var collectedItems: [Item] 14 | @State var selectedItem = Item.atom 15 | @State var showExplanation = false 16 | 17 | var body: some View { 18 | ZStack { 19 | Image("note") 20 | .resizable() 21 | .aspectRatio(contentMode: .fill) 22 | .ignoresSafeArea() 23 | VStack { 24 | Text("🎉 You've collected all of the fallen apples!! 🎉") 25 | .font(.largeTitle) 26 | .bold() 27 | Image("basketWithApple") 28 | .resizable() 29 | .aspectRatio(contentMode: .fit) 30 | .frame(width: 200, height: 200) 31 | Spacer() 32 | .frame(maxHeight: 50) 33 | Text("Learn more about physics") 34 | .font(.title) 35 | .bold() 36 | VStack(spacing: 8) { 37 | HStack(spacing: 8) { 38 | ForEach(Item.firstFourItems, id: \.self) { item in 39 | ZStack { 40 | Button { 41 | withAnimation(.easeOut(duration: 0.2)) { 42 | selectedItem = item 43 | showExplanation = true 44 | } 45 | } label: { 46 | Image(item.rawValue) 47 | .resizable() 48 | .aspectRatio(contentMode: .fit) 49 | .frame(width: 80, height: 80) 50 | } 51 | .opacity(collectedItems.contains(item) ? 1 : 0) 52 | Image(item.rawValue) 53 | .resizable() 54 | .aspectRatio(contentMode: .fit) 55 | .frame(width: 80, height: 80) 56 | .opacity(0.2) 57 | } 58 | } 59 | } 60 | HStack(spacing: 8) { 61 | ForEach(Item.lastFourItems, id: \.self) { item in 62 | ZStack { 63 | Button { 64 | withAnimation(.easeOut(duration: 0.2)) { 65 | selectedItem = item 66 | showExplanation = true 67 | } 68 | } label: { 69 | Image(item.rawValue) 70 | .resizable() 71 | .aspectRatio(contentMode: .fit) 72 | .frame(width: 80, height: 80) 73 | } 74 | .opacity(collectedItems.contains(item) ? 1 : 0) 75 | Image(item.rawValue) 76 | .resizable() 77 | .aspectRatio(contentMode: .fit) 78 | .frame(width: 80, height: 80) 79 | .opacity(0.2) 80 | } 81 | } 82 | } 83 | } 84 | .padding(32) 85 | .background( 86 | RoundedRectangle(cornerRadius: 32) 87 | .fill(Color.accentColor.opacity(0.2)) 88 | ) 89 | } 90 | HStack { 91 | Spacer(minLength: 200) 92 | VStack(spacing: 32) { 93 | Image(selectedItem.name) 94 | .resizable() 95 | .aspectRatio(contentMode: .fit) 96 | .frame(width: 200, height: 200) 97 | Text(selectedItem.displayName) 98 | .font(.title) 99 | .bold() 100 | Text(selectedItem.description) 101 | .font(.title2) 102 | } 103 | Spacer(minLength: 200) 104 | } 105 | .frame(maxWidth: .infinity, maxHeight: .infinity) 106 | .background( 107 | VisualEffectView(effect: UIBlurEffect(style: .regular)) 108 | .ignoresSafeArea() 109 | ) 110 | .onTapGesture { 111 | withAnimation(.easeOut(duration: 0.2)) { 112 | showExplanation = false 113 | } 114 | } 115 | .opacity(showExplanation ? 1 : 0) 116 | } 117 | .navigationBarBackButtonHidden(true) 118 | .preferredColorScheme(.light) 119 | } 120 | } 121 | 122 | struct ResultView_Previews: PreviewProvider { 123 | static var previews: some View { 124 | ResultView(collectedItems: [.magnet, .astronomy]) 125 | } 126 | } 127 | 128 | 129 | struct NavigationUtil { 130 | static func popToRootView() { 131 | DispatchQueue.main.asyncAfter(deadline: .now()) { 132 | findNavigationController(viewController: 133 | UIApplication.shared.windows.filter { $0.isKeyWindow 134 | }.first?.rootViewController)? 135 | .popToRootViewController(animated: true) 136 | } 137 | } 138 | static func findNavigationController(viewController: UIViewController?) 139 | -> UINavigationController? { 140 | guard let viewController = viewController else { 141 | return nil 142 | } 143 | if let navigationController = viewController as? UINavigationController 144 | { 145 | return navigationController 146 | } 147 | for childViewController in viewController.children { 148 | return findNavigationController(viewController: 149 | childViewController) 150 | } 151 | return nil 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/RotationModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RotationModifier.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/19. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DeviceRotationViewModifier: ViewModifier { 11 | let action: (Bool) -> Void 12 | 13 | func body(content: Content) -> some View { 14 | content 15 | .onAppear() 16 | .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in 17 | action((UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape)!) 18 | } 19 | } 20 | } 21 | 22 | // A View wrapper to make the modifier easier to use 23 | extension View { 24 | func onRotate(perform action: @escaping (Bool) -> Void) -> some View { 25 | self.modifier(DeviceRotationViewModifier(action: action)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/SKNodeExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SCNNodeExtension.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/14. 6 | // 7 | 8 | import SpriteKit 9 | 10 | extension SKNode { 11 | func setup(with nodeType: NodeType) { 12 | if nodeType != .item { 13 | name = nodeType.name 14 | } 15 | physicsBody?.categoryBitMask = nodeType.categoryBitMask 16 | physicsBody?.collisionBitMask = nodeType.collisionBitMask 17 | physicsBody?.contactTestBitMask = nodeType.contactTestBitMask 18 | physicsBody?.restitution = nodeType.restitution 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/Stage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stage.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/17. 6 | // 7 | 8 | import SpriteKit 9 | 10 | enum Stage: String, CaseIterable { 11 | case opening 12 | case stage1 13 | case stage2 14 | case stage3 15 | case stage4 16 | 17 | func scene(screenFrame: CGRect) -> SKScene { 18 | SKScene(fileNamed: rawValue + (max(screenFrame.width, screenFrame.height) == 2732 ? "_12_9" : "_11") )! 19 | } 20 | 21 | var next: Stage? { 22 | switch self { 23 | case .opening: 24 | return .stage1 25 | case .stage1: 26 | return .stage2 27 | case .stage2: 28 | return .stage3 29 | case .stage3: 30 | return .stage4 31 | case .stage4: 32 | return nil 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/TopView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopView.swift 3 | // SketchMaze 4 | // 5 | // Created by Kyoya Yamaguchi on 2023/04/18. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TopView: View { 11 | 12 | @State var isRightHanded = true 13 | @State var orientation = UIDeviceOrientation.unknown 14 | 15 | var body: some View { 16 | NavigationView { 17 | ZStack { 18 | Image("note") 19 | .resizable() 20 | .aspectRatio(contentMode: .fill) 21 | .ignoresSafeArea() 22 | VStack { 23 | Text("Welcome to Newton's Notebook!") 24 | .font(.largeTitle) 25 | .bold() 26 | Image("basket") 27 | .resizable() 28 | .aspectRatio(contentMode: .fit) 29 | .frame(width: 200, height: 200) 30 | Spacer() 31 | .frame(maxHeight: 50) 32 | NavigationLink(destination: ContentView(viewModel: .init(isRightHanded: isRightHanded))) { 33 | Text("Play") 34 | .font(.title) 35 | .foregroundColor(.white) 36 | .padding(EdgeInsets(top: 16, leading: 32, bottom: 16, trailing: 32)) 37 | .background(Color.accentColor) 38 | .cornerRadius(10) 39 | } 40 | Spacer() 41 | .frame(maxHeight: 32) 42 | Text("Which is your dominant hand?") 43 | .font(.title) 44 | Picker("", selection: $isRightHanded) { 45 | Text("Left") 46 | .tag(false) 47 | Text("Right") 48 | .tag(true) 49 | } 50 | .pickerStyle(.segmented) 51 | .frame(maxWidth: 200) 52 | } 53 | VStack { 54 | Text("Play this game in landscape mode") 55 | .font(.largeTitle) 56 | Image(systemName: "rotate.right.fill") 57 | .resizable() 58 | .aspectRatio(contentMode: .fit) 59 | .frame(width: 200, height: 200) 60 | } 61 | .frame(maxWidth: .infinity, maxHeight: .infinity) 62 | .background { 63 | VisualEffectView(effect: UIBlurEffect(style: .regular)) 64 | .ignoresSafeArea() 65 | } 66 | .opacity(orientation == .landscapeLeft || orientation == .landscapeRight ? 0 : 1) 67 | } 68 | } 69 | .navigationViewStyle(.stack) 70 | .onAppear { 71 | orientation = (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape)! ? .landscapeLeft : .portrait 72 | print(UIScreen.main.bounds) 73 | } 74 | .onRotate { isLandscape in 75 | orientation = isLandscape ? .landscapeLeft : .portrait 76 | } 77 | .navigationBarBackButtonHidden(true) 78 | .preferredColorScheme(.light) 79 | } 80 | } 81 | 82 | struct TopView_Previews: PreviewProvider { 83 | static var previews: some View { 84 | TopView() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Newton's Notebook.swiftpm/VisualEffectView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VisualEffectView.swift 3 | // dragonx 4 | // 5 | // Created by Masakaz Ozaki on 2022/09/15. 6 | // 7 | 8 | import SwiftUI 9 | import UIKit 10 | 11 | struct VisualEffectView: UIViewRepresentable { 12 | var effect: UIVisualEffect? 13 | func makeUIView(context: UIViewRepresentableContext) -> UIVisualEffectView { 14 | UIVisualEffectView() 15 | } 16 | func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext) { 17 | uiView.effect = effect 18 | } 19 | } 20 | 21 | struct VisualEffectView_Previews: PreviewProvider { 22 | static var previews: some View { 23 | VisualEffectView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Newton's Notebook 2 | 3 | Swift Student Challenge 2023 application (Accepted!) 4 | 5 | 6 | https://github.com/kyoya1123/Newtons-Notebook/assets/19247234/e7db503d-b5bd-45c5-8733-408a89a0b17e 7 | 8 | --------------------------------------------------------------------------------