Repository: windstormeye/SwiftGame Branch: master Commit: d2f4eefc9e28 Files: 114 Total size: 179.6 KB Directory structure: gitextract_jbiqqmeb/ ├── LICENSE ├── README.md ├── bounceGame/ │ ├── bounceGame/ │ │ ├── Actions.sks │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Ball/ │ │ │ ├── Ball.swift │ │ │ ├── Box.swift │ │ │ └── Label.swift │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Define.swift │ │ ├── Extension.swift │ │ ├── GameScene.sks │ │ ├── GameScene.swift │ │ ├── GameViewController.swift │ │ └── Info.plist │ └── bounceGame.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata/ │ │ └── pjhubs.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ └── pjhubs.xcuserdatad/ │ ├── xcdebugger/ │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes/ │ └── xcschememanagement.plist ├── liGame/ │ ├── liGame/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── 01.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon-1.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Line0.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── Line1.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── Line2.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── Line3.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_1.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_10.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_11.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_12.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_13.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_14.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_15.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_16.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_17.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_18.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_2.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_3.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_4.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_5.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_6.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_7.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_8.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── created_9.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── finalMan.imageset/ │ │ │ │ └── Contents.json │ │ │ └── finalManContent.imageset/ │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Extension/ │ │ │ ├── Animation.swift │ │ │ ├── Define.swift │ │ │ ├── UIColor+Extension.swift │ │ │ ├── UIImage+Extension.swift │ │ │ └── UIView+Extension.swift │ │ ├── Info.plist │ │ ├── View/ │ │ │ ├── Bottom/ │ │ │ │ ├── LiBottomCollectionView.swift │ │ │ │ ├── LiBottomCollectionViewCell.swift │ │ │ │ └── LiBottomView.swift │ │ │ └── Puzzle.swift │ │ └── ViewController.swift │ └── liGame.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata/ │ │ ├── pjhubs.xcuserdatad/ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── wengpeijun.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ ├── pjhubs.xcuserdatad/ │ │ ├── xcdebugger/ │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes/ │ │ └── xcschememanagement.plist │ └── wengpeijun.xcuserdatad/ │ ├── xcdebugger/ │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes/ │ └── xcschememanagement.plist └── lightGame/ ├── light-game/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj/ │ │ └── LaunchScreen.storyboard │ ├── ContentView.swift │ ├── Info.plist │ ├── Model/ │ │ ├── GameController.swift │ │ ├── GameManager.swift │ │ └── Light.swift │ ├── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ └── SceneDelegate.swift ├── light-game.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata/ │ │ ├── pjhubs.xcuserdatad/ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── wengpeijun.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ ├── pjhubs.xcuserdatad/ │ │ ├── xcdebugger/ │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes/ │ │ └── xcschememanagement.plist │ └── wengpeijun.xcuserdatad/ │ ├── xcdebugger/ │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes/ │ └── xcschememanagement.plist ├── watchapp/ │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj/ │ │ └── Interface.storyboard │ └── Info.plist └── watchapp Extension/ ├── Assets.xcassets/ │ ├── Complication.complicationset/ │ │ ├── Circular.imageset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Extra Large.imageset/ │ │ │ └── Contents.json │ │ ├── Graphic Bezel.imageset/ │ │ │ └── Contents.json │ │ ├── Graphic Circular.imageset/ │ │ │ └── Contents.json │ │ ├── Graphic Corner.imageset/ │ │ │ └── Contents.json │ │ ├── Graphic Large Rectangular.imageset/ │ │ │ └── Contents.json │ │ ├── Modular.imageset/ │ │ │ └── Contents.json │ │ └── Utilitarian.imageset/ │ │ └── Contents.json │ └── Contents.json ├── ExtensionDelegate.swift ├── HostingController.swift ├── Info.plist ├── Preview Content/ │ └── Preview Assets.xcassets/ │ └── Contents.json ├── WatchContentView.swift └── WatchGameManager.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 PJHubs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # 《Swift 游戏开发》 这里汇集了我的小专栏《Swift 游戏开发》的全部代码,小专栏地址:https://xiaozhuanlan.com/pjhubs-swift-game ![](https://images.xiaozhuanlan.com/photo/2019/4305214109ad56a7bbc8ed9e66c2bbf6.png) [Swift 游戏开发序言](https://xiaozhuanlan.com/topic/9704163285) ![](https://images.xiaozhuanlan.com/photo/2019/22c830968d31df2249696119aa63d2db.png) [Swift 游戏开发之能否关个灯(〇)](https://xiaozhuanlan.com/topic/9806127435) [Swift 游戏开发之能否关个灯(一)](https://xiaozhuanlan.com/topic/2564017938) [Swift 游戏开发之能否关个灯(二)](https://xiaozhuanlan.com/topic/8654723190) ![](https://images.xiaozhuanlan.com/photo/2019/a0dd4d112c81e08d47c1822a7a9888d0.png) [Swift 游戏开发阶段总结(〇)](https://xiaozhuanlan.com/topic/7942150836) ![](https://images.xiaozhuanlan.com/photo/2019/d8805030d828cae0c691121d3d3c5ccb.png) [Swift 游戏开发之黎锦拼图(〇)](https://xiaozhuanlan.com/topic/8079642315) [Swift 游戏开发之黎锦拼图(一)](https://xiaozhuanlan.com/topic/0312569847) [Swift 游戏开发之黎锦拼图(二)](https://xiaozhuanlan.com/topic/9738401526) [Swift 游戏开发之黎锦拼图(三)](https://xiaozhuanlan.com/topic/3968275140) [Swift 游戏开发之黎锦拼图(四)](https://xiaozhuanlan.com/topic/7436519820) ![](https://images.xiaozhuanlan.com/photo/2019/ac56d26bd4577bcdd242e2fe90141d6d.png) [Swift 游戏开发阶段总结(一)](https://xiaozhuanlan.com/topic/8109647352) ![](https://images.xiaozhuanlan.com/photo/2019/e4fb08d3c028c97f5da6e62a7a08a6cd.png) [Swift 游戏开发之方块弹珠(〇))](https://xiaozhuanlan.com/topic/1758926340) ================================================ FILE: bounceGame/bounceGame/AppDelegate.swift ================================================ // // AppDelegate.swift // bounceGame // // Created by 翁培钧 on 2019/12/22. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } } ================================================ FILE: bounceGame/bounceGame/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: bounceGame/bounceGame/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: bounceGame/bounceGame/Ball/Ball.swift ================================================ // // Ball.swift // bounceGame // // Created by 翁培钧 on 2019/12/29. // Copyright © 2019 翁培钧. All rights reserved. // import SpriteKit class Ball: SKShapeNode { var isShot = false override init() { super.init() initObject() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func initObject() { self.fillColor = .red self.physicsBody = SKPhysicsBody(circleOfRadius: 10) self.physicsBody?.categoryBitMask = BitMask.Ball self.physicsBody?.contactTestBitMask = BitMask.Box | BitMask.Ground self.physicsBody?.collisionBitMask = BitMask.Box self.physicsBody?.usesPreciseCollisionDetection = true; self.physicsBody?.linearDamping = 0 self.physicsBody?.restitution = 1.0 } } ================================================ FILE: bounceGame/bounceGame/Ball/Box.swift ================================================ // // Box.swift // bounceGame // // Created by 翁培钧 on 2020/1/7. // Copyright © 2020 翁培钧. All rights reserved. // import SpriteKit class Box: SKShapeNode { } ================================================ FILE: bounceGame/bounceGame/Ball/Label.swift ================================================ // // Label.swift // bounceGame // // Created by 翁培钧 on 2020/1/8. // Copyright © 2020 翁培钧. All rights reserved. // import SpriteKit class Label: SKLabelNode { var typoTag = 0 } ================================================ FILE: bounceGame/bounceGame/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: bounceGame/bounceGame/Base.lproj/Main.storyboard ================================================ ================================================ FILE: bounceGame/bounceGame/Define.swift ================================================ // // Define.swift // bounceGame // // Created by 翁培钧 on 2019/12/30. // Copyright © 2019 翁培钧. All rights reserved. // import Foundation struct BitMask { static let Ball = UInt32(0x00001) static let Box = UInt32(0x00002) static let Wall = UInt32(0x00003) static let Ground = UInt32(0x00004) } ================================================ FILE: bounceGame/bounceGame/Extension.swift ================================================ // // Extension.swift // bounceGame // // Created by 翁培钧 on 2020/1/8. // Copyright © 2020 翁培钧. All rights reserved. // import SpriteKit ================================================ FILE: bounceGame/bounceGame/GameScene.swift ================================================ // // GameScene.swift // bounceGame // // Created by 翁培钧 on 2019/12/22. // Copyright © 2019 翁培钧. All rights reserved. // import SpriteKit class GameScene: SKScene { private var balls = [Ball]() private var contactGroundBalls = [Ball]() private var isBegin = false private var ground = SKSpriteNode() private var firstDownBall: Ball? override init(size: CGSize) { super.init(size: size) physicsWorld.gravity = CGVector(dx: 0, dy: -1) physicsWorld.contactDelegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } var contentCreated = false override func didMove(to view: SKView) { if !contentCreated { createContent() contentCreated = true } } private func createContent() { ground = SKSpriteNode(color: .gray, size: CGSize(width: size.width, height: 120)) ground.position = CGPoint(x: size.width / 2, y: ground.size.height / 2) addChild(ground) ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size) ground.physicsBody?.isDynamic = false ground.physicsBody?.collisionBitMask = BitMask.Ball ground.physicsBody?.categoryBitMask = BitMask.Ground ground.physicsBody?.contactTestBitMask = BitMask.Ball let wall = SKNode() wall.position = CGPoint(x: 0, y: 0) wall.physicsBody?.friction = 0 wall.physicsBody?.isDynamic = false wall.physicsBody?.restitution = 1.0 wall.physicsBody?.collisionBitMask = BitMask.Wall wall.physicsBody?.categoryBitMask = BitMask.Wall wall.physicsBody?.contactTestBitMask = BitMask.Wall wall.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: 0, y: 0, width: size.width, height: size.height)) addChild(wall) for _ in 0..<5 { let ball = Ball(circleOfRadius: 10) balls.append(ball) addChild(ball) ball.physicsBody?.isDynamic = false ball.position = CGPoint(x: size.width / 2, y: ground.frame.size.height + ball.frame.size.height / 2) } for row in 1...5 { let box = Box(rectOf: CGSize(width: 50, height: 50)) box.position = CGPoint(x: 50 + (row * 50 + 20), y: (800 - row * 50 + 20)) box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50)) box.physicsBody?.categoryBitMask = BitMask.Box box.physicsBody?.contactTestBitMask = BitMask.Ball box.physicsBody?.collisionBitMask = BitMask.Box box.physicsBody?.linearDamping = 0 box.physicsBody?.restitution = 1.0 box.physicsBody?.isDynamic = false box.fillColor = .red let label = Label(text: "\(row)") label.fontSize = 22 label.typoTag = 666 label.fontName = "Arial-BoldMT" label.color = .white label.position = CGPoint(x: 0, y: -label.frame.size.height / 2) box.addChild(label) addChild(box) } } } extension GameScene { private func shot() { for (index, ball) in balls.enumerated() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) { ball.physicsBody?.isDynamic = true if (!self.children.contains(ball)) { self.addChild(ball) } ball.physicsBody?.applyImpulse(CGVector(dx: 10, dy: 10)) ball.physicsBody?.applyTorque(1.8) // ball.physicsBody?.applyForce(CGVector(dx: 400 + CGFloat(index) * 0.1, dy: 800)) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { ball.isShot = true if (index == self.balls.count - 1) { self.firstDownBall?.removeFromParent() self.firstDownBall = nil } } } } } } extension GameScene { override func touchesEnded(_ touches: Set, with event: UIEvent?) { shot() } } extension GameScene: SKPhysicsContactDelegate { func didBegin(_ contact: SKPhysicsContact) { print("\(contact.bodyA.contactTestBitMask) == \(contact.bodyB.contactTestBitMask)") switch contact.bodyA.categoryBitMask { case BitMask.Box: checkNodeIsBox(contact.bodyA.node) checkNodeIsWall(contact.bodyB.node) case BitMask.Wall: checkNodeIsWall(contact.bodyB.node) case BitMask.Ground: checkNodeIsGround(contact.bodyB.node) default: break } switch contact.bodyB.categoryBitMask { case BitMask.Box: checkNodeIsBox(contact.bodyB.node) checkNodeIsWall(contact.bodyA.node) case BitMask.Wall: checkNodeIsWall(contact.bodyA.node) case BitMask.Ground: checkNodeIsGround(contact.bodyA.node) default: break } } } extension GameScene { private func checkNodeIsBox(_ node: SKNode?) { guard let box = node else { return } if box.physicsBody?.categoryBitMask == BitMask.Box { let label = box.children.first! as! Label var tag = Int(label.text!)! if (tag > 1) { tag -= 1 label.text = "\(tag)" } else { box.removeFromParent() } } } private func checkNodeIsWall(_ node: SKNode?) { guard let ball = node as? Ball else { return } ball.isShot = true } private func checkNodeIsGround(_ node: SKNode?) { guard let ball = node as? Ball else { return } // NOTE: 小球 & 发射出去 if (ball.physicsBody?.categoryBitMask == BitMask.Ball && ball.isShot) { ball.removeFromParent(); // NOTE: 定位小球不存在 || 当前 Scene 中不包含定位小球 if (firstDownBall == nil || !children.contains(firstDownBall!)) { firstDownBall = Ball(circleOfRadius: 10) firstDownBall!.position = CGPoint(x: ball.position.x, y: ground.frame.size.height + ball.frame.size.height / 2 - 2) addChild(firstDownBall!) // NOTE: 静止 firstDownBall!.physicsBody?.isDynamic = false } // NOTE: 统一重设后续触底小球位置。二次发射时,直接读取各个小球的初始位置进行发射 ball.position = CGPoint(x: firstDownBall!.position.x, y: ground.frame.size.height + ball.frame.size.height / 2) } } } ================================================ FILE: bounceGame/bounceGame/GameViewController.swift ================================================ // // GameViewController.swift // bounceGame // // Created by 翁培钧 on 2019/12/22. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit import SpriteKit class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() if let view = self.view as! SKView? { let scene = GameScene(size: view.frame.size) scene.scaleMode = .aspectFill view.presentScene(scene) view.ignoresSiblingOrder = true view.showsFPS = true view.showsNodeCount = true view.showsPhysics = true } } override var prefersStatusBarHidden: Bool { return true } } ================================================ FILE: bounceGame/bounceGame/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UIStatusBarHidden UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: bounceGame/bounceGame.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 0C8BB82F23C4C84B00F88BD6 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8BB82E23C4C84B00F88BD6 /* Box.swift */; }; 0C964EBD23B8E5D0001EA659 /* Ball.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C964EBC23B8E5D0001EA659 /* Ball.swift */; }; 0C964EBF23BA3FDF001EA659 /* Define.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C964EBE23BA3FDF001EA659 /* Define.swift */; }; 0CA02F8523AFAB7D004A580E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA02F8423AFAB7D004A580E /* AppDelegate.swift */; }; 0CA02F8723AFAB7D004A580E /* GameScene.sks in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F8623AFAB7D004A580E /* GameScene.sks */; }; 0CA02F8923AFAB7D004A580E /* Actions.sks in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F8823AFAB7D004A580E /* Actions.sks */; }; 0CA02F8B23AFAB7D004A580E /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA02F8A23AFAB7D004A580E /* GameScene.swift */; }; 0CA02F8D23AFAB7D004A580E /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA02F8C23AFAB7D004A580E /* GameViewController.swift */; }; 0CA02F9023AFAB7D004A580E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F8E23AFAB7D004A580E /* Main.storyboard */; }; 0CA02F9223AFAB7E004A580E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F9123AFAB7E004A580E /* Assets.xcassets */; }; 0CA02F9523AFAB7E004A580E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F9323AFAB7E004A580E /* LaunchScreen.storyboard */; }; 0CFFFBBE23C629AD00BB3F13 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFFFBBD23C629AD00BB3F13 /* Label.swift */; }; 0CFFFBC223C62AD300BB3F13 /* Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFFFBC123C62AD300BB3F13 /* Extension.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0C8BB82E23C4C84B00F88BD6 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; 0C964EBC23B8E5D0001EA659 /* Ball.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ball.swift; sourceTree = ""; }; 0C964EBE23BA3FDF001EA659 /* Define.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Define.swift; sourceTree = ""; }; 0CA02F8123AFAB7D004A580E /* bounceGame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bounceGame.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0CA02F8423AFAB7D004A580E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0CA02F8623AFAB7D004A580E /* GameScene.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = GameScene.sks; sourceTree = ""; }; 0CA02F8823AFAB7D004A580E /* Actions.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = Actions.sks; sourceTree = ""; }; 0CA02F8A23AFAB7D004A580E /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScene.swift; sourceTree = ""; }; 0CA02F8C23AFAB7D004A580E /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 0CA02F8F23AFAB7D004A580E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 0CA02F9123AFAB7E004A580E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 0CA02F9423AFAB7E004A580E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 0CA02F9623AFAB7E004A580E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0CFFFBBD23C629AD00BB3F13 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 0CFFFBC123C62AD300BB3F13 /* Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extension.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 0CA02F7E23AFAB7D004A580E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0C964EBB23B8E5A6001EA659 /* Ball */ = { isa = PBXGroup; children = ( 0C964EBC23B8E5D0001EA659 /* Ball.swift */, 0C8BB82E23C4C84B00F88BD6 /* Box.swift */, 0CFFFBBD23C629AD00BB3F13 /* Label.swift */, ); path = Ball; sourceTree = ""; }; 0CA02F7823AFAB7D004A580E = { isa = PBXGroup; children = ( 0CA02F8323AFAB7D004A580E /* bounceGame */, 0CA02F8223AFAB7D004A580E /* Products */, ); sourceTree = ""; }; 0CA02F8223AFAB7D004A580E /* Products */ = { isa = PBXGroup; children = ( 0CA02F8123AFAB7D004A580E /* bounceGame.app */, ); name = Products; sourceTree = ""; }; 0CA02F8323AFAB7D004A580E /* bounceGame */ = { isa = PBXGroup; children = ( 0CA02F8423AFAB7D004A580E /* AppDelegate.swift */, 0CA02F8623AFAB7D004A580E /* GameScene.sks */, 0CA02F8823AFAB7D004A580E /* Actions.sks */, 0CA02F8A23AFAB7D004A580E /* GameScene.swift */, 0CA02F8C23AFAB7D004A580E /* GameViewController.swift */, 0C964EBE23BA3FDF001EA659 /* Define.swift */, 0CFFFBC123C62AD300BB3F13 /* Extension.swift */, 0C964EBB23B8E5A6001EA659 /* Ball */, 0CA02F8E23AFAB7D004A580E /* Main.storyboard */, 0CA02F9123AFAB7E004A580E /* Assets.xcassets */, 0CA02F9323AFAB7E004A580E /* LaunchScreen.storyboard */, 0CA02F9623AFAB7E004A580E /* Info.plist */, ); path = bounceGame; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 0CA02F8023AFAB7D004A580E /* bounceGame */ = { isa = PBXNativeTarget; buildConfigurationList = 0CA02F9923AFAB7E004A580E /* Build configuration list for PBXNativeTarget "bounceGame" */; buildPhases = ( 0CA02F7D23AFAB7D004A580E /* Sources */, 0CA02F7E23AFAB7D004A580E /* Frameworks */, 0CA02F7F23AFAB7D004A580E /* Resources */, ); buildRules = ( ); dependencies = ( ); name = bounceGame; productName = bounceGame; productReference = 0CA02F8123AFAB7D004A580E /* bounceGame.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0CA02F7923AFAB7D004A580E /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1120; LastUpgradeCheck = 1120; ORGANIZATIONNAME = "翁培钧"; TargetAttributes = { 0CA02F8023AFAB7D004A580E = { CreatedOnToolsVersion = 11.2.1; }; }; }; buildConfigurationList = 0CA02F7C23AFAB7D004A580E /* Build configuration list for PBXProject "bounceGame" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 0CA02F7823AFAB7D004A580E; productRefGroup = 0CA02F8223AFAB7D004A580E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 0CA02F8023AFAB7D004A580E /* bounceGame */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 0CA02F7F23AFAB7D004A580E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 0CA02F9023AFAB7D004A580E /* Main.storyboard in Resources */, 0CA02F8723AFAB7D004A580E /* GameScene.sks in Resources */, 0CA02F9223AFAB7E004A580E /* Assets.xcassets in Resources */, 0CA02F9523AFAB7E004A580E /* LaunchScreen.storyboard in Resources */, 0CA02F8923AFAB7D004A580E /* Actions.sks in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 0CA02F7D23AFAB7D004A580E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0C964EBD23B8E5D0001EA659 /* Ball.swift in Sources */, 0CA02F8B23AFAB7D004A580E /* GameScene.swift in Sources */, 0CA02F8D23AFAB7D004A580E /* GameViewController.swift in Sources */, 0C8BB82F23C4C84B00F88BD6 /* Box.swift in Sources */, 0C964EBF23BA3FDF001EA659 /* Define.swift in Sources */, 0CFFFBBE23C629AD00BB3F13 /* Label.swift in Sources */, 0CA02F8523AFAB7D004A580E /* AppDelegate.swift in Sources */, 0CFFFBC223C62AD300BB3F13 /* Extension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 0CA02F8E23AFAB7D004A580E /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 0CA02F8F23AFAB7D004A580E /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 0CA02F9323AFAB7E004A580E /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 0CA02F9423AFAB7E004A580E /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 0CA02F9723AFAB7E004A580E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 0CA02F9823AFAB7E004A580E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 0CA02F9A23AFAB7E004A580E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = U6J5965DZK; INFOPLIST_FILE = bounceGame/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.bounceGame; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 0CA02F9B23AFAB7E004A580E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = U6J5965DZK; INFOPLIST_FILE = bounceGame/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.bounceGame; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 0CA02F7C23AFAB7D004A580E /* Build configuration list for PBXProject "bounceGame" */ = { isa = XCConfigurationList; buildConfigurations = ( 0CA02F9723AFAB7E004A580E /* Debug */, 0CA02F9823AFAB7E004A580E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 0CA02F9923AFAB7E004A580E /* Build configuration list for PBXNativeTarget "bounceGame" */ = { isa = XCConfigurationList; buildConfigurations = ( 0CA02F9A23AFAB7E004A580E /* Debug */, 0CA02F9B23AFAB7E004A580E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 0CA02F7923AFAB7D004A580E /* Project object */; } ================================================ FILE: bounceGame/bounceGame.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: bounceGame/bounceGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: bounceGame/bounceGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: bounceGame/bounceGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState bounceGame.xcscheme_^#shared#^_ orderHint 0 ================================================ FILE: liGame/liGame/AppDelegate.swift ================================================ // // AppDelegate.swift // liGame // // Created by 翁培钧 on 2019/9/4. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } } ================================================ FILE: liGame/liGame/Assets.xcassets/01.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "01.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/AppIcon-1.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/Line0.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "Line0@2x.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/Line1.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "Line1@2x.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/Line2.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "Line2@2x.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/Line3.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "Line3@2x.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_1.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_1.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_10.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_10.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_11.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_11.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_12.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_12.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_13.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_13.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_14.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_14.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_15.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_15.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_16.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_16.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_17.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_1.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_18.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_3.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_2.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_2.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_3.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_3.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_4.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_4.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_5.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_5.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_6.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_6.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_7.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_7.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_8.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_8.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/created_9.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "created_9.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/finalMan.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "finalMan.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Assets.xcassets/finalManContent.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "finalMamContent.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: liGame/liGame/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: liGame/liGame/Base.lproj/Main.storyboard ================================================ ================================================ FILE: liGame/liGame/Extension/Animation.swift ================================================ // // Animation.swift // WWDC19 // // Created by PJHubs on 2019/3/20. // Copyright © 2019 PJHubs. All rights reserved. // import UIKit protocol PJParticleAnimationable {} extension PJParticleAnimationable where Self: UIViewController { func startParticleAnimation(_ point : CGPoint) { let emitter = CAEmitterLayer() emitter.emitterPosition = point emitter.preservesDepth = true var cells = [CAEmitterCell]() for i in 0..<20 { let cell = CAEmitterCell() cell.velocity = 150 cell.velocityRange = 100 cell.scale = 0.7 cell.scaleRange = 0.3 cell.emissionLongitude = CGFloat(-Double.pi / 2) cell.emissionRange = CGFloat(Double.pi / 2) cell.lifetime = 3 cell.lifetimeRange = 1.5 cell.spin = CGFloat(Double.pi / 2) cell.spinRange = CGFloat(Double.pi / 2 / 2) cell.birthRate = 2 cell.contents = UIImage(named: "Line\(i)")?.cgImage cells.append(cell) } emitter.emitterCells = cells view.layer.addSublayer(emitter) } func stopParticleAnimation() { view.layer.sublayers?.filter({ $0.isKind(of: CAEmitterLayer.self)}).first?.removeFromSuperlayer() } } ================================================ FILE: liGame/liGame/Extension/Define.swift ================================================ // // Define.swift // liGame // // Created by 翁培钧 on 2019/9/4. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit /// 屏幕宽 let screenWidth = UIScreen.main.bounds.size.width /// 屏幕高 let screenHeight = UIScreen.main.bounds.size.height /// 底部安全距离 let bottomSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0.0 ///顶部的安全距离 let topSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0.0 /// 状态栏高度 let statusBarHeight = UIApplication.shared.statusBarFrame.height; /// 导航栏高度 let navigationBarHeight = CGFloat(44 + topSafeAreaHeight) ================================================ FILE: liGame/liGame/Extension/UIColor+Extension.swift ================================================ // // UIColor+Extension.swift // liGame // // Created by 翁培钧 on 2019/9/4. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit extension UIColor { class func rgb(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> UIColor { return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: 1) } class func rgba(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat, _ a: CGFloat) -> UIColor { return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: a) } static var bgColor: UIColor { return rgb(29, 36, 73) } } ================================================ FILE: liGame/liGame/Extension/UIImage+Extension.swift ================================================ // // UIImage+Extension.swift // liGame // // Created by 翁培钧 on 2019/9/6. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit extension UIImage { /// 通过原图获取 rect 大小的图片 func image(with rect: CGRect) -> UIImage { let scale: CGFloat = 2 let x = rect.origin.x * scale let y = rect.origin.y * scale let w = rect.size.width * scale let h = rect.size.height * scale let finalRect = CGRect(x: x, y: y, width: w, height: h) let originImageRef = self.cgImage let finanImageRef = originImageRef!.cropping(to: finalRect) let finanImage = UIImage(cgImage: finanImageRef!, scale: scale, orientation: .up) return finanImage } } ================================================ FILE: liGame/liGame/Extension/UIView+Extension.swift ================================================ // // UIView+Extension.swift // liGame // // Created by 翁培钧 on 2019/9/4. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit extension UIView { static private let PJSCREEN_SCALE = UIScreen.main.scale private func getPixintegral(pointValue: CGFloat) -> CGFloat { return round(pointValue * UIView.PJSCREEN_SCALE) / UIView.PJSCREEN_SCALE } public var x: CGFloat { get { return self.frame.origin.x } set(x) { self.frame = CGRect.init( x: getPixintegral(pointValue: x), y: self.y, width: self.width, height: self.height ) } } public var y: CGFloat { get { return self.frame.origin.y } set(y) { self.frame = CGRect.init( x: self.x, y: getPixintegral(pointValue: y), width: self.width, height: self.height ) } } public var width: CGFloat { get { return self.frame.size.width } set(width) { self.frame = CGRect.init( x: self.x, y: self.y, width: getPixintegral(pointValue: width), height: self.height ) } } public var height: CGFloat { get { return self.frame.size.height } set (height) { self.frame = CGRect.init( x: self.x, y: self.y, width: self.width, height: getPixintegral(pointValue: height) ) } } public var bottom: CGFloat { get { return self.y + self.height } set(bottom) { self.y = bottom - self.height } } public var right: CGFloat { get { return self.x + self.width } set (right) { self.x = right - self.width } } public var left: CGFloat { get { return self.x } set(left) { self.x = left } } public var top: CGFloat { get { return self.y } set(top) { self.y = top } } public var centerX: CGFloat { get { return self.center.x } set(centerX) { self.center = CGPoint.init( x: getPixintegral(pointValue: centerX), y: self.center.y ) } } public var centerY: CGFloat { get { return self.center.y } set (centerY) { self.center = CGPoint.init(x: self.center.x, y: getPixintegral(pointValue: centerY)) } } } extension UIView { func insertRoundingCorners() { let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 8.0, height: 8.0)) let pathMaskLayer = CAShapeLayer() pathMaskLayer.frame = bounds pathMaskLayer.path = path.cgPath layer.mask = pathMaskLayer } } ================================================ FILE: liGame/liGame/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: liGame/liGame/View/Bottom/LiBottomCollectionView.swift ================================================ // // LiBottomCollectionView.swift // liGame // // Created by pjhubs on 2019/9/8. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit class LiBottomCollectionView: UICollectionView { var longTapBegan: ((Puzzle, CGPoint) -> ())? var longTapChange: ((CGPoint) -> ())? var longTapEnded: (() -> ())? let cellIdentifier = "PJLineCollectionViewCell" var viewModels = [Puzzle]() override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: layout) initView() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } private func initView() { backgroundColor = .clear showsHorizontalScrollIndicator = false isPagingEnabled = true // isUserInteractionEnabled = true dataSource = self register(LiBottomCollectionViewCell.self, forCellWithReuseIdentifier: "LiBottomCollectionViewCell") } } extension LiBottomCollectionView: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return viewModels.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LiBottomCollectionViewCell", for: indexPath) as! LiBottomCollectionViewCell cell.cellIndex = indexPath.row cell.gameIndex = viewModels[indexPath.row].tag cell.viewModel = viewModels[indexPath.row] cell.longTapBegan = { [weak self] index in guard let self = self else { return } guard self.viewModels.count != 0 else { return } self.longTapBegan?(self.viewModels[index], cell.center) } cell.longTapChange = { self.longTapChange?($0) } cell.longTapEnded = { self.viewModels.remove(at: $0) self.reloadData() self.longTapEnded?() } return cell } } ================================================ FILE: liGame/liGame/View/Bottom/LiBottomCollectionViewCell.swift ================================================ // // LiBottomCollectionViewCell.swift // liGame // // Created by pjhubs on 2019/9/8. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit class LiBottomCollectionViewCell: UICollectionViewCell { var longTapBegan: ((Int) -> ())? var longTapChange: ((CGPoint) -> ())? var longTapEnded: ((Int) -> ())? var cellIndex: Int? var gameIndex: Int? private var img = UIImageView() private var tipLabel = UILabel() var viewModel: Puzzle? { didSet { setViewModel() } } override init(frame: CGRect) { super.init(frame: frame) layer.borderWidth = 1 layer.borderColor = UIColor.darkGray.cgColor layer.shadowColor = UIColor.black.cgColor layer.shadowRadius = 10 layer.shadowOffset = CGSize.zero layer.shadowOpacity = 1 img.contentMode = .scaleAspectFit img.frame = CGRect(x: 0, y: 0, width: width, height: height) addSubview(img) tipLabel = UILabel(frame: CGRect(x: width - 10, y: top - 10, width: 17, height: 17)) tipLabel.font = UIFont.systemFont(ofSize: 11) tipLabel.backgroundColor = UIColor.rgb(80, 80, 80) tipLabel.textColor = .white tipLabel.textAlignment = .center tipLabel.layer.cornerRadius = tipLabel.width / 2 tipLabel.layer.masksToBounds = true addSubview(tipLabel) let longTapGesture = UILongPressGestureRecognizer(target: self, action: .longTap) addGestureRecognizer(longTapGesture) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setViewModel() { img.image = viewModel?.image tipLabel.text = "\(gameIndex!)" } } extension LiBottomCollectionViewCell { @objc fileprivate func longTap(_ longTapGesture: UILongPressGestureRecognizer) { guard let cellIndex = cellIndex else { return } switch longTapGesture.state { case .began: longTapBegan?(cellIndex) case .changed: var translation = longTapGesture.location(in: superview) let itemCount = 5 if cellIndex > itemCount { translation.x = translation.x - CGFloat(cellIndex / itemCount * Int(screenWidth)) } let point = CGPoint(x: translation.x, y: translation.y) longTapChange?(point) case .ended: longTapEnded?(cellIndex) default: break } } } private extension Selector { static let longTap = #selector(LiBottomCollectionViewCell.longTap(_:)) } ================================================ FILE: liGame/liGame/View/Bottom/LiBottomView.swift ================================================ // // LiBottomView.swift // liGame // // Created by pjhubs on 2019/9/8. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit class LiBottomView: UIView { var viewModels = [Puzzle]() { didSet { collectionView!.viewModels = viewModels } } var moveBegin: ((Puzzle) -> Void)? var moveChanged: ((CGPoint) -> Void)? var moveEnd: ((Puzzle) -> Void)? var tempPuzzle: Puzzle? var collectionView: LiBottomCollectionView? var longPressView: UIView? private var rightPoint: CGFloat = 0 private var leftaPoint: CGFloat = 0 private var topPoint: CGFloat = 0 private var bottomPoint: CGFloat = 0 public override init(frame: CGRect) { super.init(frame: frame) } convenience init(frame: CGRect, longPressView: UIView?) { self.init(frame: frame) self.longPressView = self initView() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } private func initView() { backgroundColor = .clear topPoint = topSafeAreaHeight bottomPoint = screenHeight - bottomSafeAreaHeight rightPoint = screenWidth / 2 leftaPoint = 0 let effect = UIBlurEffect(style: .extraLight) let effectView = UIVisualEffectView(effect: effect) effectView.frame = CGRect(x: 0, y: 0, width: width, height: height) addSubview(effectView) insertRoundingCorners() let collectionViewLayout = UICollectionViewFlowLayout() let itemW = 50 let innerW = (screenWidth - 5 * 50) / 5 collectionViewLayout.itemSize = CGSize(width: itemW , height: itemW) collectionViewLayout.minimumLineSpacing = innerW collectionViewLayout.minimumInteritemSpacing = 10 collectionViewLayout.scrollDirection = .horizontal collectionViewLayout.sectionInset = UIEdgeInsets.init(top: 0, left: innerW / 2, bottom: 0, right: innerW / 2) collectionView = LiBottomCollectionView(frame: CGRect(x: 0, y: 0, width: width, height: height), collectionViewLayout: collectionViewLayout) addSubview(collectionView!) collectionView!.longTapBegan = { let center = $1 let tempPuzzle = Puzzle(size: $0.frame.size, isCopy: false) // 补上游戏索引 tag tempPuzzle.tag = $0.tag tempPuzzle.image = $0.image tempPuzzle.center = center tempPuzzle.y += self.top self.tempPuzzle = tempPuzzle self.moveBegin?(tempPuzzle) } collectionView!.longTapChange = { guard let tempPuzzle = self.tempPuzzle else { return } tempPuzzle.center = CGPoint(x: $0.x, y: $0.y + self.top) if tempPuzzle.right > self.rightPoint { tempPuzzle.right = self.rightPoint } if tempPuzzle.left < self.leftaPoint { tempPuzzle.left = self.leftaPoint } if tempPuzzle.top < self.topPoint { tempPuzzle.top = self.topPoint } if tempPuzzle.bottom > self.bottomPoint { tempPuzzle.bottom = self.bottomPoint } self.moveChanged?(tempPuzzle.center) } collectionView!.longTapEnded = { guard let tempPuzzle = self.tempPuzzle else { return } self.moveEnd?(tempPuzzle) } } } extension LiBottomView { @objc fileprivate func pan(_ panGesture: UIPanGestureRecognizer) { let translation = panGesture.translation(in: superview) panGesture.view!.center = CGPoint(x: panGesture.view!.center.x + translation.x, y: panGesture.view!.center.y + translation.y) panGesture.setTranslation(.zero, in: superview) } } private extension Selector { static let pan = #selector(LiBottomView.pan(_:)) } ================================================ FILE: liGame/liGame/View/Puzzle.swift ================================================ // // Puzzle.swift // liGame // // Created by 翁培钧 on 2019/9/6. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit class Puzzle: UIImageView { var panChange: ((CGPoint) -> ())? var panEnded: (() -> ())? var beginMovedPoint = CGPoint() // 当前在 X 轴上的位置 var Xindex: Int? // 当前在 Y 轴上的位置 var Yindex: Int? /// 是否为「拷贝」拼图元素 private var isCopy = false private var rightPoint: CGFloat = 0 private var leftaPoint: CGFloat = 0 private var topPoint: CGFloat = 0 private var bottomPoint: CGFloat = 0 override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } convenience init(size: CGSize, isCopy: Bool) { self.init(frame: CGRect(x: -1000, y: -1000, width: size.width, height: size.height)) self.isCopy = isCopy initView() } // MARK: Init private func initView() { // 全部靠左,copyPuzzle 镜像对称 contentMode = .left if !isCopy { isUserInteractionEnabled = true let panGesture = UIPanGestureRecognizer(target: self, action: .pan) self.addGestureRecognizer(panGesture) } else { transform = CGAffineTransform(scaleX: -1, y: 1) } } /// 更新边界 func updateEdge() { if superview != nil { if !isCopy { topPoint = topSafeAreaHeight bottomPoint = superview!.bottom - bottomSafeAreaHeight rightPoint = superview!.width / 2 leftaPoint = 0 } } else { if superview != nil { topPoint = superview!.top bottomPoint = superview!.bottom rightPoint = superview!.width leftaPoint = superview!.width / 2 } } } /// 移动 `rightPuzzle` func copyPuzzleCenterChange(centerPoint: CGPoint) { if !isCopy { return } center = CGPoint(x: screenWidth - centerPoint.x, y: centerPoint.y) } } extension Puzzle { @objc fileprivate func pan(_ panGesture: UIPanGestureRecognizer) { let translation = panGesture.translation(in: superview) switch panGesture.state { case .began: beginMovedPoint = center layer.borderColor = UIColor.white.cgColor layer.borderWidth = 1 case .changed: if right > rightPoint { right = rightPoint } if left < leftaPoint { left = leftaPoint } if top < topPoint { top = topPoint } if bottom > bottomPoint { bottom = bottomPoint } case .ended: layer.borderWidth = 0 self.panEnded?() default: break } center = CGPoint(x: center.x + translation.x, y: center.y + translation.y) panGesture.setTranslation(.zero, in: superview) panChange?(center) } } private extension Selector { static let pan = #selector(Puzzle.pan(_:)) } ================================================ FILE: liGame/liGame/ViewController.swift ================================================ // // ViewController.swift // liGame // // Created by 翁培钧 on 2019/9/4. // Copyright © 2019 翁培钧. All rights reserved. // import UIKit class ViewController: UIViewController, PJParticleAnimationable { private var lineImageView = UIImageView() private var bottomView = LiBottomView() private var puzzles = [Puzzle]() private var defaultPuzzles = [Puzzle]() private var finalPuzzleTags = [[Int]]() private var leftPuzzles = [Puzzle]() private var rightPuzzles = [Puzzle]() private var tempCopyPuzzle: Puzzle? override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .bgColor self.winLabel.isHidden = true // 底图适配 let contentImage = UIImage(named: "01")! let contentImageScale = view.width / contentImage.size.width let contentImageViewHeight = contentImage.size.height * contentImageScale let contentImageView = UIImageView(frame: CGRect(x: 0, y: topSafeAreaHeight, width: view.width, height: contentImageViewHeight)) contentImageView.image = contentImage let imgView = UIImageView(frame: CGRect(x: view.width / 2, y: topSafeAreaHeight, width: 5, height: view.height - bottomSafeAreaHeight)) view.addSubview(imgView) UIGraphicsBeginImageContext(imgView.frame.size) // 位图上下文绘制区域 imgView.image?.draw(in: imgView.bounds) lineImageView = imgView let context:CGContext = UIGraphicsGetCurrentContext()! context.setLineCap(CGLineCap.square) context.setStrokeColor(UIColor.white.cgColor) context.setLineWidth(3) context.setLineDash(phase: 0, lengths: [10,20]) context.move(to: CGPoint(x: 0, y: 0)) context.addLine(to: CGPoint(x: 0, y: view.height)) context.strokePath() imgView.image = UIGraphicsGetImageFromCurrentImageContext() // 一行六个 let itemHCount = 3 let itemW = Int(view.width / CGFloat(itemHCount * 2)) let itemH = itemW let itemVCount = Int(contentImageView.height / CGFloat(itemW)) finalPuzzleTags = Array(repeating: Array(repeating: -1, count: itemHCount), count: itemVCount) for itemY in 0.. 0 { tempPuzzleXIndex += 1 } var tempPuzzleYIndex = CGFloat(Int(tempPuzzleCenterPoint.y / tempPuzzle.height)) if Int(tempPuzzleCenterPoint.y) % Int(tempPuzzle.height) > 0 { tempPuzzleYIndex += 1 } let Xedge = tempPuzzleXIndex * tempPuzzle.width let Yedge = tempPuzzleYIndex * tempPuzzle.height if tempPuzzleCenterPoint.x < Xedge { tempPuzzleCenterPoint.x = Xedge - tempPuzzle.width / 2 } if tempPuzzleCenterPoint.y < Yedge { // 当为最后一列时,往上移 20 if (Int(tempPuzzleYIndex) == finalPuzzleTags.count) { tempPuzzleCenterPoint.y = Yedge - tempPuzzle.height / 2 - 20 } else { tempPuzzleCenterPoint.y = Yedge - tempPuzzle.height / 2 } } // 超出最下边 if (Int(tempPuzzleYIndex) > self.finalPuzzleTags.count) { tempPuzzle.center = tempPuzzle.beginMovedPoint } // 已经有的不能占据 if (self.finalPuzzleTags[Int(tempPuzzleYIndex - 1)][Int(tempPuzzleXIndex - 1)] == -1) { self.finalPuzzleTags[Int(tempPuzzleYIndex - 1)][Int(tempPuzzleXIndex - 1)] = tempPuzzle.tag if ((tempPuzzle.Xindex != nil) && (tempPuzzle.Yindex != nil)) { self.finalPuzzleTags[tempPuzzle.Xindex!][tempPuzzle.Yindex!] = -1 } tempPuzzle.Xindex = Int(tempPuzzleYIndex - 1) tempPuzzle.Yindex = Int(tempPuzzleXIndex - 1) tempPuzzle.center = tempPuzzleCenterPoint } else { tempPuzzle.center = tempPuzzle.beginMovedPoint } } /// 判赢算法 private func isWin() -> Bool { var winCount = 0 for (Vindex, HTags) in self.finalPuzzleTags.enumerated() { for (Hindex, tag) in HTags.enumerated() { let currentIndex = Vindex * 3 + Hindex if defaultPuzzles.count - 1 >= currentIndex { if defaultPuzzles[currentIndex].tag == tag { winCount += 1 continue } } return false } } if winCount == defaultPuzzles.count { return true } return false } private func winAnimate() { startParticleAnimation(CGPoint(x: screenWidth / 2, y: screenHeight - 10)) UIView.animate(withDuration: 0.25, animations: { self.bottomView.top = screenHeight }) for sv in self.view.subviews { sv.removeFromSuperview() } self.winLabel.isHidden = false let finalManContentView = UIImageView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight - 64)) finalManContentView.image = UIImage(named: "finalManContent") self.view.addSubview(finalManContentView) let finalMan = UIImageView(frame: CGRect(x: 0, y: 0, width: finalManContentView.width * 0.85, height: finalManContentView.width * 0.8 * 0.85)) finalMan.center = self.view.center finalMan.image = UIImage(named: "finalMan") self.view.addSubview(finalMan) UIView.animate(withDuration: 0.5, animations: { finalMan.transform = CGAffineTransform(rotationAngle: 0.25) }) { (finished) in UIView.animate(withDuration: 0.5, animations: { finalMan.transform = CGAffineTransform(rotationAngle: -0.25) }, completion: { (finished) in UIView.animate(withDuration: 0.5, animations: { finalMan.transform = CGAffineTransform(rotationAngle: 0) }) }) } } // MARK: - Lazy lazy var winLabel: UILabel = { let label = UILabel(frame: CGRect(x: 0, y: screenHeight - 64, width: screenWidth, height: 64)) label.centerX = view.centerX label.font = UIFont.systemFont(ofSize: 40, weight: UIFont.Weight.light) label.text = "Dàlì shén" label.textAlignment = .center label.textColor = .white view.addSubview(label) return label }() } ================================================ FILE: liGame/liGame.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 0C7ACB9C2322A97000AA4FA4 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7ACB9B2322A97000AA4FA4 /* UIImage+Extension.swift */; }; 0C99777123200DE600EA0130 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C99777023200DE600EA0130 /* AppDelegate.swift */; }; 0C99777323200DE600EA0130 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C99777223200DE600EA0130 /* ViewController.swift */; }; 0C99777623200DE600EA0130 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C99777423200DE600EA0130 /* Main.storyboard */; }; 0C99777823200DE700EA0130 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C99777723200DE700EA0130 /* Assets.xcassets */; }; 0C99777B23200DE700EA0130 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C99777923200DE700EA0130 /* LaunchScreen.storyboard */; }; 0C9977832320135200EA0130 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9977822320135200EA0130 /* UIView+Extension.swift */; }; 0C997785232013C300EA0130 /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C997784232013C300EA0130 /* UIColor+Extension.swift */; }; 0C997788232014A500EA0130 /* Define.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C997787232014A500EA0130 /* Define.swift */; }; 0CAE10A62321E76900CFECA5 /* Puzzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAE10A52321E76900CFECA5 /* Puzzle.swift */; }; 0CE27C0A2371C4950032CF94 /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE27C092371C4950032CF94 /* Animation.swift */; }; A64F18812325121900F531F4 /* LiBottomCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64F18802325121900F531F4 /* LiBottomCollectionView.swift */; }; A64F18832325126800F531F4 /* LiBottomCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64F18822325126800F531F4 /* LiBottomCollectionViewCell.swift */; }; A64F18852325133E00F531F4 /* LiBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64F18842325133E00F531F4 /* LiBottomView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0C7ACB9B2322A97000AA4FA4 /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = ""; }; 0C99776D23200DE600EA0130 /* liGame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = liGame.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0C99777023200DE600EA0130 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0C99777223200DE600EA0130 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 0C99777523200DE600EA0130 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 0C99777723200DE700EA0130 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 0C99777A23200DE700EA0130 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 0C99777C23200DE700EA0130 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0C9977822320135200EA0130 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 0C997784232013C300EA0130 /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 0C997787232014A500EA0130 /* Define.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Define.swift; sourceTree = ""; }; 0CAE10A52321E76900CFECA5 /* Puzzle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Puzzle.swift; sourceTree = ""; }; 0CE27C092371C4950032CF94 /* Animation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animation.swift; sourceTree = ""; }; A64F18802325121900F531F4 /* LiBottomCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiBottomCollectionView.swift; sourceTree = ""; }; A64F18822325126800F531F4 /* LiBottomCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiBottomCollectionViewCell.swift; sourceTree = ""; }; A64F18842325133E00F531F4 /* LiBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiBottomView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 0C99776A23200DE600EA0130 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0C99776423200DE600EA0130 = { isa = PBXGroup; children = ( 0C99776F23200DE600EA0130 /* liGame */, 0C99776E23200DE600EA0130 /* Products */, ); sourceTree = ""; }; 0C99776E23200DE600EA0130 /* Products */ = { isa = PBXGroup; children = ( 0C99776D23200DE600EA0130 /* liGame.app */, ); name = Products; sourceTree = ""; }; 0C99776F23200DE600EA0130 /* liGame */ = { isa = PBXGroup; children = ( 0C99777023200DE600EA0130 /* AppDelegate.swift */, 0C99777223200DE600EA0130 /* ViewController.swift */, 0C9977892321E70900EA0130 /* View */, 0C9977862320149900EA0130 /* Extension */, 0C99777423200DE600EA0130 /* Main.storyboard */, 0C99777723200DE700EA0130 /* Assets.xcassets */, 0C99777923200DE700EA0130 /* LaunchScreen.storyboard */, 0C99777C23200DE700EA0130 /* Info.plist */, ); path = liGame; sourceTree = ""; }; 0C9977862320149900EA0130 /* Extension */ = { isa = PBXGroup; children = ( 0CE27C092371C4950032CF94 /* Animation.swift */, 0C9977822320135200EA0130 /* UIView+Extension.swift */, 0C997784232013C300EA0130 /* UIColor+Extension.swift */, 0C997787232014A500EA0130 /* Define.swift */, 0C7ACB9B2322A97000AA4FA4 /* UIImage+Extension.swift */, ); path = Extension; sourceTree = ""; }; 0C9977892321E70900EA0130 /* View */ = { isa = PBXGroup; children = ( A64F187F232511F500F531F4 /* Bottom */, 0CAE10A52321E76900CFECA5 /* Puzzle.swift */, ); path = View; sourceTree = ""; }; A64F187F232511F500F531F4 /* Bottom */ = { isa = PBXGroup; children = ( A64F18802325121900F531F4 /* LiBottomCollectionView.swift */, A64F18822325126800F531F4 /* LiBottomCollectionViewCell.swift */, A64F18842325133E00F531F4 /* LiBottomView.swift */, ); path = Bottom; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 0C99776C23200DE600EA0130 /* liGame */ = { isa = PBXNativeTarget; buildConfigurationList = 0C99777F23200DE700EA0130 /* Build configuration list for PBXNativeTarget "liGame" */; buildPhases = ( 0C99776923200DE600EA0130 /* Sources */, 0C99776A23200DE600EA0130 /* Frameworks */, 0C99776B23200DE600EA0130 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = liGame; productName = liGame; productReference = 0C99776D23200DE600EA0130 /* liGame.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0C99776523200DE600EA0130 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1020; LastUpgradeCheck = 1020; ORGANIZATIONNAME = "翁培钧"; TargetAttributes = { 0C99776C23200DE600EA0130 = { CreatedOnToolsVersion = 10.2.1; }; }; }; buildConfigurationList = 0C99776823200DE600EA0130 /* Build configuration list for PBXProject "liGame" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 0C99776423200DE600EA0130; productRefGroup = 0C99776E23200DE600EA0130 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 0C99776C23200DE600EA0130 /* liGame */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 0C99776B23200DE600EA0130 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 0C99777B23200DE700EA0130 /* LaunchScreen.storyboard in Resources */, 0C99777823200DE700EA0130 /* Assets.xcassets in Resources */, 0C99777623200DE600EA0130 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 0C99776923200DE600EA0130 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0CE27C0A2371C4950032CF94 /* Animation.swift in Sources */, A64F18812325121900F531F4 /* LiBottomCollectionView.swift in Sources */, 0C9977832320135200EA0130 /* UIView+Extension.swift in Sources */, 0C7ACB9C2322A97000AA4FA4 /* UIImage+Extension.swift in Sources */, 0C99777323200DE600EA0130 /* ViewController.swift in Sources */, A64F18832325126800F531F4 /* LiBottomCollectionViewCell.swift in Sources */, 0C99777123200DE600EA0130 /* AppDelegate.swift in Sources */, 0CAE10A62321E76900CFECA5 /* Puzzle.swift in Sources */, 0C997788232014A500EA0130 /* Define.swift in Sources */, A64F18852325133E00F531F4 /* LiBottomView.swift in Sources */, 0C997785232013C300EA0130 /* UIColor+Extension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 0C99777423200DE600EA0130 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 0C99777523200DE600EA0130 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 0C99777923200DE700EA0130 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 0C99777A23200DE700EA0130 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 0C99777D23200DE700EA0130 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 0C99777E23200DE700EA0130 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 0C99778023200DE700EA0130 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = U6J5965DZK; INFOPLIST_FILE = liGame/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.liGame; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 0C99778123200DE700EA0130 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = U6J5965DZK; INFOPLIST_FILE = liGame/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.liGame; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 0C99776823200DE600EA0130 /* Build configuration list for PBXProject "liGame" */ = { isa = XCConfigurationList; buildConfigurations = ( 0C99777D23200DE700EA0130 /* Debug */, 0C99777E23200DE700EA0130 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 0C99777F23200DE700EA0130 /* Build configuration list for PBXNativeTarget "liGame" */ = { isa = XCConfigurationList; buildConfigurations = ( 0C99778023200DE700EA0130 /* Debug */, 0C99778123200DE700EA0130 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 0C99776523200DE600EA0130 /* Project object */; } ================================================ FILE: liGame/liGame.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: liGame/liGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: liGame/liGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: liGame/liGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState liGame.xcscheme_^#shared#^_ orderHint 0 ================================================ FILE: liGame/liGame.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: liGame/liGame.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState liGame.xcscheme_^#shared#^_ orderHint 0 ================================================ FILE: lightGame/light-game/AppDelegate.swift ================================================ // // AppDelegate.swift // light-game // // Created by pjhubs on 2019/8/29. // Copyright © 2019 PJHubs. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } } ================================================ FILE: lightGame/light-game/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/light-game/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/light-game/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: lightGame/light-game/ContentView.swift ================================================ // // ContentView.swift // light-game // // Created by pjhubs on 2019/8/29. // Copyright © 2019 PJHubs. All rights reserved. // import SwiftUI struct ContentView: View { @ObservedObject var gameManager = GameManager(size: 5, lightSequence: [1, 2, 3]) @State var isShowHistory = false var body: some View { VStack { HStack { Text("\(gameManager.timeString)") .font(.system(size: 45)) Spacer() Text("\(gameManager.clickTimes)步") .font(.system(size: 45)) } .padding(20) ForEach(0.. CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UISceneConfigurations UIWindowSceneSessionRoleApplication UISceneConfigurationName Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: lightGame/light-game/Model/GameController.swift ================================================ // // GameController.swift // light-game // // Created by 翁培钧 on 2019/11/30. // Copyright © 2019 PJHubs. All rights reserved. // import GameController class GameController { var movingX = false var movingY = false var isSelectX: ((Bool) -> ())? var isSelectY: ((Bool) -> ())? var isTapButtonA: (() -> ())? var isTapButtonB: (() -> ())? init() { NotificationCenter.default.addObserver(self, selector: .didConnect, name: .GCControllerDidConnect, object: nil) NotificationCenter.default.addObserver(self, selector: .didConnect, name: .GCControllerDidDisconnect, object: nil) } } extension GameController { @objc fileprivate func gameControllerDidConnect() { for controller in GCController.controllers() { if controller.extendedGamepad != nil { setupControllerControls(controller: controller) } } } @objc fileprivate func gameControllerDidDisconnect() { } func setupControllerControls(controller: GCController) { controller.extendedGamepad?.valueChangedHandler = { (gamepad: GCExtendedGamepad, element: GCControllerElement) in self.controllerInput(gamePad: gamepad, element: element) } } private func controllerInput(gamePad: GCExtendedGamepad, element: GCControllerElement) { // gamePad 为支持的类型,element 为当前手柄的输入 // 不能 return,因为单次出发会带入多个按键的值 if (gamePad.leftThumbstick == element) { if (gamePad.leftThumbstick.yAxis.value != 0 && !movingY && !movingX) { movingY = true isSelectY?(gamePad.leftThumbstick.yAxis.value > 0) } else if (gamePad.leftThumbstick.yAxis.value == 0) { movingY = false } if (gamePad.leftThumbstick.xAxis.value != 0 && !movingX && !movingY) { isSelectX?(gamePad.leftThumbstick.xAxis.value > 0) movingX = true } else if (gamePad.leftThumbstick.xAxis.value == 0) { movingX = false } } else if (gamePad.rightThumbstick == element) { if (gamePad.rightThumbstick.xAxis.value != 0) { print("rightThumbstickXAxis: \(gamePad.rightThumbstick.xAxis)") } } else if (gamePad.dpad == element) { if (gamePad.dpad.xAxis.value != 0) { isSelectX?(gamePad.dpad.xAxis.value > 0) } else if (gamePad.dpad.xAxis.value == 0) { } if (gamePad.dpad.yAxis.value != 0) { isSelectY?(gamePad.dpad.yAxis.value > 0) } else if (gamePad.dpad.yAxis.value == 0) { } } else if (gamePad.buttonA == element) { if (gamePad.buttonA.value != 0) { isTapButtonA?() } } else if (gamePad.buttonB == element) { if (gamePad.buttonB.value != 0) { isTapButtonB?() } } else if (gamePad.buttonY == element) { if (gamePad.buttonY.value != 0) { // print("Y-Button Pressed!") // buttonYTap = true } } else if (gamePad.buttonX == element) { if (gamePad.buttonX.value != 0) { // print("X-Button Pressed!") // buttonXTap = true } } } } private extension Selector { static let didConnect = #selector(GameController.gameControllerDidConnect) static let didDisConnect = #selector(GameController.gameControllerDidDisconnect) } ================================================ FILE: lightGame/light-game/Model/GameManager.swift ================================================ // // GameManager.swift // light-game // // Created by pjhubs on 2019/8/30. // Copyright © 2019 PJHubs. All rights reserved. // import SwiftUI import Combine class GameManager: ObservableObject { /// 对外发布的格式化计时器字符串 @Published var timeString = "00:00" /// 点击次数 @Published var clickTimes = 0 /// 灯状态 @Published var lights = [[Light]]() @Published var isWin = false /// 当前游戏状态 private var currentStatus: GameStatus = .during { didSet { switch currentStatus { case .win: isWin = true case .lose: isWin = false case .during: break } } } /// 游戏尺寸大小 private(set) var size: Int? /// 游戏计时器 private var timer: Timer? /// 游戏持续时间 private var durations = 1 private var gameController = GameController() private var row = 0 private var column = 0 private var pRow = 0 private var pColumn = 0 /// 游戏控制器暂停 private var isPause = false // MARK: - Init /// 便捷构造方法 /// - Parameters: /// - size: 游戏布局尺寸,默认值 5x5 /// - lightSequence: 亮灯序列,默认全灭 convenience init(size: Int = 5, lightSequence: [Int] = [Int]()) { self.init() var size = size // 太大了不好玩 if size > 8 { size = 7 } // 太小了没意思 if size < 2 { size = 2 } self.size = size lights = Array(repeating: Array(repeating: Light(), count: size), count: size) initGameController() start(lightSequence) } // MARK: Public /// 游戏配置 /// - Parameter lightSequence: 亮灯序列 func start(_ lightSequence: [Int]) { currentStatus = .during clickTimes = 0 updateLightStatus(lightSequence) timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in let min = self.durations >= 60 ? self.durations / 60 : 0 let seconds = self.durations - min * 60 let minString = min >= 10 ? "\(min)" : "0\(min)" let secondString = self.durations - min * 60 >= 10 ? "\(seconds)" : "0\(seconds)" self.timeString = minString + ":" + secondString self.durations += 1 }) } /// 停止 func timerStop() { timer?.fireDate = Date.distantFuture } /// 重新创建 func timerRestart() { isPause = false durations = 0 timeString = "00:00" timer?.fireDate = Date() } /// 获取灯的尺寸 func circleWidth() -> CGFloat { guard let size = size else { return 0 } /// 距离屏幕左右两边的间距 let padding: CGFloat = 20 /// 左右两灯之间的间距 let innerSpacing: CGFloat = 20 var circleWidth = (UIScreen.main.bounds.width - padding - (CGFloat(size) * innerSpacing)) / CGFloat(size) // 太大了会很丑,过滤下 if circleWidth > UIScreen.main.bounds.width / 5 { circleWidth = UIScreen.main.bounds.width / 5 } return circleWidth } /// 通过坐标索引修改灯状态 /// - Parameters: /// - column: 灯-列索引 /// - size: 灯-行索引 func updateLightStatus(column: Int, row: Int, userTouch: Bool) { lights[row][column].status.toggle() self.column = row self.row = column // 上 let top = row - 1 if !(top < 0) { lights[top][column].status.toggle() } // 下 let bottom = row + 1 if !(bottom > lights.count - 1) { lights[bottom][column].status.toggle() } // 左 let left = column - 1 if !(left < 0) { lights[row][left].status.toggle() } // 右 let right = column + 1 if !(right > lights.count - 1) { lights[row][right].status.toggle() } updateGameStatus() if (userTouch) { disSelectedGameControllerStatus() } } // MARK: Private /// 通过亮灯序列修改灯状态 /// - Parameter lightSequence: 亮灯序列 private func updateLightStatus(_ lightSequence: [Int]) { guard let size = size else { return } for lightIndex in lightSequence { var index = lightIndex // 防止数组越出最大边界处 if index >= size * size { index = size * size - 1 } let row = lightIndex / size let column = lightIndex % size // column 不为 0,说明非最后一个 // row 为 0,说明为第一行 updateLightStatus(column: column, row: row, userTouch: true) } } private func updateLightSelected() { lights[pColumn][pRow].selected = false lights[column][row].selected = true pRow = row pColumn = column } private func disSelectedGameControllerStatus() { lights[pColumn][pRow].selected = false } /// 判赢 private func updateGameStatus() { guard let size = size else { return } var lightingCount = 0 for lightArr in lights { for light in lightArr { if light.status { lightingCount += 1 } } } if lightingCount == size * size { timerStop() currentStatus = .lose return } if lightingCount == 0 { timerStop() currentStatus = .win isPause = true return } } private func initGameController() { gameController.isSelectX = { guard !self.isPause else { return } if $0 { if (self.row < self.lights.count - 1) { self.row += 1 } } else { if self.row > 0 { self.row -= 1 } } self.updateLightSelected() } gameController.isSelectY = { guard !self.isPause else { return } if $0 { if (self.column > 0) { self.column -= 1 } } else { if (self.column < self.lights.count - 1) { self.column += 1 } } self.updateLightSelected() } gameController.isTapButtonA = { self.clickTimes += 1 self.updateLightStatus(column: self.row, row: self.column, userTouch: false) } } } extension GameManager { enum GameStatus { /// 赢 case win /// 输 case lose /// 进行中 case during } } ================================================ FILE: lightGame/light-game/Model/Light.swift ================================================ // // Light.swift // light-game // // Created by pjhubs on 2019/8/29. // Copyright © 2019 PJHubs. All rights reserved. // import Foundation struct Light { /// 开关状态 var status = false /// 选中状态 var selected = false } ================================================ FILE: lightGame/light-game/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/light-game/SceneDelegate.swift ================================================ // // SceneDelegate.swift // light-game // // Created by pjhubs on 2019/8/29. // Copyright © 2019 PJHubs. All rights reserved. // import UIKit import SwiftUI class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). // Create the SwiftUI view that provides the window contents. #if !os(watchOS) let contentView = ContentView() #else let contentView = WatchContentView() #endif // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } } func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). } func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. } func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). } func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. } func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. } } ================================================ FILE: lightGame/light-game.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 0C39CB06239BEF1100444729 /* Light.swift in Sources */ = {isa = PBXBuildFile; fileRef = A601DB2A2317775100327867 /* Light.swift */; }; 0C39CB0B239BF2EE00444729 /* WatchGameManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C39CB0A239BF2EE00444729 /* WatchGameManager.swift */; }; 0C39CB0D239C020000444729 /* WatchContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C39CB0C239C020000444729 /* WatchContentView.swift */; }; 0C3DCCE42392A5CC00C762A9 /* GameController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3DCCE32392A5CC00C762A9 /* GameController.swift */; }; 0C751029239BEAA500E85E20 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C751027239BEAA500E85E20 /* Interface.storyboard */; }; 0C75102B239BEAA800E85E20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C75102A239BEAA800E85E20 /* Assets.xcassets */; }; 0C751032239BEAA900E85E20 /* watchapp Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0C751031239BEAA900E85E20 /* watchapp Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 0C751039239BEAA900E85E20 /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C751038239BEAA900E85E20 /* HostingController.swift */; }; 0C75103B239BEAA900E85E20 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C75103A239BEAA900E85E20 /* ExtensionDelegate.swift */; }; 0C75103D239BEAA900E85E20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C75103C239BEAA900E85E20 /* Assets.xcassets */; }; 0C751040239BEAA900E85E20 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C75103F239BEAA900E85E20 /* Preview Assets.xcassets */; }; 0C751044239BEAA900E85E20 /* watchapp.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 0C751025239BEAA500E85E20 /* watchapp.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; A601DB2B2317775100327867 /* Light.swift in Sources */ = {isa = PBXBuildFile; fileRef = A601DB2A2317775100327867 /* Light.swift */; }; A62D002F231904F80062A70E /* GameManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62D002E231904F80062A70E /* GameManager.swift */; }; A676EA93231771D3002074BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A676EA92231771D3002074BF /* AppDelegate.swift */; }; A676EA95231771D3002074BF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A676EA94231771D3002074BF /* SceneDelegate.swift */; }; A676EA97231771D3002074BF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A676EA96231771D3002074BF /* ContentView.swift */; }; A676EA99231771D5002074BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A676EA98231771D5002074BF /* Assets.xcassets */; }; A676EA9C231771D5002074BF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A676EA9B231771D5002074BF /* Preview Assets.xcassets */; }; A676EA9F231771D5002074BF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A676EA9D231771D5002074BF /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 0C751033239BEAA900E85E20 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A676EA87231771D2002074BF /* Project object */; proxyType = 1; remoteGlobalIDString = 0C751030239BEAA900E85E20; remoteInfo = "watchapp Extension"; }; 0C751042239BEAA900E85E20 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A676EA87231771D2002074BF /* Project object */; proxyType = 1; remoteGlobalIDString = 0C751024239BEAA500E85E20; remoteInfo = watchapp; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 0C751045239BEAA900E85E20 /* Embed Watch Content */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; dstSubfolderSpec = 16; files = ( 0C751044239BEAA900E85E20 /* watchapp.app in Embed Watch Content */, ); name = "Embed Watch Content"; runOnlyForDeploymentPostprocessing = 0; }; 0C751048239BEAA900E85E20 /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( 0C751032239BEAA900E85E20 /* watchapp Extension.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 0C39CB0A239BF2EE00444729 /* WatchGameManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchGameManager.swift; sourceTree = ""; }; 0C39CB0C239C020000444729 /* WatchContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchContentView.swift; sourceTree = ""; }; 0C3DCCE32392A5CC00C762A9 /* GameController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameController.swift; sourceTree = ""; }; 0C751025239BEAA500E85E20 /* watchapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = watchapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0C751028239BEAA500E85E20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; 0C75102A239BEAA800E85E20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 0C75102C239BEAA800E85E20 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0C751031239BEAA900E85E20 /* watchapp Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "watchapp Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 0C751038239BEAA900E85E20 /* HostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingController.swift; sourceTree = ""; }; 0C75103A239BEAA900E85E20 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; 0C75103C239BEAA900E85E20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 0C75103F239BEAA900E85E20 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 0C751041239BEAA900E85E20 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A601DB2A2317775100327867 /* Light.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Light.swift; sourceTree = ""; }; A62D002E231904F80062A70E /* GameManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameManager.swift; sourceTree = ""; }; A676EA8F231771D3002074BF /* light-game.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "light-game.app"; sourceTree = BUILT_PRODUCTS_DIR; }; A676EA92231771D3002074BF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A676EA94231771D3002074BF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; A676EA96231771D3002074BF /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; A676EA98231771D5002074BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A676EA9B231771D5002074BF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; A676EA9E231771D5002074BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; A676EAA0231771D5002074BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 0C75102E239BEAA900E85E20 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; A676EA8C231771D2002074BF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0C751026239BEAA500E85E20 /* watchapp */ = { isa = PBXGroup; children = ( 0C751027239BEAA500E85E20 /* Interface.storyboard */, 0C75102A239BEAA800E85E20 /* Assets.xcassets */, 0C75102C239BEAA800E85E20 /* Info.plist */, ); path = watchapp; sourceTree = ""; }; 0C751035239BEAA900E85E20 /* watchapp Extension */ = { isa = PBXGroup; children = ( 0C751038239BEAA900E85E20 /* HostingController.swift */, 0C39CB0C239C020000444729 /* WatchContentView.swift */, 0C39CB0A239BF2EE00444729 /* WatchGameManager.swift */, 0C75103A239BEAA900E85E20 /* ExtensionDelegate.swift */, 0C75103C239BEAA900E85E20 /* Assets.xcassets */, 0C751041239BEAA900E85E20 /* Info.plist */, 0C75103E239BEAA900E85E20 /* Preview Content */, ); path = "watchapp Extension"; sourceTree = ""; }; 0C75103E239BEAA900E85E20 /* Preview Content */ = { isa = PBXGroup; children = ( 0C75103F239BEAA900E85E20 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; A601DB292317774600327867 /* Model */ = { isa = PBXGroup; children = ( A601DB2A2317775100327867 /* Light.swift */, A62D002E231904F80062A70E /* GameManager.swift */, 0C3DCCE32392A5CC00C762A9 /* GameController.swift */, ); path = Model; sourceTree = ""; }; A676EA86231771D2002074BF = { isa = PBXGroup; children = ( A676EA91231771D3002074BF /* light-game */, 0C751026239BEAA500E85E20 /* watchapp */, 0C751035239BEAA900E85E20 /* watchapp Extension */, A676EA90231771D3002074BF /* Products */, ); sourceTree = ""; }; A676EA90231771D3002074BF /* Products */ = { isa = PBXGroup; children = ( A676EA8F231771D3002074BF /* light-game.app */, 0C751025239BEAA500E85E20 /* watchapp.app */, 0C751031239BEAA900E85E20 /* watchapp Extension.appex */, ); name = Products; sourceTree = ""; }; A676EA91231771D3002074BF /* light-game */ = { isa = PBXGroup; children = ( A676EA92231771D3002074BF /* AppDelegate.swift */, A676EA94231771D3002074BF /* SceneDelegate.swift */, A676EA96231771D3002074BF /* ContentView.swift */, A601DB292317774600327867 /* Model */, A676EA98231771D5002074BF /* Assets.xcassets */, A676EA9D231771D5002074BF /* LaunchScreen.storyboard */, A676EAA0231771D5002074BF /* Info.plist */, A676EA9A231771D5002074BF /* Preview Content */, ); path = "light-game"; sourceTree = ""; }; A676EA9A231771D5002074BF /* Preview Content */ = { isa = PBXGroup; children = ( A676EA9B231771D5002074BF /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 0C751024239BEAA500E85E20 /* watchapp */ = { isa = PBXNativeTarget; buildConfigurationList = 0C75104C239BEAA900E85E20 /* Build configuration list for PBXNativeTarget "watchapp" */; buildPhases = ( 0C751023239BEAA500E85E20 /* Resources */, 0C751048239BEAA900E85E20 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( 0C751034239BEAA900E85E20 /* PBXTargetDependency */, ); name = watchapp; productName = watchapp; productReference = 0C751025239BEAA500E85E20 /* watchapp.app */; productType = "com.apple.product-type.application.watchapp2"; }; 0C751030239BEAA900E85E20 /* watchapp Extension */ = { isa = PBXNativeTarget; buildConfigurationList = 0C75104B239BEAA900E85E20 /* Build configuration list for PBXNativeTarget "watchapp Extension" */; buildPhases = ( 0C75102D239BEAA900E85E20 /* Sources */, 0C75102E239BEAA900E85E20 /* Frameworks */, 0C75102F239BEAA900E85E20 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "watchapp Extension"; productName = "watchapp Extension"; productReference = 0C751031239BEAA900E85E20 /* watchapp Extension.appex */; productType = "com.apple.product-type.watchkit2-extension"; }; A676EA8E231771D2002074BF /* light-game */ = { isa = PBXNativeTarget; buildConfigurationList = A676EAA3231771D5002074BF /* Build configuration list for PBXNativeTarget "light-game" */; buildPhases = ( A676EA8B231771D2002074BF /* Sources */, A676EA8C231771D2002074BF /* Frameworks */, A676EA8D231771D2002074BF /* Resources */, 0C751045239BEAA900E85E20 /* Embed Watch Content */, ); buildRules = ( ); dependencies = ( 0C751043239BEAA900E85E20 /* PBXTargetDependency */, ); name = "light-game"; productName = "light-game"; productReference = A676EA8F231771D3002074BF /* light-game.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ A676EA87231771D2002074BF /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1120; LastUpgradeCheck = 1100; ORGANIZATIONNAME = PJHubs; TargetAttributes = { 0C751024239BEAA500E85E20 = { CreatedOnToolsVersion = 11.2.1; }; 0C751030239BEAA900E85E20 = { CreatedOnToolsVersion = 11.2.1; }; A676EA8E231771D2002074BF = { CreatedOnToolsVersion = 11.0; }; }; }; buildConfigurationList = A676EA8A231771D2002074BF /* Build configuration list for PBXProject "light-game" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = A676EA86231771D2002074BF; productRefGroup = A676EA90231771D3002074BF /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( A676EA8E231771D2002074BF /* light-game */, 0C751024239BEAA500E85E20 /* watchapp */, 0C751030239BEAA900E85E20 /* watchapp Extension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 0C751023239BEAA500E85E20 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 0C75102B239BEAA800E85E20 /* Assets.xcassets in Resources */, 0C751029239BEAA500E85E20 /* Interface.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 0C75102F239BEAA900E85E20 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 0C751040239BEAA900E85E20 /* Preview Assets.xcassets in Resources */, 0C75103D239BEAA900E85E20 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; A676EA8D231771D2002074BF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( A676EA9F231771D5002074BF /* LaunchScreen.storyboard in Resources */, A676EA9C231771D5002074BF /* Preview Assets.xcassets in Resources */, A676EA99231771D5002074BF /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 0C75102D239BEAA900E85E20 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 0C751039239BEAA900E85E20 /* HostingController.swift in Sources */, 0C39CB0B239BF2EE00444729 /* WatchGameManager.swift in Sources */, 0C39CB0D239C020000444729 /* WatchContentView.swift in Sources */, 0C39CB06239BEF1100444729 /* Light.swift in Sources */, 0C75103B239BEAA900E85E20 /* ExtensionDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; A676EA8B231771D2002074BF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( A676EA93231771D3002074BF /* AppDelegate.swift in Sources */, 0C3DCCE42392A5CC00C762A9 /* GameController.swift in Sources */, A676EA95231771D3002074BF /* SceneDelegate.swift in Sources */, A601DB2B2317775100327867 /* Light.swift in Sources */, A676EA97231771D3002074BF /* ContentView.swift in Sources */, A62D002F231904F80062A70E /* GameManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 0C751034239BEAA900E85E20 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 0C751030239BEAA900E85E20 /* watchapp Extension */; targetProxy = 0C751033239BEAA900E85E20 /* PBXContainerItemProxy */; }; 0C751043239BEAA900E85E20 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 0C751024239BEAA500E85E20 /* watchapp */; targetProxy = 0C751042239BEAA900E85E20 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 0C751027239BEAA500E85E20 /* Interface.storyboard */ = { isa = PBXVariantGroup; children = ( 0C751028239BEAA500E85E20 /* Base */, ); name = Interface.storyboard; sourceTree = ""; }; A676EA9D231771D5002074BF /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( A676EA9E231771D5002074BF /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 0C751046239BEAA900E85E20 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = U6J5965DZK; IBSC_MODULE = watchapp_Extension; INFOPLIST_FILE = watchapp/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = "com.pjhubs.light-game.watchkitapp"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 6.1; }; name = Debug; }; 0C751047239BEAA900E85E20 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = U6J5965DZK; IBSC_MODULE = watchapp_Extension; INFOPLIST_FILE = watchapp/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = "com.pjhubs.light-game.watchkitapp"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 6.1; }; name = Release; }; 0C751049239BEAA900E85E20 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"watchapp Extension/Preview Content\""; DEVELOPMENT_TEAM = U6J5965DZK; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "watchapp Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.pjhubs.light-game.watchkitapp.watchkitextension"; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 6.1; }; name = Debug; }; 0C75104A239BEAA900E85E20 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"watchapp Extension/Preview Content\""; DEVELOPMENT_TEAM = U6J5965DZK; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "watchapp Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.pjhubs.light-game.watchkitapp.watchkitextension"; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 6.1; }; name = Release; }; A676EAA1231771D5002074BF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; A676EAA2231771D5002074BF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; A676EAA4231771D5002074BF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"light-game/Preview Content\""; DEVELOPMENT_TEAM = U6J5965DZK; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "light-game/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.pjhubs.light-game"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; A676EAA5231771D5002074BF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"light-game/Preview Content\""; DEVELOPMENT_TEAM = U6J5965DZK; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "light-game/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.pjhubs.light-game"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 0C75104B239BEAA900E85E20 /* Build configuration list for PBXNativeTarget "watchapp Extension" */ = { isa = XCConfigurationList; buildConfigurations = ( 0C751049239BEAA900E85E20 /* Debug */, 0C75104A239BEAA900E85E20 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 0C75104C239BEAA900E85E20 /* Build configuration list for PBXNativeTarget "watchapp" */ = { isa = XCConfigurationList; buildConfigurations = ( 0C751046239BEAA900E85E20 /* Debug */, 0C751047239BEAA900E85E20 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; A676EA8A231771D2002074BF /* Build configuration list for PBXProject "light-game" */ = { isa = XCConfigurationList; buildConfigurations = ( A676EAA1231771D5002074BF /* Debug */, A676EAA2231771D5002074BF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; A676EAA3231771D5002074BF /* Build configuration list for PBXNativeTarget "light-game" */ = { isa = XCConfigurationList; buildConfigurations = ( A676EAA4231771D5002074BF /* Debug */, A676EAA5231771D5002074BF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = A676EA87231771D2002074BF /* Project object */; } ================================================ FILE: lightGame/light-game.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: lightGame/light-game.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: lightGame/light-game.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: lightGame/light-game.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState light-game.xcscheme_^#shared#^_ orderHint 0 watchapp.xcscheme_^#shared#^_ orderHint 1 ================================================ FILE: lightGame/light-game.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: lightGame/light-game.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState light-game.xcscheme_^#shared#^_ orderHint 0 ================================================ FILE: lightGame/watchapp/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "24x24", "idiom" : "watch", "scale" : "2x", "role" : "notificationCenter", "subtype" : "38mm" }, { "size" : "27.5x27.5", "idiom" : "watch", "scale" : "2x", "role" : "notificationCenter", "subtype" : "42mm" }, { "size" : "29x29", "idiom" : "watch", "role" : "companionSettings", "scale" : "2x" }, { "size" : "29x29", "idiom" : "watch", "role" : "companionSettings", "scale" : "3x" }, { "size" : "40x40", "idiom" : "watch", "scale" : "2x", "role" : "appLauncher", "subtype" : "38mm" }, { "size" : "44x44", "idiom" : "watch", "scale" : "2x", "role" : "appLauncher", "subtype" : "40mm" }, { "size" : "50x50", "idiom" : "watch", "scale" : "2x", "role" : "appLauncher", "subtype" : "44mm" }, { "size" : "86x86", "idiom" : "watch", "scale" : "2x", "role" : "quickLook", "subtype" : "38mm" }, { "size" : "98x98", "idiom" : "watch", "scale" : "2x", "role" : "quickLook", "subtype" : "42mm" }, { "size" : "108x108", "idiom" : "watch", "scale" : "2x", "role" : "quickLook", "subtype" : "44mm" }, { "idiom" : "watch-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp/Base.lproj/Interface.storyboard ================================================ ================================================ FILE: lightGame/watchapp/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName light-game CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown WKCompanionAppBundleIdentifier com.pjhubs.light-game WKWatchKitApp ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Contents.json ================================================ { "assets" : [ { "idiom" : "watch", "filename" : "Circular.imageset", "role" : "circular" }, { "idiom" : "watch", "filename" : "Extra Large.imageset", "role" : "extra-large" }, { "idiom" : "watch", "filename" : "Graphic Bezel.imageset", "role" : "graphic-bezel" }, { "idiom" : "watch", "filename" : "Graphic Circular.imageset", "role" : "graphic-circular" }, { "idiom" : "watch", "filename" : "Graphic Corner.imageset", "role" : "graphic-corner" }, { "idiom" : "watch", "filename" : "Graphic Large Rectangular.imageset", "role" : "graphic-large-rectangular" }, { "idiom" : "watch", "filename" : "Modular.imageset", "role" : "modular" }, { "idiom" : "watch", "filename" : "Utilitarian.imageset", "role" : "utilitarian" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "watch", "scale" : "2x", "screen-width" : "<=145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">161" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">145" }, { "idiom" : "watch", "scale" : "2x", "screen-width" : ">183" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/ExtensionDelegate.swift ================================================ // // ExtensionDelegate.swift // watchapp Extension // // Created by 翁培钧 on 2019/12/7. // Copyright © 2019 PJHubs. All rights reserved. // import WatchKit class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { // Perform any final initialization of your application. } func applicationDidBecomeActive() { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillResignActive() { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, etc. } func handle(_ backgroundTasks: Set) { // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. for task in backgroundTasks { // Use a switch statement to check the task type switch task { case let backgroundTask as WKApplicationRefreshBackgroundTask: // Be sure to complete the background task once you’re done. backgroundTask.setTaskCompletedWithSnapshot(false) case let snapshotTask as WKSnapshotRefreshBackgroundTask: // Snapshot tasks have a unique completion call, make sure to set your expiration date snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: // Be sure to complete the connectivity task once you’re done. connectivityTask.setTaskCompletedWithSnapshot(false) case let urlSessionTask as WKURLSessionRefreshBackgroundTask: // Be sure to complete the URL session task once you’re done. urlSessionTask.setTaskCompletedWithSnapshot(false) case let relevantShortcutTask as WKRelevantShortcutRefreshBackgroundTask: // Be sure to complete the relevant-shortcut task once you're done. relevantShortcutTask.setTaskCompletedWithSnapshot(false) case let intentDidRunTask as WKIntentDidRunRefreshBackgroundTask: // Be sure to complete the intent-did-run task once you're done. intentDidRunTask.setTaskCompletedWithSnapshot(false) default: // make sure to complete unhandled task types task.setTaskCompletedWithSnapshot(false) } } } } ================================================ FILE: lightGame/watchapp Extension/HostingController.swift ================================================ // // HostingController.swift // watchapp Extension // // Created by 翁培钧 on 2019/12/7. // Copyright © 2019 PJHubs. All rights reserved. // import WatchKit import Foundation import SwiftUI class HostingController: WKHostingController { override var body: ContentView { return ContentView() } } ================================================ FILE: lightGame/watchapp Extension/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName watchapp Extension CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 NSExtension NSExtensionAttributes WKAppBundleIdentifier com.pjhubs.light-game.watchkitapp NSExtensionPointIdentifier com.apple.watchkit WKExtensionDelegateClassName $(PRODUCT_MODULE_NAME).ExtensionDelegate WKRunsIndependentlyOfCompanionApp ================================================ FILE: lightGame/watchapp Extension/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: lightGame/watchapp Extension/WatchContentView.swift ================================================ // // WatchContentView.swift // watchapp Extension // // Created by 翁培钧 on 2019/12/7. // Copyright © 2019 PJHubs. All rights reserved. // import SwiftUI struct ContentView: View { @ObservedObject var gameManager = WatchGameManager(size: 3, lightSequence: [1, 2, 3]) @State var isShowHistory = false var body: some View { VStack { ForEach(0.. 8 { size = 7 } // 太小了没意思 if size < 2 { size = 2 } self.size = size lights = Array(repeating: Array(repeating: Light(), count: size), count: size) // initGameController() start(lightSequence) } // MARK: Public /// 游戏配置 /// - Parameter lightSequence: 亮灯序列 func start(_ lightSequence: [Int]) { currentStatus = .during clickTimes = 0 updateLightStatus(lightSequence) timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in let min = self.durations >= 60 ? self.durations / 60 : 0 let seconds = self.durations - min * 60 let minString = min >= 10 ? "\(min)" : "0\(min)" let secondString = self.durations - min * 60 >= 10 ? "\(seconds)" : "0\(seconds)" self.timeString = minString + ":" + secondString self.durations += 1 }) } /// 停止 func timerStop() { timer?.fireDate = Date.distantFuture } /// 重新创建 func timerRestart() { isPause = false durations = 0 timeString = "00:00" timer?.fireDate = Date() } /// 获取灯的尺寸 func circleWidth() -> CGFloat { guard let size = size else { return 0 } /// 距离屏幕左右两边的间距 let padding: CGFloat = 20 /// 左右两灯之间的间距 let innerSpacing: CGFloat = 20 let screenBounds = WKInterfaceDevice.current().screenBounds var circleWidth = (screenBounds.width - padding - (CGFloat(size) * innerSpacing)) / CGFloat(size) // 太大了会很丑,过滤下 if circleWidth > screenBounds.width / 5 { circleWidth = screenBounds.width / 5 } return circleWidth } /// 通过坐标索引修改灯状态 /// - Parameters: /// - column: 灯-列索引 /// - size: 灯-行索引 func updateLightStatus(column: Int, row: Int, userTouch: Bool) { lights[row][column].status.toggle() self.column = row self.row = column // 上 let top = row - 1 if !(top < 0) { lights[top][column].status.toggle() } // 下 let bottom = row + 1 if !(bottom > lights.count - 1) { lights[bottom][column].status.toggle() } // 左 let left = column - 1 if !(left < 0) { lights[row][left].status.toggle() } // 右 let right = column + 1 if !(right > lights.count - 1) { lights[row][right].status.toggle() } updateGameStatus() // if (userTouch) { // disSelectedGameControllerStatus() // } } // MARK: Private /// 通过亮灯序列修改灯状态 /// - Parameter lightSequence: 亮灯序列 private func updateLightStatus(_ lightSequence: [Int]) { guard let size = size else { return } for lightIndex in lightSequence { var index = lightIndex // 防止数组越出最大边界处 if index >= size * size { index = size * size - 1 } let row = lightIndex / size let column = lightIndex % size // column 不为 0,说明非最后一个 // row 为 0,说明为第一行 updateLightStatus(column: column, row: row, userTouch: true) } } private func updateLightSelected() { lights[pColumn][pRow].selected = false lights[column][row].selected = true pRow = row pColumn = column } // private func disSelectedGameControllerStatus() { // lights[pColumn][pRow].selected = false // } /// 判赢 private func updateGameStatus() { guard let size = size else { return } var lightingCount = 0 for lightArr in lights { for light in lightArr { if light.status { lightingCount += 1 } } } if lightingCount == size * size { timerStop() currentStatus = .lose return } if lightingCount == 0 { timerStop() currentStatus = .win isPause = true return } } // private func initGameController() { // gameController.isSelectX = { // guard !self.isPause else { return } // // if $0 { // if (self.row < self.lights.count - 1) { // self.row += 1 // } // } else { // if self.row > 0 { // self.row -= 1 // } // } // // self.updateLightSelected() // } // // gameController.isSelectY = { // guard !self.isPause else { return } // // if $0 { // if (self.column > 0) { // self.column -= 1 // } // } else { // if (self.column < self.lights.count - 1) { // self.column += 1 // } // } // // self.updateLightSelected() // } // // gameController.isTapButtonA = { // self.clickTimes += 1 // self.updateLightStatus(column: self.row, row: self.column, userTouch: false) // } // } } extension WatchGameManager { enum WatchGameStatus { /// 赢 case win /// 输 case lose /// 进行中 case during } }