├── LICENSE ├── README.md ├── bounceGame ├── 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 └── 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 ├── liGame ├── 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 └── liGame │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── 01.imageset │ │ ├── 01.png │ │ └── Contents.json │ ├── AppIcon-1.appiconset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── Line0.imageset │ │ ├── Contents.json │ │ └── Line0@2x.png │ ├── Line1.imageset │ │ ├── Contents.json │ │ └── Line1@2x.png │ ├── Line2.imageset │ │ ├── Contents.json │ │ └── Line2@2x.png │ ├── Line3.imageset │ │ ├── Contents.json │ │ └── Line3@2x.png │ ├── created_1.imageset │ │ ├── Contents.json │ │ └── created_1.png │ ├── created_10.imageset │ │ ├── Contents.json │ │ └── created_10.png │ ├── created_11.imageset │ │ ├── Contents.json │ │ └── created_11.png │ ├── created_12.imageset │ │ ├── Contents.json │ │ └── created_12.png │ ├── created_13.imageset │ │ ├── Contents.json │ │ └── created_13.png │ ├── created_14.imageset │ │ ├── Contents.json │ │ └── created_14.png │ ├── created_15.imageset │ │ ├── Contents.json │ │ └── created_15.png │ ├── created_16.imageset │ │ ├── Contents.json │ │ └── created_16.png │ ├── created_17.imageset │ │ ├── Contents.json │ │ └── created_1.png │ ├── created_18.imageset │ │ ├── Contents.json │ │ └── created_3.png │ ├── created_2.imageset │ │ ├── Contents.json │ │ └── created_2.png │ ├── created_3.imageset │ │ ├── Contents.json │ │ └── created_3.png │ ├── created_4.imageset │ │ ├── Contents.json │ │ └── created_4.png │ ├── created_5.imageset │ │ ├── Contents.json │ │ └── created_5.png │ ├── created_6.imageset │ │ ├── Contents.json │ │ └── created_6.png │ ├── created_7.imageset │ │ ├── Contents.json │ │ └── created_7.png │ ├── created_8.imageset │ │ ├── Contents.json │ │ └── created_8.png │ ├── created_9.imageset │ │ ├── Contents.json │ │ └── created_9.png │ ├── finalMan.imageset │ │ ├── Contents.json │ │ └── finalMan.png │ └── finalManContent.imageset │ │ ├── Contents.json │ │ └── finalMamContent.png │ ├── 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 └── lightGame ├── 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 ├── 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 ├── 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 └── watchapp ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj └── Interface.storyboard └── Info.plist /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 PJHubs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《Swift 游戏开发》 2 | 3 | 这里汇集了我的小专栏《Swift 游戏开发》的全部代码,小专栏地址:https://xiaozhuanlan.com/pjhubs-swift-game 4 | 5 | ![](https://images.xiaozhuanlan.com/photo/2019/4305214109ad56a7bbc8ed9e66c2bbf6.png) 6 | 7 | [Swift 游戏开发序言](https://xiaozhuanlan.com/topic/9704163285) 8 | 9 | ![](https://images.xiaozhuanlan.com/photo/2019/22c830968d31df2249696119aa63d2db.png) 10 | 11 | [Swift 游戏开发之能否关个灯(〇)](https://xiaozhuanlan.com/topic/9806127435) 12 | 13 | [Swift 游戏开发之能否关个灯(一)](https://xiaozhuanlan.com/topic/2564017938) 14 | 15 | [Swift 游戏开发之能否关个灯(二)](https://xiaozhuanlan.com/topic/8654723190) 16 | 17 | ![](https://images.xiaozhuanlan.com/photo/2019/a0dd4d112c81e08d47c1822a7a9888d0.png) 18 | 19 | [Swift 游戏开发阶段总结(〇)](https://xiaozhuanlan.com/topic/7942150836) 20 | 21 | ![](https://images.xiaozhuanlan.com/photo/2019/d8805030d828cae0c691121d3d3c5ccb.png) 22 | 23 | [Swift 游戏开发之黎锦拼图(〇)](https://xiaozhuanlan.com/topic/8079642315) 24 | 25 | [Swift 游戏开发之黎锦拼图(一)](https://xiaozhuanlan.com/topic/0312569847) 26 | 27 | [Swift 游戏开发之黎锦拼图(二)](https://xiaozhuanlan.com/topic/9738401526) 28 | 29 | [Swift 游戏开发之黎锦拼图(三)](https://xiaozhuanlan.com/topic/3968275140) 30 | 31 | [Swift 游戏开发之黎锦拼图(四)](https://xiaozhuanlan.com/topic/7436519820) 32 | 33 | ![](https://images.xiaozhuanlan.com/photo/2019/ac56d26bd4577bcdd242e2fe90141d6d.png) 34 | 35 | [Swift 游戏开发阶段总结(一)](https://xiaozhuanlan.com/topic/8109647352) 36 | 37 | ![](https://images.xiaozhuanlan.com/photo/2019/e4fb08d3c028c97f5da6e62a7a08a6cd.png) 38 | 39 | [Swift 游戏开发之方块弹珠(〇))](https://xiaozhuanlan.com/topic/1758926340) 40 | 41 | -------------------------------------------------------------------------------- /bounceGame/bounceGame.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C8BB82F23C4C84B00F88BD6 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8BB82E23C4C84B00F88BD6 /* Box.swift */; }; 11 | 0C964EBD23B8E5D0001EA659 /* Ball.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C964EBC23B8E5D0001EA659 /* Ball.swift */; }; 12 | 0C964EBF23BA3FDF001EA659 /* Define.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C964EBE23BA3FDF001EA659 /* Define.swift */; }; 13 | 0CA02F8523AFAB7D004A580E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA02F8423AFAB7D004A580E /* AppDelegate.swift */; }; 14 | 0CA02F8723AFAB7D004A580E /* GameScene.sks in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F8623AFAB7D004A580E /* GameScene.sks */; }; 15 | 0CA02F8923AFAB7D004A580E /* Actions.sks in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F8823AFAB7D004A580E /* Actions.sks */; }; 16 | 0CA02F8B23AFAB7D004A580E /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA02F8A23AFAB7D004A580E /* GameScene.swift */; }; 17 | 0CA02F8D23AFAB7D004A580E /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA02F8C23AFAB7D004A580E /* GameViewController.swift */; }; 18 | 0CA02F9023AFAB7D004A580E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F8E23AFAB7D004A580E /* Main.storyboard */; }; 19 | 0CA02F9223AFAB7E004A580E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F9123AFAB7E004A580E /* Assets.xcassets */; }; 20 | 0CA02F9523AFAB7E004A580E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0CA02F9323AFAB7E004A580E /* LaunchScreen.storyboard */; }; 21 | 0CFFFBBE23C629AD00BB3F13 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFFFBBD23C629AD00BB3F13 /* Label.swift */; }; 22 | 0CFFFBC223C62AD300BB3F13 /* Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFFFBC123C62AD300BB3F13 /* Extension.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 0C8BB82E23C4C84B00F88BD6 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; 27 | 0C964EBC23B8E5D0001EA659 /* Ball.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ball.swift; sourceTree = ""; }; 28 | 0C964EBE23BA3FDF001EA659 /* Define.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Define.swift; sourceTree = ""; }; 29 | 0CA02F8123AFAB7D004A580E /* bounceGame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bounceGame.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 0CA02F8423AFAB7D004A580E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 31 | 0CA02F8623AFAB7D004A580E /* GameScene.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = GameScene.sks; sourceTree = ""; }; 32 | 0CA02F8823AFAB7D004A580E /* Actions.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = Actions.sks; sourceTree = ""; }; 33 | 0CA02F8A23AFAB7D004A580E /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScene.swift; sourceTree = ""; }; 34 | 0CA02F8C23AFAB7D004A580E /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 35 | 0CA02F8F23AFAB7D004A580E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36 | 0CA02F9123AFAB7E004A580E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 37 | 0CA02F9423AFAB7E004A580E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 38 | 0CA02F9623AFAB7E004A580E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 0CFFFBBD23C629AD00BB3F13 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 40 | 0CFFFBC123C62AD300BB3F13 /* Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extension.swift; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 0CA02F7E23AFAB7D004A580E /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 0C964EBB23B8E5A6001EA659 /* Ball */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 0C964EBC23B8E5D0001EA659 /* Ball.swift */, 58 | 0C8BB82E23C4C84B00F88BD6 /* Box.swift */, 59 | 0CFFFBBD23C629AD00BB3F13 /* Label.swift */, 60 | ); 61 | path = Ball; 62 | sourceTree = ""; 63 | }; 64 | 0CA02F7823AFAB7D004A580E = { 65 | isa = PBXGroup; 66 | children = ( 67 | 0CA02F8323AFAB7D004A580E /* bounceGame */, 68 | 0CA02F8223AFAB7D004A580E /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 0CA02F8223AFAB7D004A580E /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 0CA02F8123AFAB7D004A580E /* bounceGame.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 0CA02F8323AFAB7D004A580E /* bounceGame */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 0CA02F8423AFAB7D004A580E /* AppDelegate.swift */, 84 | 0CA02F8623AFAB7D004A580E /* GameScene.sks */, 85 | 0CA02F8823AFAB7D004A580E /* Actions.sks */, 86 | 0CA02F8A23AFAB7D004A580E /* GameScene.swift */, 87 | 0CA02F8C23AFAB7D004A580E /* GameViewController.swift */, 88 | 0C964EBE23BA3FDF001EA659 /* Define.swift */, 89 | 0CFFFBC123C62AD300BB3F13 /* Extension.swift */, 90 | 0C964EBB23B8E5A6001EA659 /* Ball */, 91 | 0CA02F8E23AFAB7D004A580E /* Main.storyboard */, 92 | 0CA02F9123AFAB7E004A580E /* Assets.xcassets */, 93 | 0CA02F9323AFAB7E004A580E /* LaunchScreen.storyboard */, 94 | 0CA02F9623AFAB7E004A580E /* Info.plist */, 95 | ); 96 | path = bounceGame; 97 | sourceTree = ""; 98 | }; 99 | /* End PBXGroup section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | 0CA02F8023AFAB7D004A580E /* bounceGame */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = 0CA02F9923AFAB7E004A580E /* Build configuration list for PBXNativeTarget "bounceGame" */; 105 | buildPhases = ( 106 | 0CA02F7D23AFAB7D004A580E /* Sources */, 107 | 0CA02F7E23AFAB7D004A580E /* Frameworks */, 108 | 0CA02F7F23AFAB7D004A580E /* Resources */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = bounceGame; 115 | productName = bounceGame; 116 | productReference = 0CA02F8123AFAB7D004A580E /* bounceGame.app */; 117 | productType = "com.apple.product-type.application"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | 0CA02F7923AFAB7D004A580E /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | LastSwiftUpdateCheck = 1120; 126 | LastUpgradeCheck = 1120; 127 | ORGANIZATIONNAME = "翁培钧"; 128 | TargetAttributes = { 129 | 0CA02F8023AFAB7D004A580E = { 130 | CreatedOnToolsVersion = 11.2.1; 131 | }; 132 | }; 133 | }; 134 | buildConfigurationList = 0CA02F7C23AFAB7D004A580E /* Build configuration list for PBXProject "bounceGame" */; 135 | compatibilityVersion = "Xcode 9.3"; 136 | developmentRegion = en; 137 | hasScannedForEncodings = 0; 138 | knownRegions = ( 139 | en, 140 | Base, 141 | ); 142 | mainGroup = 0CA02F7823AFAB7D004A580E; 143 | productRefGroup = 0CA02F8223AFAB7D004A580E /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | 0CA02F8023AFAB7D004A580E /* bounceGame */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXResourcesBuildPhase section */ 153 | 0CA02F7F23AFAB7D004A580E /* Resources */ = { 154 | isa = PBXResourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 0CA02F9023AFAB7D004A580E /* Main.storyboard in Resources */, 158 | 0CA02F8723AFAB7D004A580E /* GameScene.sks in Resources */, 159 | 0CA02F9223AFAB7E004A580E /* Assets.xcassets in Resources */, 160 | 0CA02F9523AFAB7E004A580E /* LaunchScreen.storyboard in Resources */, 161 | 0CA02F8923AFAB7D004A580E /* Actions.sks in Resources */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | /* End PBXResourcesBuildPhase section */ 166 | 167 | /* Begin PBXSourcesBuildPhase section */ 168 | 0CA02F7D23AFAB7D004A580E /* Sources */ = { 169 | isa = PBXSourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | 0C964EBD23B8E5D0001EA659 /* Ball.swift in Sources */, 173 | 0CA02F8B23AFAB7D004A580E /* GameScene.swift in Sources */, 174 | 0CA02F8D23AFAB7D004A580E /* GameViewController.swift in Sources */, 175 | 0C8BB82F23C4C84B00F88BD6 /* Box.swift in Sources */, 176 | 0C964EBF23BA3FDF001EA659 /* Define.swift in Sources */, 177 | 0CFFFBBE23C629AD00BB3F13 /* Label.swift in Sources */, 178 | 0CA02F8523AFAB7D004A580E /* AppDelegate.swift in Sources */, 179 | 0CFFFBC223C62AD300BB3F13 /* Extension.swift in Sources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXSourcesBuildPhase section */ 184 | 185 | /* Begin PBXVariantGroup section */ 186 | 0CA02F8E23AFAB7D004A580E /* Main.storyboard */ = { 187 | isa = PBXVariantGroup; 188 | children = ( 189 | 0CA02F8F23AFAB7D004A580E /* Base */, 190 | ); 191 | name = Main.storyboard; 192 | sourceTree = ""; 193 | }; 194 | 0CA02F9323AFAB7E004A580E /* LaunchScreen.storyboard */ = { 195 | isa = PBXVariantGroup; 196 | children = ( 197 | 0CA02F9423AFAB7E004A580E /* Base */, 198 | ); 199 | name = LaunchScreen.storyboard; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXVariantGroup section */ 203 | 204 | /* Begin XCBuildConfiguration section */ 205 | 0CA02F9723AFAB7E004A580E /* Debug */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | ALWAYS_SEARCH_USER_PATHS = NO; 209 | CLANG_ANALYZER_NONNULL = YES; 210 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 211 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 212 | CLANG_CXX_LIBRARY = "libc++"; 213 | CLANG_ENABLE_MODULES = YES; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_ENABLE_OBJC_WEAK = YES; 216 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 217 | CLANG_WARN_BOOL_CONVERSION = YES; 218 | CLANG_WARN_COMMA = YES; 219 | CLANG_WARN_CONSTANT_CONVERSION = YES; 220 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 221 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 222 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 223 | CLANG_WARN_EMPTY_BODY = YES; 224 | CLANG_WARN_ENUM_CONVERSION = YES; 225 | CLANG_WARN_INFINITE_RECURSION = YES; 226 | CLANG_WARN_INT_CONVERSION = YES; 227 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 228 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 229 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 230 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 231 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 232 | CLANG_WARN_STRICT_PROTOTYPES = YES; 233 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 234 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 235 | CLANG_WARN_UNREACHABLE_CODE = YES; 236 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 237 | COPY_PHASE_STRIP = NO; 238 | DEBUG_INFORMATION_FORMAT = dwarf; 239 | ENABLE_STRICT_OBJC_MSGSEND = YES; 240 | ENABLE_TESTABILITY = YES; 241 | GCC_C_LANGUAGE_STANDARD = gnu11; 242 | GCC_DYNAMIC_NO_PIC = NO; 243 | GCC_NO_COMMON_BLOCKS = YES; 244 | GCC_OPTIMIZATION_LEVEL = 0; 245 | GCC_PREPROCESSOR_DEFINITIONS = ( 246 | "DEBUG=1", 247 | "$(inherited)", 248 | ); 249 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 250 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 251 | GCC_WARN_UNDECLARED_SELECTOR = YES; 252 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 253 | GCC_WARN_UNUSED_FUNCTION = YES; 254 | GCC_WARN_UNUSED_VARIABLE = YES; 255 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 256 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 257 | MTL_FAST_MATH = YES; 258 | ONLY_ACTIVE_ARCH = YES; 259 | SDKROOT = iphoneos; 260 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 261 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 262 | }; 263 | name = Debug; 264 | }; 265 | 0CA02F9823AFAB7E004A580E /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 272 | CLANG_CXX_LIBRARY = "libc++"; 273 | CLANG_ENABLE_MODULES = YES; 274 | CLANG_ENABLE_OBJC_ARC = YES; 275 | CLANG_ENABLE_OBJC_WEAK = YES; 276 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 277 | CLANG_WARN_BOOL_CONVERSION = YES; 278 | CLANG_WARN_COMMA = YES; 279 | CLANG_WARN_CONSTANT_CONVERSION = YES; 280 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 282 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 283 | CLANG_WARN_EMPTY_BODY = YES; 284 | CLANG_WARN_ENUM_CONVERSION = YES; 285 | CLANG_WARN_INFINITE_RECURSION = YES; 286 | CLANG_WARN_INT_CONVERSION = YES; 287 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 288 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 289 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 291 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 292 | CLANG_WARN_STRICT_PROTOTYPES = YES; 293 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 294 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 295 | CLANG_WARN_UNREACHABLE_CODE = YES; 296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 297 | COPY_PHASE_STRIP = NO; 298 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 299 | ENABLE_NS_ASSERTIONS = NO; 300 | ENABLE_STRICT_OBJC_MSGSEND = YES; 301 | GCC_C_LANGUAGE_STANDARD = gnu11; 302 | GCC_NO_COMMON_BLOCKS = YES; 303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 305 | GCC_WARN_UNDECLARED_SELECTOR = YES; 306 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 307 | GCC_WARN_UNUSED_FUNCTION = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 310 | MTL_ENABLE_DEBUG_INFO = NO; 311 | MTL_FAST_MATH = YES; 312 | SDKROOT = iphoneos; 313 | SWIFT_COMPILATION_MODE = wholemodule; 314 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 315 | VALIDATE_PRODUCT = YES; 316 | }; 317 | name = Release; 318 | }; 319 | 0CA02F9A23AFAB7E004A580E /* Debug */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 323 | CODE_SIGN_STYLE = Automatic; 324 | DEVELOPMENT_TEAM = U6J5965DZK; 325 | INFOPLIST_FILE = bounceGame/Info.plist; 326 | LD_RUNPATH_SEARCH_PATHS = ( 327 | "$(inherited)", 328 | "@executable_path/Frameworks", 329 | ); 330 | PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.bounceGame; 331 | PRODUCT_NAME = "$(TARGET_NAME)"; 332 | SWIFT_VERSION = 5.0; 333 | TARGETED_DEVICE_FAMILY = "1,2"; 334 | }; 335 | name = Debug; 336 | }; 337 | 0CA02F9B23AFAB7E004A580E /* Release */ = { 338 | isa = XCBuildConfiguration; 339 | buildSettings = { 340 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 341 | CODE_SIGN_STYLE = Automatic; 342 | DEVELOPMENT_TEAM = U6J5965DZK; 343 | INFOPLIST_FILE = bounceGame/Info.plist; 344 | LD_RUNPATH_SEARCH_PATHS = ( 345 | "$(inherited)", 346 | "@executable_path/Frameworks", 347 | ); 348 | PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.bounceGame; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | SWIFT_VERSION = 5.0; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Release; 354 | }; 355 | /* End XCBuildConfiguration section */ 356 | 357 | /* Begin XCConfigurationList section */ 358 | 0CA02F7C23AFAB7D004A580E /* Build configuration list for PBXProject "bounceGame" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | 0CA02F9723AFAB7E004A580E /* Debug */, 362 | 0CA02F9823AFAB7E004A580E /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | 0CA02F9923AFAB7E004A580E /* Build configuration list for PBXNativeTarget "bounceGame" */ = { 368 | isa = XCConfigurationList; 369 | buildConfigurations = ( 370 | 0CA02F9A23AFAB7E004A580E /* Debug */, 371 | 0CA02F9B23AFAB7E004A580E /* Release */, 372 | ); 373 | defaultConfigurationIsVisible = 0; 374 | defaultConfigurationName = Release; 375 | }; 376 | /* End XCConfigurationList section */ 377 | }; 378 | rootObject = 0CA02F7923AFAB7D004A580E /* Project object */; 379 | } 380 | -------------------------------------------------------------------------------- /bounceGame/bounceGame.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /bounceGame/bounceGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /bounceGame/bounceGame.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/bounceGame/bounceGame.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /bounceGame/bounceGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /bounceGame/bounceGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | bounceGame.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Actions.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/bounceGame/bounceGame/Actions.sks -------------------------------------------------------------------------------- /bounceGame/bounceGame/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2019/12/22. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // 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. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /bounceGame/bounceGame/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /bounceGame/bounceGame/Ball/Ball.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ball.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2019/12/29. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | class Ball: SKShapeNode { 12 | var isShot = false 13 | 14 | 15 | override init() { 16 | super.init() 17 | 18 | initObject() 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | 25 | private func initObject() { 26 | self.fillColor = .red 27 | self.physicsBody = SKPhysicsBody(circleOfRadius: 10) 28 | self.physicsBody?.categoryBitMask = BitMask.Ball 29 | self.physicsBody?.contactTestBitMask = BitMask.Box | BitMask.Ground 30 | self.physicsBody?.collisionBitMask = BitMask.Box 31 | self.physicsBody?.usesPreciseCollisionDetection = true; 32 | self.physicsBody?.linearDamping = 0 33 | self.physicsBody?.restitution = 1.0 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Ball/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2020/1/7. 6 | // Copyright © 2020 翁培钧. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | class Box: SKShapeNode { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Ball/Label.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Label.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2020/1/8. 6 | // Copyright © 2020 翁培钧. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | class Label: SKLabelNode { 12 | var typoTag = 0 13 | } 14 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Define.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Define.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2019/12/30. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | struct BitMask { 13 | static let Ball = UInt32(0x00001) 14 | static let Box = UInt32(0x00002) 15 | static let Wall = UInt32(0x00003) 16 | static let Ground = UInt32(0x00004) 17 | } 18 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2020/1/8. 6 | // Copyright © 2020 翁培钧. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | 12 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/GameScene.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/bounceGame/bounceGame/GameScene.sks -------------------------------------------------------------------------------- /bounceGame/bounceGame/GameScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2019/12/22. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | class GameScene: SKScene { 12 | 13 | private var balls = [Ball]() 14 | private var contactGroundBalls = [Ball]() 15 | 16 | 17 | private var isBegin = false 18 | 19 | 20 | private var ground = SKSpriteNode() 21 | private var firstDownBall: Ball? 22 | 23 | 24 | 25 | 26 | override init(size: CGSize) { 27 | super.init(size: size) 28 | 29 | physicsWorld.gravity = CGVector(dx: 0, dy: -1) 30 | physicsWorld.contactDelegate = self 31 | } 32 | 33 | required init?(coder aDecoder: NSCoder) { 34 | fatalError("init(coder:) has not been implemented") 35 | } 36 | 37 | var contentCreated = false 38 | 39 | override func didMove(to view: SKView) { 40 | if !contentCreated { 41 | createContent() 42 | contentCreated = true 43 | } 44 | } 45 | 46 | private func createContent() { 47 | ground = SKSpriteNode(color: .gray, size: CGSize(width: size.width, height: 120)) 48 | ground.position = CGPoint(x: size.width / 2, y: ground.size.height / 2) 49 | addChild(ground) 50 | ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size) 51 | ground.physicsBody?.isDynamic = false 52 | ground.physicsBody?.collisionBitMask = BitMask.Ball 53 | ground.physicsBody?.categoryBitMask = BitMask.Ground 54 | ground.physicsBody?.contactTestBitMask = BitMask.Ball 55 | 56 | let wall = SKNode() 57 | wall.position = CGPoint(x: 0, y: 0) 58 | wall.physicsBody?.friction = 0 59 | wall.physicsBody?.isDynamic = false 60 | wall.physicsBody?.restitution = 1.0 61 | wall.physicsBody?.collisionBitMask = BitMask.Wall 62 | wall.physicsBody?.categoryBitMask = BitMask.Wall 63 | wall.physicsBody?.contactTestBitMask = BitMask.Wall 64 | wall.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: 0, y: 0, width: size.width, height: size.height)) 65 | addChild(wall) 66 | 67 | for _ in 0..<5 { 68 | let ball = Ball(circleOfRadius: 10) 69 | balls.append(ball) 70 | addChild(ball) 71 | ball.physicsBody?.isDynamic = false 72 | ball.position = CGPoint(x: size.width / 2, y: ground.frame.size.height + ball.frame.size.height / 2) 73 | } 74 | 75 | for row in 1...5 { 76 | let box = Box(rectOf: CGSize(width: 50, height: 50)) 77 | box.position = CGPoint(x: 50 + (row * 50 + 20), y: (800 - row * 50 + 20)) 78 | box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50)) 79 | box.physicsBody?.categoryBitMask = BitMask.Box 80 | box.physicsBody?.contactTestBitMask = BitMask.Ball 81 | box.physicsBody?.collisionBitMask = BitMask.Box 82 | box.physicsBody?.linearDamping = 0 83 | box.physicsBody?.restitution = 1.0 84 | box.physicsBody?.isDynamic = false 85 | box.fillColor = .red 86 | 87 | let label = Label(text: "\(row)") 88 | label.fontSize = 22 89 | label.typoTag = 666 90 | label.fontName = "Arial-BoldMT" 91 | label.color = .white 92 | label.position = CGPoint(x: 0, y: -label.frame.size.height / 2) 93 | box.addChild(label) 94 | 95 | addChild(box) 96 | } 97 | } 98 | } 99 | 100 | extension GameScene { 101 | private func shot() { 102 | for (index, ball) in balls.enumerated() { 103 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) { 104 | ball.physicsBody?.isDynamic = true 105 | if (!self.children.contains(ball)) { 106 | self.addChild(ball) 107 | } 108 | ball.physicsBody?.applyImpulse(CGVector(dx: 10, dy: 10)) 109 | ball.physicsBody?.applyTorque(1.8) 110 | // ball.physicsBody?.applyForce(CGVector(dx: 400 + CGFloat(index) * 0.1, dy: 800)) 111 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { 112 | ball.isShot = true 113 | 114 | if (index == self.balls.count - 1) { 115 | self.firstDownBall?.removeFromParent() 116 | self.firstDownBall = nil 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | 124 | extension GameScene { 125 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 126 | shot() 127 | } 128 | } 129 | 130 | extension GameScene: SKPhysicsContactDelegate { 131 | func didBegin(_ contact: SKPhysicsContact) { 132 | print("\(contact.bodyA.contactTestBitMask) == \(contact.bodyB.contactTestBitMask)") 133 | 134 | 135 | 136 | switch contact.bodyA.categoryBitMask { 137 | case BitMask.Box: 138 | checkNodeIsBox(contact.bodyA.node) 139 | checkNodeIsWall(contact.bodyB.node) 140 | case BitMask.Wall: 141 | checkNodeIsWall(contact.bodyB.node) 142 | case BitMask.Ground: 143 | checkNodeIsGround(contact.bodyB.node) 144 | default: 145 | break 146 | } 147 | 148 | switch contact.bodyB.categoryBitMask { 149 | case BitMask.Box: 150 | checkNodeIsBox(contact.bodyB.node) 151 | checkNodeIsWall(contact.bodyA.node) 152 | case BitMask.Wall: 153 | checkNodeIsWall(contact.bodyA.node) 154 | case BitMask.Ground: 155 | checkNodeIsGround(contact.bodyA.node) 156 | default: 157 | break 158 | } 159 | } 160 | } 161 | 162 | extension GameScene { 163 | private func checkNodeIsBox(_ node: SKNode?) { 164 | guard let box = node else { return } 165 | 166 | if box.physicsBody?.categoryBitMask == BitMask.Box { 167 | let label = box.children.first! as! Label 168 | var tag = Int(label.text!)! 169 | if (tag > 1) { 170 | tag -= 1 171 | label.text = "\(tag)" 172 | } else { 173 | box.removeFromParent() 174 | } 175 | } 176 | } 177 | 178 | private func checkNodeIsWall(_ node: SKNode?) { 179 | guard let ball = node as? Ball else { return } 180 | 181 | ball.isShot = true 182 | } 183 | 184 | private func checkNodeIsGround(_ node: SKNode?) { 185 | guard let ball = node as? Ball else { return } 186 | 187 | // NOTE: 小球 & 发射出去 188 | if (ball.physicsBody?.categoryBitMask == BitMask.Ball && ball.isShot) { 189 | ball.removeFromParent(); 190 | 191 | // NOTE: 定位小球不存在 || 当前 Scene 中不包含定位小球 192 | if (firstDownBall == nil || !children.contains(firstDownBall!)) { 193 | firstDownBall = Ball(circleOfRadius: 10) 194 | firstDownBall!.position = CGPoint(x: ball.position.x, y: ground.frame.size.height + ball.frame.size.height / 2 - 2) 195 | addChild(firstDownBall!) 196 | // NOTE: 静止 197 | firstDownBall!.physicsBody?.isDynamic = false 198 | } 199 | 200 | // NOTE: 统一重设后续触底小球位置。二次发射时,直接读取各个小球的初始位置进行发射 201 | ball.position = CGPoint(x: firstDownBall!.position.x, y: ground.frame.size.height + ball.frame.size.height / 2) 202 | } 203 | } 204 | } 205 | 206 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // bounceGame 4 | // 5 | // Created by 翁培钧 on 2019/12/22. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | 12 | class GameViewController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | if let view = self.view as! SKView? { 18 | 19 | let scene = GameScene(size: view.frame.size) 20 | scene.scaleMode = .aspectFill 21 | view.presentScene(scene) 22 | view.ignoresSiblingOrder = true 23 | view.showsFPS = true 24 | view.showsNodeCount = true 25 | view.showsPhysics = true 26 | } 27 | } 28 | 29 | override var prefersStatusBarHidden: Bool { 30 | return true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bounceGame/bounceGame/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C7ACB9C2322A97000AA4FA4 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7ACB9B2322A97000AA4FA4 /* UIImage+Extension.swift */; }; 11 | 0C99777123200DE600EA0130 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C99777023200DE600EA0130 /* AppDelegate.swift */; }; 12 | 0C99777323200DE600EA0130 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C99777223200DE600EA0130 /* ViewController.swift */; }; 13 | 0C99777623200DE600EA0130 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C99777423200DE600EA0130 /* Main.storyboard */; }; 14 | 0C99777823200DE700EA0130 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C99777723200DE700EA0130 /* Assets.xcassets */; }; 15 | 0C99777B23200DE700EA0130 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C99777923200DE700EA0130 /* LaunchScreen.storyboard */; }; 16 | 0C9977832320135200EA0130 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9977822320135200EA0130 /* UIView+Extension.swift */; }; 17 | 0C997785232013C300EA0130 /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C997784232013C300EA0130 /* UIColor+Extension.swift */; }; 18 | 0C997788232014A500EA0130 /* Define.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C997787232014A500EA0130 /* Define.swift */; }; 19 | 0CAE10A62321E76900CFECA5 /* Puzzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAE10A52321E76900CFECA5 /* Puzzle.swift */; }; 20 | 0CE27C0A2371C4950032CF94 /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE27C092371C4950032CF94 /* Animation.swift */; }; 21 | A64F18812325121900F531F4 /* LiBottomCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64F18802325121900F531F4 /* LiBottomCollectionView.swift */; }; 22 | A64F18832325126800F531F4 /* LiBottomCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64F18822325126800F531F4 /* LiBottomCollectionViewCell.swift */; }; 23 | A64F18852325133E00F531F4 /* LiBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64F18842325133E00F531F4 /* LiBottomView.swift */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 0C7ACB9B2322A97000AA4FA4 /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = ""; }; 28 | 0C99776D23200DE600EA0130 /* liGame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = liGame.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 0C99777023200DE600EA0130 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 30 | 0C99777223200DE600EA0130 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 31 | 0C99777523200DE600EA0130 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | 0C99777723200DE700EA0130 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 0C99777A23200DE700EA0130 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | 0C99777C23200DE700EA0130 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 0C9977822320135200EA0130 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 36 | 0C997784232013C300EA0130 /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 37 | 0C997787232014A500EA0130 /* Define.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Define.swift; sourceTree = ""; }; 38 | 0CAE10A52321E76900CFECA5 /* Puzzle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Puzzle.swift; sourceTree = ""; }; 39 | 0CE27C092371C4950032CF94 /* Animation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animation.swift; sourceTree = ""; }; 40 | A64F18802325121900F531F4 /* LiBottomCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiBottomCollectionView.swift; sourceTree = ""; }; 41 | A64F18822325126800F531F4 /* LiBottomCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiBottomCollectionViewCell.swift; sourceTree = ""; }; 42 | A64F18842325133E00F531F4 /* LiBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiBottomView.swift; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | 0C99776A23200DE600EA0130 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 0C99776423200DE600EA0130 = { 57 | isa = PBXGroup; 58 | children = ( 59 | 0C99776F23200DE600EA0130 /* liGame */, 60 | 0C99776E23200DE600EA0130 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | 0C99776E23200DE600EA0130 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 0C99776D23200DE600EA0130 /* liGame.app */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | 0C99776F23200DE600EA0130 /* liGame */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 0C99777023200DE600EA0130 /* AppDelegate.swift */, 76 | 0C99777223200DE600EA0130 /* ViewController.swift */, 77 | 0C9977892321E70900EA0130 /* View */, 78 | 0C9977862320149900EA0130 /* Extension */, 79 | 0C99777423200DE600EA0130 /* Main.storyboard */, 80 | 0C99777723200DE700EA0130 /* Assets.xcassets */, 81 | 0C99777923200DE700EA0130 /* LaunchScreen.storyboard */, 82 | 0C99777C23200DE700EA0130 /* Info.plist */, 83 | ); 84 | path = liGame; 85 | sourceTree = ""; 86 | }; 87 | 0C9977862320149900EA0130 /* Extension */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 0CE27C092371C4950032CF94 /* Animation.swift */, 91 | 0C9977822320135200EA0130 /* UIView+Extension.swift */, 92 | 0C997784232013C300EA0130 /* UIColor+Extension.swift */, 93 | 0C997787232014A500EA0130 /* Define.swift */, 94 | 0C7ACB9B2322A97000AA4FA4 /* UIImage+Extension.swift */, 95 | ); 96 | path = Extension; 97 | sourceTree = ""; 98 | }; 99 | 0C9977892321E70900EA0130 /* View */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | A64F187F232511F500F531F4 /* Bottom */, 103 | 0CAE10A52321E76900CFECA5 /* Puzzle.swift */, 104 | ); 105 | path = View; 106 | sourceTree = ""; 107 | }; 108 | A64F187F232511F500F531F4 /* Bottom */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | A64F18802325121900F531F4 /* LiBottomCollectionView.swift */, 112 | A64F18822325126800F531F4 /* LiBottomCollectionViewCell.swift */, 113 | A64F18842325133E00F531F4 /* LiBottomView.swift */, 114 | ); 115 | path = Bottom; 116 | sourceTree = ""; 117 | }; 118 | /* End PBXGroup section */ 119 | 120 | /* Begin PBXNativeTarget section */ 121 | 0C99776C23200DE600EA0130 /* liGame */ = { 122 | isa = PBXNativeTarget; 123 | buildConfigurationList = 0C99777F23200DE700EA0130 /* Build configuration list for PBXNativeTarget "liGame" */; 124 | buildPhases = ( 125 | 0C99776923200DE600EA0130 /* Sources */, 126 | 0C99776A23200DE600EA0130 /* Frameworks */, 127 | 0C99776B23200DE600EA0130 /* Resources */, 128 | ); 129 | buildRules = ( 130 | ); 131 | dependencies = ( 132 | ); 133 | name = liGame; 134 | productName = liGame; 135 | productReference = 0C99776D23200DE600EA0130 /* liGame.app */; 136 | productType = "com.apple.product-type.application"; 137 | }; 138 | /* End PBXNativeTarget section */ 139 | 140 | /* Begin PBXProject section */ 141 | 0C99776523200DE600EA0130 /* Project object */ = { 142 | isa = PBXProject; 143 | attributes = { 144 | LastSwiftUpdateCheck = 1020; 145 | LastUpgradeCheck = 1020; 146 | ORGANIZATIONNAME = "翁培钧"; 147 | TargetAttributes = { 148 | 0C99776C23200DE600EA0130 = { 149 | CreatedOnToolsVersion = 10.2.1; 150 | }; 151 | }; 152 | }; 153 | buildConfigurationList = 0C99776823200DE600EA0130 /* Build configuration list for PBXProject "liGame" */; 154 | compatibilityVersion = "Xcode 9.3"; 155 | developmentRegion = en; 156 | hasScannedForEncodings = 0; 157 | knownRegions = ( 158 | en, 159 | Base, 160 | ); 161 | mainGroup = 0C99776423200DE600EA0130; 162 | productRefGroup = 0C99776E23200DE600EA0130 /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | 0C99776C23200DE600EA0130 /* liGame */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | 0C99776B23200DE600EA0130 /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | 0C99777B23200DE700EA0130 /* LaunchScreen.storyboard in Resources */, 177 | 0C99777823200DE700EA0130 /* Assets.xcassets in Resources */, 178 | 0C99777623200DE600EA0130 /* Main.storyboard in Resources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXResourcesBuildPhase section */ 183 | 184 | /* Begin PBXSourcesBuildPhase section */ 185 | 0C99776923200DE600EA0130 /* Sources */ = { 186 | isa = PBXSourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 0CE27C0A2371C4950032CF94 /* Animation.swift in Sources */, 190 | A64F18812325121900F531F4 /* LiBottomCollectionView.swift in Sources */, 191 | 0C9977832320135200EA0130 /* UIView+Extension.swift in Sources */, 192 | 0C7ACB9C2322A97000AA4FA4 /* UIImage+Extension.swift in Sources */, 193 | 0C99777323200DE600EA0130 /* ViewController.swift in Sources */, 194 | A64F18832325126800F531F4 /* LiBottomCollectionViewCell.swift in Sources */, 195 | 0C99777123200DE600EA0130 /* AppDelegate.swift in Sources */, 196 | 0CAE10A62321E76900CFECA5 /* Puzzle.swift in Sources */, 197 | 0C997788232014A500EA0130 /* Define.swift in Sources */, 198 | A64F18852325133E00F531F4 /* LiBottomView.swift in Sources */, 199 | 0C997785232013C300EA0130 /* UIColor+Extension.swift in Sources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXSourcesBuildPhase section */ 204 | 205 | /* Begin PBXVariantGroup section */ 206 | 0C99777423200DE600EA0130 /* Main.storyboard */ = { 207 | isa = PBXVariantGroup; 208 | children = ( 209 | 0C99777523200DE600EA0130 /* Base */, 210 | ); 211 | name = Main.storyboard; 212 | sourceTree = ""; 213 | }; 214 | 0C99777923200DE700EA0130 /* LaunchScreen.storyboard */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | 0C99777A23200DE700EA0130 /* Base */, 218 | ); 219 | name = LaunchScreen.storyboard; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXVariantGroup section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | 0C99777D23200DE700EA0130 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_ANALYZER_NONNULL = YES; 230 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 231 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 232 | CLANG_CXX_LIBRARY = "libc++"; 233 | CLANG_ENABLE_MODULES = YES; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | CLANG_ENABLE_OBJC_WEAK = YES; 236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_COMMA = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 249 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 251 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 252 | CLANG_WARN_STRICT_PROTOTYPES = YES; 253 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 254 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 255 | CLANG_WARN_UNREACHABLE_CODE = YES; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | CODE_SIGN_IDENTITY = "iPhone Developer"; 258 | COPY_PHASE_STRIP = NO; 259 | DEBUG_INFORMATION_FORMAT = dwarf; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | ENABLE_TESTABILITY = YES; 262 | GCC_C_LANGUAGE_STANDARD = gnu11; 263 | GCC_DYNAMIC_NO_PIC = NO; 264 | GCC_NO_COMMON_BLOCKS = YES; 265 | GCC_OPTIMIZATION_LEVEL = 0; 266 | GCC_PREPROCESSOR_DEFINITIONS = ( 267 | "DEBUG=1", 268 | "$(inherited)", 269 | ); 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 272 | GCC_WARN_UNDECLARED_SELECTOR = YES; 273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 274 | GCC_WARN_UNUSED_FUNCTION = YES; 275 | GCC_WARN_UNUSED_VARIABLE = YES; 276 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 277 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 278 | MTL_FAST_MATH = YES; 279 | ONLY_ACTIVE_ARCH = YES; 280 | SDKROOT = iphoneos; 281 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 282 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 283 | }; 284 | name = Debug; 285 | }; 286 | 0C99777E23200DE700EA0130 /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_ANALYZER_NONNULL = YES; 291 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_ENABLE_MODULES = YES; 295 | CLANG_ENABLE_OBJC_ARC = YES; 296 | CLANG_ENABLE_OBJC_WEAK = YES; 297 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_COMMA = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INFINITE_RECURSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 310 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 311 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 312 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 313 | CLANG_WARN_STRICT_PROTOTYPES = YES; 314 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 315 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | CODE_SIGN_IDENTITY = "iPhone Developer"; 319 | COPY_PHASE_STRIP = NO; 320 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 321 | ENABLE_NS_ASSERTIONS = NO; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu11; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 332 | MTL_ENABLE_DEBUG_INFO = NO; 333 | MTL_FAST_MATH = YES; 334 | SDKROOT = iphoneos; 335 | SWIFT_COMPILATION_MODE = wholemodule; 336 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 337 | VALIDATE_PRODUCT = YES; 338 | }; 339 | name = Release; 340 | }; 341 | 0C99778023200DE700EA0130 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | CODE_SIGN_STYLE = Automatic; 346 | DEVELOPMENT_TEAM = U6J5965DZK; 347 | INFOPLIST_FILE = liGame/Info.plist; 348 | LD_RUNPATH_SEARCH_PATHS = ( 349 | "$(inherited)", 350 | "@executable_path/Frameworks", 351 | ); 352 | PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.liGame; 353 | PRODUCT_NAME = "$(TARGET_NAME)"; 354 | SWIFT_VERSION = 5.0; 355 | TARGETED_DEVICE_FAMILY = "1,2"; 356 | }; 357 | name = Debug; 358 | }; 359 | 0C99778123200DE700EA0130 /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | CODE_SIGN_STYLE = Automatic; 364 | DEVELOPMENT_TEAM = U6J5965DZK; 365 | INFOPLIST_FILE = liGame/Info.plist; 366 | LD_RUNPATH_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "@executable_path/Frameworks", 369 | ); 370 | PRODUCT_BUNDLE_IDENTIFIER = com.pjhubs.liGame; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SWIFT_VERSION = 5.0; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | }; 375 | name = Release; 376 | }; 377 | /* End XCBuildConfiguration section */ 378 | 379 | /* Begin XCConfigurationList section */ 380 | 0C99776823200DE600EA0130 /* Build configuration list for PBXProject "liGame" */ = { 381 | isa = XCConfigurationList; 382 | buildConfigurations = ( 383 | 0C99777D23200DE700EA0130 /* Debug */, 384 | 0C99777E23200DE700EA0130 /* Release */, 385 | ); 386 | defaultConfigurationIsVisible = 0; 387 | defaultConfigurationName = Release; 388 | }; 389 | 0C99777F23200DE700EA0130 /* Build configuration list for PBXNativeTarget "liGame" */ = { 390 | isa = XCConfigurationList; 391 | buildConfigurations = ( 392 | 0C99778023200DE700EA0130 /* Debug */, 393 | 0C99778123200DE700EA0130 /* Release */, 394 | ); 395 | defaultConfigurationIsVisible = 0; 396 | defaultConfigurationName = Release; 397 | }; 398 | /* End XCConfigurationList section */ 399 | }; 400 | rootObject = 0C99776523200DE600EA0130 /* Project object */; 401 | } 402 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/project.xcworkspace/xcuserdata/wengpeijun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame.xcodeproj/project.xcworkspace/xcuserdata/wengpeijun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | liGame.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 41 | 53 | 54 | 55 | 57 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /liGame/liGame.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | liGame.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /liGame/liGame/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/4. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/01.imageset/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/01.imageset/01.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/01.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "01.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/AppIcon-1.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Line0@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line0.imageset/Line0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/Line0.imageset/Line0@2x.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Line1@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line1.imageset/Line1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/Line1.imageset/Line1@2x.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Line2@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line2.imageset/Line2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/Line2.imageset/Line2@2x.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Line3@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/Line3.imageset/Line3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/Line3.imageset/Line3@2x.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_1.imageset/created_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_1.imageset/created_1.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_10.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_10.imageset/created_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_10.imageset/created_10.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_11.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_11.imageset/created_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_11.imageset/created_11.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_12.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_12.imageset/created_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_12.imageset/created_12.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_13.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_13.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_13.imageset/created_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_13.imageset/created_13.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_14.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_14.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_14.imageset/created_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_14.imageset/created_14.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_15.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_15.imageset/created_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_15.imageset/created_15.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_16.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_16.imageset/created_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_16.imageset/created_16.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_17.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_17.imageset/created_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_17.imageset/created_1.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_18.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_18.imageset/created_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_18.imageset/created_3.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_2.imageset/created_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_2.imageset/created_2.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_3.imageset/created_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_3.imageset/created_3.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_4.imageset/created_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_4.imageset/created_4.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_5.imageset/created_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_5.imageset/created_5.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_6.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_6.imageset/created_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_6.imageset/created_6.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_7.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_7.imageset/created_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_7.imageset/created_7.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_8.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_8.imageset/created_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_8.imageset/created_8.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "created_9.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/created_9.imageset/created_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/created_9.imageset/created_9.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/finalMan.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "finalMan.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/finalMan.imageset/finalMan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/finalMan.imageset/finalMan.png -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/finalManContent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "finalMamContent.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /liGame/liGame/Assets.xcassets/finalManContent.imageset/finalMamContent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/liGame/liGame/Assets.xcassets/finalManContent.imageset/finalMamContent.png -------------------------------------------------------------------------------- /liGame/liGame/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /liGame/liGame/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /liGame/liGame/Extension/Animation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Animation.swift 3 | // WWDC19 4 | // 5 | // Created by PJHubs on 2019/3/20. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol PJParticleAnimationable {} 12 | 13 | extension PJParticleAnimationable where Self: UIViewController { 14 | func startParticleAnimation(_ point : CGPoint) { 15 | let emitter = CAEmitterLayer() 16 | emitter.emitterPosition = point 17 | emitter.preservesDepth = true 18 | 19 | var cells = [CAEmitterCell]() 20 | for i in 0..<20 { 21 | let cell = CAEmitterCell() 22 | cell.velocity = 150 23 | cell.velocityRange = 100 24 | cell.scale = 0.7 25 | cell.scaleRange = 0.3 26 | cell.emissionLongitude = CGFloat(-Double.pi / 2) 27 | cell.emissionRange = CGFloat(Double.pi / 2) 28 | cell.lifetime = 3 29 | cell.lifetimeRange = 1.5 30 | cell.spin = CGFloat(Double.pi / 2) 31 | cell.spinRange = CGFloat(Double.pi / 2 / 2) 32 | cell.birthRate = 2 33 | cell.contents = UIImage(named: "Line\(i)")?.cgImage 34 | 35 | cells.append(cell) 36 | } 37 | emitter.emitterCells = cells 38 | view.layer.addSublayer(emitter) 39 | } 40 | 41 | func stopParticleAnimation() { 42 | view.layer.sublayers?.filter({ $0.isKind(of: CAEmitterLayer.self)}).first?.removeFromSuperlayer() 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /liGame/liGame/Extension/Define.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Define.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/4. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// 屏幕宽 12 | let screenWidth = UIScreen.main.bounds.size.width 13 | /// 屏幕高 14 | let screenHeight = UIScreen.main.bounds.size.height 15 | /// 底部安全距离 16 | let bottomSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0.0 17 | ///顶部的安全距离 18 | let topSafeAreaHeight = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0.0 19 | /// 状态栏高度 20 | let statusBarHeight = UIApplication.shared.statusBarFrame.height; 21 | /// 导航栏高度 22 | let navigationBarHeight = CGFloat(44 + topSafeAreaHeight) 23 | 24 | -------------------------------------------------------------------------------- /liGame/liGame/Extension/UIColor+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/4. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | class func rgb(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> UIColor { 13 | return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: 1) 14 | } 15 | 16 | class func rgba(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat, _ a: CGFloat) -> UIColor { 17 | return UIColor(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: a) 18 | } 19 | 20 | static var bgColor: UIColor { 21 | return rgb(29, 36, 73) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /liGame/liGame/Extension/UIImage+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Extension.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/6. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | /// 通过原图获取 rect 大小的图片 13 | func image(with rect: CGRect) -> UIImage { 14 | let scale: CGFloat = 2 15 | let x = rect.origin.x * scale 16 | let y = rect.origin.y * scale 17 | let w = rect.size.width * scale 18 | let h = rect.size.height * scale 19 | let finalRect = CGRect(x: x, y: y, width: w, height: h) 20 | 21 | let originImageRef = self.cgImage 22 | let finanImageRef = originImageRef!.cropping(to: finalRect) 23 | let finanImage = UIImage(cgImage: finanImageRef!, scale: scale, orientation: .up) 24 | 25 | return finanImage 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /liGame/liGame/Extension/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/4. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | static private let PJSCREEN_SCALE = UIScreen.main.scale 14 | 15 | private func getPixintegral(pointValue: CGFloat) -> CGFloat { 16 | return round(pointValue * UIView.PJSCREEN_SCALE) / UIView.PJSCREEN_SCALE 17 | } 18 | 19 | public var x: CGFloat { 20 | get { 21 | return self.frame.origin.x 22 | } 23 | set(x) { 24 | self.frame = CGRect.init( 25 | x: getPixintegral(pointValue: x), 26 | y: self.y, 27 | width: self.width, 28 | height: self.height 29 | ) 30 | } 31 | } 32 | 33 | public var y: CGFloat { 34 | get { 35 | return self.frame.origin.y 36 | } 37 | set(y) { 38 | self.frame = CGRect.init( 39 | x: self.x, 40 | y: getPixintegral(pointValue: y), 41 | width: self.width, 42 | height: self.height 43 | ) 44 | } 45 | } 46 | 47 | public var width: CGFloat { 48 | get { 49 | return self.frame.size.width 50 | } 51 | set(width) { 52 | self.frame = CGRect.init( 53 | x: self.x, 54 | y: self.y, 55 | width: getPixintegral(pointValue: width), 56 | height: self.height 57 | ) 58 | } 59 | } 60 | 61 | public var height: CGFloat { 62 | get { 63 | return self.frame.size.height 64 | } 65 | set (height) { 66 | self.frame = CGRect.init( 67 | x: self.x, 68 | y: self.y, 69 | width: self.width, 70 | height: getPixintegral(pointValue: height) 71 | ) 72 | } 73 | } 74 | 75 | public var bottom: CGFloat { 76 | get { 77 | return self.y + self.height 78 | } 79 | set(bottom) { 80 | self.y = bottom - self.height 81 | } 82 | } 83 | 84 | public var right: CGFloat { 85 | get { 86 | return self.x + self.width 87 | } 88 | set (right) { 89 | self.x = right - self.width 90 | } 91 | } 92 | 93 | public var left: CGFloat { 94 | get { 95 | return self.x 96 | } 97 | set(left) { 98 | self.x = left 99 | } 100 | } 101 | 102 | public var top: CGFloat { 103 | get { 104 | return self.y 105 | } 106 | set(top) { 107 | self.y = top 108 | } 109 | } 110 | 111 | public var centerX: CGFloat { 112 | get { 113 | return self.center.x 114 | } 115 | set(centerX) { 116 | self.center = CGPoint.init( 117 | x: getPixintegral(pointValue: centerX), 118 | y: self.center.y 119 | ) 120 | } 121 | } 122 | 123 | public var centerY: CGFloat { 124 | get { 125 | return self.center.y 126 | } 127 | set (centerY) { 128 | self.center = CGPoint.init(x: self.center.x, y: getPixintegral(pointValue: centerY)) 129 | } 130 | } 131 | 132 | } 133 | 134 | extension UIView { 135 | func insertRoundingCorners() { 136 | let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 8.0, height: 8.0)) 137 | let pathMaskLayer = CAShapeLayer() 138 | pathMaskLayer.frame = bounds 139 | pathMaskLayer.path = path.cgPath 140 | layer.mask = pathMaskLayer 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /liGame/liGame/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /liGame/liGame/View/Bottom/LiBottomCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiBottomCollectionView.swift 3 | // liGame 4 | // 5 | // Created by pjhubs on 2019/9/8. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LiBottomCollectionView: UICollectionView { 12 | 13 | var longTapBegan: ((Puzzle, CGPoint) -> ())? 14 | var longTapChange: ((CGPoint) -> ())? 15 | var longTapEnded: (() -> ())? 16 | 17 | 18 | let cellIdentifier = "PJLineCollectionViewCell" 19 | var viewModels = [Puzzle]() 20 | 21 | override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { 22 | super.init(frame: frame, collectionViewLayout: layout) 23 | initView() 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | super.init(coder: aDecoder) 28 | } 29 | 30 | private func initView() { 31 | backgroundColor = .clear 32 | showsHorizontalScrollIndicator = false 33 | isPagingEnabled = true 34 | // isUserInteractionEnabled = true 35 | dataSource = self 36 | 37 | register(LiBottomCollectionViewCell.self, forCellWithReuseIdentifier: "LiBottomCollectionViewCell") 38 | } 39 | } 40 | 41 | extension LiBottomCollectionView: UICollectionViewDataSource { 42 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 43 | return viewModels.count 44 | } 45 | 46 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 47 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LiBottomCollectionViewCell", for: indexPath) as! LiBottomCollectionViewCell 48 | cell.cellIndex = indexPath.row 49 | cell.gameIndex = viewModels[indexPath.row].tag 50 | cell.viewModel = viewModels[indexPath.row] 51 | cell.longTapBegan = { [weak self] index in 52 | guard let self = self else { return } 53 | guard self.viewModels.count != 0 else { return } 54 | self.longTapBegan?(self.viewModels[index], cell.center) 55 | } 56 | cell.longTapChange = { 57 | self.longTapChange?($0) 58 | } 59 | cell.longTapEnded = { 60 | self.viewModels.remove(at: $0) 61 | self.reloadData() 62 | 63 | self.longTapEnded?() 64 | } 65 | 66 | return cell 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /liGame/liGame/View/Bottom/LiBottomCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiBottomCollectionViewCell.swift 3 | // liGame 4 | // 5 | // Created by pjhubs on 2019/9/8. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LiBottomCollectionViewCell: UICollectionViewCell { 12 | var longTapBegan: ((Int) -> ())? 13 | var longTapChange: ((CGPoint) -> ())? 14 | var longTapEnded: ((Int) -> ())? 15 | 16 | var cellIndex: Int? 17 | var gameIndex: Int? 18 | 19 | private var img = UIImageView() 20 | private var tipLabel = UILabel() 21 | 22 | 23 | var viewModel: Puzzle? { 24 | didSet { setViewModel() } 25 | } 26 | 27 | override init(frame: CGRect) { 28 | super.init(frame: frame) 29 | 30 | layer.borderWidth = 1 31 | layer.borderColor = UIColor.darkGray.cgColor 32 | layer.shadowColor = UIColor.black.cgColor 33 | layer.shadowRadius = 10 34 | layer.shadowOffset = CGSize.zero 35 | layer.shadowOpacity = 1 36 | 37 | img.contentMode = .scaleAspectFit 38 | img.frame = CGRect(x: 0, y: 0, width: width, height: height) 39 | addSubview(img) 40 | 41 | 42 | tipLabel = UILabel(frame: CGRect(x: width - 10, y: top - 10, width: 17, height: 17)) 43 | tipLabel.font = UIFont.systemFont(ofSize: 11) 44 | tipLabel.backgroundColor = UIColor.rgb(80, 80, 80) 45 | tipLabel.textColor = .white 46 | tipLabel.textAlignment = .center 47 | tipLabel.layer.cornerRadius = tipLabel.width / 2 48 | tipLabel.layer.masksToBounds = true 49 | addSubview(tipLabel) 50 | 51 | 52 | let longTapGesture = UILongPressGestureRecognizer(target: self, action: .longTap) 53 | addGestureRecognizer(longTapGesture) 54 | } 55 | 56 | required init?(coder: NSCoder) { 57 | fatalError("init(coder:) has not been implemented") 58 | } 59 | 60 | private func setViewModel() { 61 | img.image = viewModel?.image 62 | tipLabel.text = "\(gameIndex!)" 63 | } 64 | } 65 | 66 | extension LiBottomCollectionViewCell { 67 | @objc 68 | fileprivate func longTap(_ longTapGesture: UILongPressGestureRecognizer) { 69 | guard let cellIndex = cellIndex else { return } 70 | 71 | switch longTapGesture.state { 72 | case .began: 73 | longTapBegan?(cellIndex) 74 | case .changed: 75 | var translation = longTapGesture.location(in: superview) 76 | 77 | let itemCount = 5 78 | if cellIndex > itemCount { 79 | translation.x = translation.x - CGFloat(cellIndex / itemCount * Int(screenWidth)) 80 | } 81 | 82 | let point = CGPoint(x: translation.x, y: translation.y) 83 | longTapChange?(point) 84 | case .ended: 85 | longTapEnded?(cellIndex) 86 | default: break 87 | } 88 | } 89 | } 90 | 91 | private extension Selector { 92 | static let longTap = #selector(LiBottomCollectionViewCell.longTap(_:)) 93 | } 94 | -------------------------------------------------------------------------------- /liGame/liGame/View/Bottom/LiBottomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiBottomView.swift 3 | // liGame 4 | // 5 | // Created by pjhubs on 2019/9/8. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LiBottomView: UIView { 12 | var viewModels = [Puzzle]() { 13 | didSet { collectionView!.viewModels = viewModels } 14 | } 15 | 16 | var moveBegin: ((Puzzle) -> Void)? 17 | var moveChanged: ((CGPoint) -> Void)? 18 | var moveEnd: ((Puzzle) -> Void)? 19 | 20 | var tempPuzzle: Puzzle? 21 | var collectionView: LiBottomCollectionView? 22 | var longPressView: UIView? 23 | 24 | private var rightPoint: CGFloat = 0 25 | private var leftaPoint: CGFloat = 0 26 | private var topPoint: CGFloat = 0 27 | private var bottomPoint: CGFloat = 0 28 | 29 | public override init(frame: CGRect) { 30 | super.init(frame: frame) 31 | } 32 | 33 | convenience init(frame: CGRect, longPressView: UIView?) { 34 | self.init(frame: frame) 35 | self.longPressView = self 36 | initView() 37 | } 38 | 39 | required init?(coder aDecoder: NSCoder) { 40 | super.init(coder: aDecoder) 41 | } 42 | 43 | private func initView() { 44 | backgroundColor = .clear 45 | 46 | topPoint = topSafeAreaHeight 47 | bottomPoint = screenHeight - bottomSafeAreaHeight 48 | rightPoint = screenWidth / 2 49 | leftaPoint = 0 50 | 51 | let effect = UIBlurEffect(style: .extraLight) 52 | let effectView = UIVisualEffectView(effect: effect) 53 | effectView.frame = CGRect(x: 0, y: 0, width: width, height: height) 54 | addSubview(effectView) 55 | 56 | insertRoundingCorners() 57 | 58 | let collectionViewLayout = UICollectionViewFlowLayout() 59 | let itemW = 50 60 | let innerW = (screenWidth - 5 * 50) / 5 61 | collectionViewLayout.itemSize = CGSize(width: itemW , height: itemW) 62 | collectionViewLayout.minimumLineSpacing = innerW 63 | collectionViewLayout.minimumInteritemSpacing = 10 64 | collectionViewLayout.scrollDirection = .horizontal 65 | collectionViewLayout.sectionInset = UIEdgeInsets.init(top: 0, left: innerW / 2, bottom: 0, right: innerW / 2) 66 | 67 | 68 | collectionView = LiBottomCollectionView(frame: CGRect(x: 0, y: 0, width: width, height: height), collectionViewLayout: collectionViewLayout) 69 | addSubview(collectionView!) 70 | collectionView!.longTapBegan = { 71 | let center = $1 72 | let tempPuzzle = Puzzle(size: $0.frame.size, isCopy: false) 73 | // 补上游戏索引 tag 74 | tempPuzzle.tag = $0.tag 75 | tempPuzzle.image = $0.image 76 | tempPuzzle.center = center 77 | tempPuzzle.y += self.top 78 | self.tempPuzzle = tempPuzzle 79 | 80 | self.moveBegin?(tempPuzzle) 81 | } 82 | collectionView!.longTapChange = { 83 | guard let tempPuzzle = self.tempPuzzle else { return } 84 | tempPuzzle.center = CGPoint(x: $0.x, y: $0.y + self.top) 85 | 86 | if tempPuzzle.right > self.rightPoint { 87 | tempPuzzle.right = self.rightPoint 88 | } 89 | if tempPuzzle.left < self.leftaPoint { 90 | tempPuzzle.left = self.leftaPoint 91 | } 92 | if tempPuzzle.top < self.topPoint { 93 | tempPuzzle.top = self.topPoint 94 | } 95 | if tempPuzzle.bottom > self.bottomPoint { 96 | tempPuzzle.bottom = self.bottomPoint 97 | } 98 | 99 | self.moveChanged?(tempPuzzle.center) 100 | } 101 | collectionView!.longTapEnded = { 102 | guard let tempPuzzle = self.tempPuzzle else { return } 103 | self.moveEnd?(tempPuzzle) 104 | } 105 | } 106 | } 107 | 108 | extension LiBottomView { 109 | @objc 110 | fileprivate func pan(_ panGesture: UIPanGestureRecognizer) { 111 | let translation = panGesture.translation(in: superview) 112 | panGesture.view!.center = CGPoint(x: panGesture.view!.center.x + translation.x, 113 | y: panGesture.view!.center.y + translation.y) 114 | panGesture.setTranslation(.zero, in: superview) 115 | } 116 | } 117 | 118 | private extension Selector { 119 | static let pan = #selector(LiBottomView.pan(_:)) 120 | } 121 | -------------------------------------------------------------------------------- /liGame/liGame/View/Puzzle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Puzzle.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/6. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Puzzle: UIImageView { 12 | 13 | var panChange: ((CGPoint) -> ())? 14 | var panEnded: (() -> ())? 15 | var beginMovedPoint = CGPoint() 16 | // 当前在 X 轴上的位置 17 | var Xindex: Int? 18 | // 当前在 Y 轴上的位置 19 | var Yindex: Int? 20 | 21 | /// 是否为「拷贝」拼图元素 22 | private var isCopy = false 23 | private var rightPoint: CGFloat = 0 24 | private var leftaPoint: CGFloat = 0 25 | private var topPoint: CGFloat = 0 26 | private var bottomPoint: CGFloat = 0 27 | 28 | 29 | override init(frame: CGRect) { 30 | super.init(frame: frame) 31 | } 32 | 33 | required init?(coder: NSCoder) { 34 | fatalError("init(coder:) has not been implemented") 35 | } 36 | 37 | convenience init(size: CGSize, isCopy: Bool) { 38 | self.init(frame: CGRect(x: -1000, y: -1000, width: size.width, height: size.height)) 39 | self.isCopy = isCopy 40 | 41 | initView() 42 | } 43 | 44 | 45 | // MARK: Init 46 | 47 | private func initView() { 48 | // 全部靠左,copyPuzzle 镜像对称 49 | contentMode = .left 50 | 51 | if !isCopy { 52 | isUserInteractionEnabled = true 53 | 54 | let panGesture = UIPanGestureRecognizer(target: self, action: .pan) 55 | self.addGestureRecognizer(panGesture) 56 | } else { 57 | transform = CGAffineTransform(scaleX: -1, y: 1) 58 | } 59 | } 60 | 61 | /// 更新边界 62 | func updateEdge() { 63 | if superview != nil { 64 | if !isCopy { 65 | topPoint = topSafeAreaHeight 66 | bottomPoint = superview!.bottom - bottomSafeAreaHeight 67 | rightPoint = superview!.width / 2 68 | leftaPoint = 0 69 | } 70 | } else { 71 | if superview != nil { 72 | topPoint = superview!.top 73 | bottomPoint = superview!.bottom 74 | rightPoint = superview!.width 75 | leftaPoint = superview!.width / 2 76 | } 77 | } 78 | } 79 | 80 | /// 移动 `rightPuzzle` 81 | func copyPuzzleCenterChange(centerPoint: CGPoint) { 82 | if !isCopy { return } 83 | center = CGPoint(x: screenWidth - centerPoint.x, y: centerPoint.y) 84 | } 85 | 86 | } 87 | 88 | 89 | extension Puzzle { 90 | @objc 91 | fileprivate func pan(_ panGesture: UIPanGestureRecognizer) { 92 | let translation = panGesture.translation(in: superview) 93 | 94 | switch panGesture.state { 95 | case .began: 96 | beginMovedPoint = center 97 | layer.borderColor = UIColor.white.cgColor 98 | layer.borderWidth = 1 99 | case .changed: 100 | if right > rightPoint { 101 | right = rightPoint 102 | } 103 | if left < leftaPoint { 104 | left = leftaPoint 105 | } 106 | if top < topPoint { 107 | top = topPoint 108 | } 109 | if bottom > bottomPoint { 110 | bottom = bottomPoint 111 | } 112 | 113 | case .ended: 114 | layer.borderWidth = 0 115 | self.panEnded?() 116 | default: break 117 | } 118 | 119 | center = CGPoint(x: center.x + translation.x, y: center.y + translation.y) 120 | panGesture.setTranslation(.zero, in: superview) 121 | 122 | panChange?(center) 123 | } 124 | } 125 | 126 | private extension Selector { 127 | static let pan = #selector(Puzzle.pan(_:)) 128 | } 129 | -------------------------------------------------------------------------------- /liGame/liGame/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // liGame 4 | // 5 | // Created by 翁培钧 on 2019/9/4. 6 | // Copyright © 2019 翁培钧. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, PJParticleAnimationable { 12 | 13 | private var lineImageView = UIImageView() 14 | private var bottomView = LiBottomView() 15 | 16 | private var puzzles = [Puzzle]() 17 | private var defaultPuzzles = [Puzzle]() 18 | private var finalPuzzleTags = [[Int]]() 19 | private var leftPuzzles = [Puzzle]() 20 | private var rightPuzzles = [Puzzle]() 21 | 22 | private var tempCopyPuzzle: Puzzle? 23 | 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | view.backgroundColor = .bgColor 28 | self.winLabel.isHidden = true 29 | 30 | 31 | // 底图适配 32 | let contentImage = UIImage(named: "01")! 33 | let contentImageScale = view.width / contentImage.size.width 34 | let contentImageViewHeight = contentImage.size.height * contentImageScale 35 | 36 | let contentImageView = UIImageView(frame: CGRect(x: 0, y: topSafeAreaHeight, width: view.width, height: contentImageViewHeight)) 37 | contentImageView.image = contentImage 38 | 39 | let imgView = UIImageView(frame: CGRect(x: view.width / 2, y: topSafeAreaHeight, width: 5, height: view.height - bottomSafeAreaHeight)) 40 | view.addSubview(imgView) 41 | UIGraphicsBeginImageContext(imgView.frame.size) // 位图上下文绘制区域 42 | imgView.image?.draw(in: imgView.bounds) 43 | lineImageView = imgView 44 | 45 | let context:CGContext = UIGraphicsGetCurrentContext()! 46 | context.setLineCap(CGLineCap.square) 47 | context.setStrokeColor(UIColor.white.cgColor) 48 | context.setLineWidth(3) 49 | context.setLineDash(phase: 0, lengths: [10,20]) 50 | context.move(to: CGPoint(x: 0, y: 0)) 51 | context.addLine(to: CGPoint(x: 0, y: view.height)) 52 | context.strokePath() 53 | 54 | imgView.image = UIGraphicsGetImageFromCurrentImageContext() 55 | 56 | // 一行六个 57 | let itemHCount = 3 58 | let itemW = Int(view.width / CGFloat(itemHCount * 2)) 59 | let itemH = itemW 60 | let itemVCount = Int(contentImageView.height / CGFloat(itemW)) 61 | 62 | finalPuzzleTags = Array(repeating: Array(repeating: -1, count: itemHCount), count: itemVCount) 63 | 64 | for itemY in 0.. 0 { 168 | tempPuzzleXIndex += 1 169 | } 170 | 171 | var tempPuzzleYIndex = CGFloat(Int(tempPuzzleCenterPoint.y / tempPuzzle.height)) 172 | if Int(tempPuzzleCenterPoint.y) % Int(tempPuzzle.height) > 0 { 173 | tempPuzzleYIndex += 1 174 | } 175 | 176 | 177 | let Xedge = tempPuzzleXIndex * tempPuzzle.width 178 | let Yedge = tempPuzzleYIndex * tempPuzzle.height 179 | 180 | if tempPuzzleCenterPoint.x < Xedge { 181 | tempPuzzleCenterPoint.x = Xedge - tempPuzzle.width / 2 182 | } 183 | 184 | if tempPuzzleCenterPoint.y < Yedge { 185 | // 当为最后一列时,往上移 20 186 | if (Int(tempPuzzleYIndex) == finalPuzzleTags.count) { 187 | tempPuzzleCenterPoint.y = Yedge - tempPuzzle.height / 2 - 20 188 | } else { 189 | tempPuzzleCenterPoint.y = Yedge - tempPuzzle.height / 2 190 | } 191 | } 192 | 193 | // 超出最下边 194 | if (Int(tempPuzzleYIndex) > self.finalPuzzleTags.count) { 195 | tempPuzzle.center = tempPuzzle.beginMovedPoint 196 | } 197 | 198 | // 已经有的不能占据 199 | if (self.finalPuzzleTags[Int(tempPuzzleYIndex - 1)][Int(tempPuzzleXIndex - 1)] == -1) { 200 | self.finalPuzzleTags[Int(tempPuzzleYIndex - 1)][Int(tempPuzzleXIndex - 1)] = tempPuzzle.tag 201 | 202 | 203 | if ((tempPuzzle.Xindex != nil) && (tempPuzzle.Yindex != nil)) { 204 | self.finalPuzzleTags[tempPuzzle.Xindex!][tempPuzzle.Yindex!] = -1 205 | } 206 | 207 | tempPuzzle.Xindex = Int(tempPuzzleYIndex - 1) 208 | tempPuzzle.Yindex = Int(tempPuzzleXIndex - 1) 209 | 210 | tempPuzzle.center = tempPuzzleCenterPoint 211 | } else { 212 | tempPuzzle.center = tempPuzzle.beginMovedPoint 213 | } 214 | } 215 | 216 | /// 判赢算法 217 | private func isWin() -> Bool { 218 | 219 | var winCount = 0 220 | for (Vindex, HTags) in self.finalPuzzleTags.enumerated() { 221 | for (Hindex, tag) in HTags.enumerated() { 222 | let currentIndex = Vindex * 3 + Hindex 223 | if defaultPuzzles.count - 1 >= currentIndex { 224 | if defaultPuzzles[currentIndex].tag == tag { 225 | winCount += 1 226 | continue 227 | } 228 | } 229 | 230 | return false 231 | } 232 | } 233 | 234 | if winCount == defaultPuzzles.count { 235 | return true 236 | } 237 | return false 238 | } 239 | 240 | 241 | private func winAnimate() { 242 | startParticleAnimation(CGPoint(x: screenWidth / 2, y: screenHeight - 10)) 243 | 244 | UIView.animate(withDuration: 0.25, animations: { 245 | self.bottomView.top = screenHeight 246 | }) 247 | 248 | for sv in self.view.subviews { 249 | sv.removeFromSuperview() 250 | } 251 | 252 | self.winLabel.isHidden = false 253 | let finalManContentView = UIImageView(frame: CGRect(x: 0, y: 0, 254 | width: screenWidth, 255 | height: screenHeight - 64)) 256 | finalManContentView.image = UIImage(named: "finalManContent") 257 | self.view.addSubview(finalManContentView) 258 | 259 | let finalMan = UIImageView(frame: CGRect(x: 0, y: 0, 260 | width: finalManContentView.width * 0.85, 261 | height: finalManContentView.width * 0.8 * 0.85)) 262 | finalMan.center = self.view.center 263 | finalMan.image = UIImage(named: "finalMan") 264 | self.view.addSubview(finalMan) 265 | 266 | 267 | UIView.animate(withDuration: 0.5, animations: { 268 | finalMan.transform = CGAffineTransform(rotationAngle: 0.25) 269 | }) { (finished) in 270 | UIView.animate(withDuration: 0.5, animations: { 271 | finalMan.transform = CGAffineTransform(rotationAngle: -0.25) 272 | }, completion: { (finished) in 273 | UIView.animate(withDuration: 0.5, animations: { 274 | finalMan.transform = CGAffineTransform(rotationAngle: 0) 275 | }) 276 | }) 277 | } 278 | } 279 | 280 | 281 | // MARK: - Lazy 282 | lazy var winLabel: UILabel = { 283 | let label = UILabel(frame: CGRect(x: 0, y: screenHeight - 64, 284 | width: screenWidth, height: 64)) 285 | label.centerX = view.centerX 286 | label.font = UIFont.systemFont(ofSize: 40, 287 | weight: UIFont.Weight.light) 288 | label.text = "Dàlì shén" 289 | label.textAlignment = .center 290 | label.textColor = .white 291 | view.addSubview(label) 292 | return label 293 | }() 294 | } 295 | -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/lightGame/light-game.xcodeproj/project.xcworkspace/xcuserdata/pjhubs.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/project.xcworkspace/xcuserdata/wengpeijun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/SwiftGame/d2f4eefc9e285601902c424783efde51b48749dd/lightGame/light-game.xcodeproj/project.xcworkspace/xcuserdata/wengpeijun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 41 | 53 | 54 | 55 | 57 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/xcuserdata/pjhubs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | light-game.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | watchapp.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /lightGame/light-game.xcodeproj/xcuserdata/wengpeijun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | light-game.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lightGame/light-game/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // light-game 4 | // 5 | // Created by pjhubs on 2019/8/29. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /lightGame/light-game/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /lightGame/light-game/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /lightGame/light-game/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lightGame/light-game/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // light-game 4 | // 5 | // Created by pjhubs on 2019/8/29. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | @ObservedObject var gameManager = GameManager(size: 5, lightSequence: [1, 2, 3]) 13 | 14 | @State var isShowHistory = false 15 | 16 | var body: some View { 17 | VStack { 18 | HStack { 19 | Text("\(gameManager.timeString)") 20 | .font(.system(size: 45)) 21 | 22 | Spacer() 23 | 24 | Text("\(gameManager.clickTimes)步") 25 | .font(.system(size: 45)) 26 | 27 | } 28 | .padding(20) 29 | 30 | ForEach(0.. 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /lightGame/light-game/Model/GameController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameController.swift 3 | // light-game 4 | // 5 | // Created by 翁培钧 on 2019/11/30. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import GameController 10 | 11 | class GameController { 12 | 13 | var movingX = false 14 | var movingY = false 15 | 16 | var isSelectX: ((Bool) -> ())? 17 | var isSelectY: ((Bool) -> ())? 18 | var isTapButtonA: (() -> ())? 19 | var isTapButtonB: (() -> ())? 20 | 21 | init() { 22 | NotificationCenter.default.addObserver(self, selector: .didConnect, name: .GCControllerDidConnect, object: nil) 23 | NotificationCenter.default.addObserver(self, selector: .didConnect, name: .GCControllerDidDisconnect, object: nil) 24 | } 25 | } 26 | 27 | 28 | extension GameController { 29 | @objc fileprivate func gameControllerDidConnect() { 30 | for controller in GCController.controllers() { 31 | if controller.extendedGamepad != nil { 32 | setupControllerControls(controller: controller) 33 | } 34 | } 35 | } 36 | 37 | @objc fileprivate func gameControllerDidDisconnect() { 38 | 39 | } 40 | 41 | func setupControllerControls(controller: GCController) { 42 | controller.extendedGamepad?.valueChangedHandler = { 43 | (gamepad: GCExtendedGamepad, element: GCControllerElement) in 44 | self.controllerInput(gamePad: gamepad, element: element) 45 | } 46 | } 47 | 48 | private func controllerInput(gamePad: GCExtendedGamepad, element: GCControllerElement) { 49 | // gamePad 为支持的类型,element 为当前手柄的输入 50 | // 不能 return,因为单次出发会带入多个按键的值 51 | 52 | if (gamePad.leftThumbstick == element) { 53 | if (gamePad.leftThumbstick.yAxis.value != 0 && !movingY && !movingX) { 54 | movingY = true 55 | isSelectY?(gamePad.leftThumbstick.yAxis.value > 0) 56 | } else if (gamePad.leftThumbstick.yAxis.value == 0) { 57 | movingY = false 58 | } 59 | 60 | if (gamePad.leftThumbstick.xAxis.value != 0 && !movingX && !movingY) { 61 | isSelectX?(gamePad.leftThumbstick.xAxis.value > 0) 62 | movingX = true 63 | } else if (gamePad.leftThumbstick.xAxis.value == 0) { 64 | movingX = false 65 | } 66 | } else if (gamePad.rightThumbstick == element) { 67 | if (gamePad.rightThumbstick.xAxis.value != 0) { 68 | print("rightThumbstickXAxis: \(gamePad.rightThumbstick.xAxis)") 69 | } 70 | } else if (gamePad.dpad == element) { 71 | if (gamePad.dpad.xAxis.value != 0) { 72 | isSelectX?(gamePad.dpad.xAxis.value > 0) 73 | } else if (gamePad.dpad.xAxis.value == 0) { 74 | 75 | } 76 | 77 | if (gamePad.dpad.yAxis.value != 0) { 78 | isSelectY?(gamePad.dpad.yAxis.value > 0) 79 | } else if (gamePad.dpad.yAxis.value == 0) { 80 | 81 | } 82 | } else if (gamePad.buttonA == element) { 83 | if (gamePad.buttonA.value != 0) { 84 | isTapButtonA?() 85 | } 86 | } else if (gamePad.buttonB == element) { 87 | if (gamePad.buttonB.value != 0) { 88 | isTapButtonB?() 89 | } 90 | } else if (gamePad.buttonY == element) { 91 | if (gamePad.buttonY.value != 0) { 92 | // print("Y-Button Pressed!") 93 | // buttonYTap = true 94 | } 95 | } else if (gamePad.buttonX == element) { 96 | if (gamePad.buttonX.value != 0) { 97 | // print("X-Button Pressed!") 98 | // buttonXTap = true 99 | } 100 | } 101 | } 102 | } 103 | 104 | private extension Selector { 105 | static let didConnect = #selector(GameController.gameControllerDidConnect) 106 | static let didDisConnect = #selector(GameController.gameControllerDidDisconnect) 107 | } 108 | -------------------------------------------------------------------------------- /lightGame/light-game/Model/GameManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameManager.swift 3 | // light-game 4 | // 5 | // Created by pjhubs on 2019/8/30. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | class GameManager: ObservableObject { 13 | /// 对外发布的格式化计时器字符串 14 | @Published var timeString = "00:00" 15 | /// 点击次数 16 | @Published var clickTimes = 0 17 | /// 灯状态 18 | @Published var lights = [[Light]]() 19 | @Published var isWin = false 20 | /// 当前游戏状态 21 | private var currentStatus: GameStatus = .during { 22 | didSet { 23 | switch currentStatus { 24 | case .win: isWin = true 25 | case .lose: isWin = false 26 | case .during: break 27 | } 28 | } 29 | } 30 | 31 | /// 游戏尺寸大小 32 | private(set) var size: Int? 33 | /// 游戏计时器 34 | private var timer: Timer? 35 | /// 游戏持续时间 36 | private var durations = 1 37 | private var gameController = GameController() 38 | private var row = 0 39 | private var column = 0 40 | private var pRow = 0 41 | private var pColumn = 0 42 | 43 | /// 游戏控制器暂停 44 | private var isPause = false 45 | 46 | // MARK: - Init 47 | 48 | /// 便捷构造方法 49 | /// - Parameters: 50 | /// - size: 游戏布局尺寸,默认值 5x5 51 | /// - lightSequence: 亮灯序列,默认全灭 52 | convenience init(size: Int = 5, 53 | lightSequence: [Int] = [Int]()) { 54 | 55 | self.init() 56 | 57 | var size = size 58 | // 太大了不好玩 59 | if size > 8 { 60 | size = 7 61 | } 62 | // 太小了没意思 63 | if size < 2 { 64 | size = 2 65 | } 66 | self.size = size 67 | lights = Array(repeating: Array(repeating: Light(), count: size), count: size) 68 | 69 | initGameController() 70 | start(lightSequence) 71 | } 72 | 73 | // MARK: Public 74 | 75 | /// 游戏配置 76 | /// - Parameter lightSequence: 亮灯序列 77 | func start(_ lightSequence: [Int]) { 78 | currentStatus = .during 79 | clickTimes = 0 80 | updateLightStatus(lightSequence) 81 | 82 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in 83 | let min = self.durations >= 60 ? self.durations / 60 : 0 84 | let seconds = self.durations - min * 60 85 | 86 | 87 | let minString = min >= 10 ? "\(min)" : "0\(min)" 88 | let secondString = self.durations - min * 60 >= 10 ? "\(seconds)" : "0\(seconds)" 89 | self.timeString = minString + ":" + secondString 90 | 91 | self.durations += 1 92 | }) 93 | } 94 | 95 | /// 停止 96 | func timerStop() { 97 | timer?.fireDate = Date.distantFuture 98 | } 99 | 100 | /// 重新创建 101 | func timerRestart() { 102 | isPause = false 103 | durations = 0 104 | timeString = "00:00" 105 | timer?.fireDate = Date() 106 | } 107 | 108 | /// 获取灯的尺寸 109 | func circleWidth() -> CGFloat { 110 | guard let size = size else { return 0 } 111 | 112 | /// 距离屏幕左右两边的间距 113 | let padding: CGFloat = 20 114 | /// 左右两灯之间的间距 115 | let innerSpacing: CGFloat = 20 116 | 117 | var circleWidth = (UIScreen.main.bounds.width - padding - (CGFloat(size) * innerSpacing)) / CGFloat(size) 118 | 119 | // 太大了会很丑,过滤下 120 | if circleWidth > UIScreen.main.bounds.width / 5 { 121 | circleWidth = UIScreen.main.bounds.width / 5 122 | } 123 | 124 | return circleWidth 125 | } 126 | 127 | /// 通过坐标索引修改灯状态 128 | /// - Parameters: 129 | /// - column: 灯-列索引 130 | /// - size: 灯-行索引 131 | func updateLightStatus(column: Int, row: Int, userTouch: Bool) { 132 | lights[row][column].status.toggle() 133 | self.column = row 134 | self.row = column 135 | 136 | // 上 137 | let top = row - 1 138 | if !(top < 0) { 139 | lights[top][column].status.toggle() 140 | } 141 | // 下 142 | let bottom = row + 1 143 | if !(bottom > lights.count - 1) { 144 | lights[bottom][column].status.toggle() 145 | } 146 | // 左 147 | let left = column - 1 148 | if !(left < 0) { 149 | lights[row][left].status.toggle() 150 | } 151 | // 右 152 | let right = column + 1 153 | if !(right > lights.count - 1) { 154 | lights[row][right].status.toggle() 155 | } 156 | 157 | updateGameStatus() 158 | 159 | if (userTouch) { 160 | disSelectedGameControllerStatus() 161 | } 162 | } 163 | 164 | // MARK: Private 165 | 166 | /// 通过亮灯序列修改灯状态 167 | /// - Parameter lightSequence: 亮灯序列 168 | private func updateLightStatus(_ lightSequence: [Int]) { 169 | guard let size = size else { return } 170 | for lightIndex in lightSequence { 171 | var index = lightIndex 172 | // 防止数组越出最大边界处 173 | if index >= size * size { 174 | index = size * size - 1 175 | } 176 | let row = lightIndex / size 177 | let column = lightIndex % size 178 | // column 不为 0,说明非最后一个 179 | // row 为 0,说明为第一行 180 | updateLightStatus(column: column, row: row, userTouch: true) 181 | } 182 | } 183 | 184 | private func updateLightSelected() { 185 | lights[pColumn][pRow].selected = false 186 | lights[column][row].selected = true 187 | 188 | pRow = row 189 | pColumn = column 190 | } 191 | 192 | private func disSelectedGameControllerStatus() { 193 | lights[pColumn][pRow].selected = false 194 | } 195 | 196 | /// 判赢 197 | private func updateGameStatus() { 198 | guard let size = size else { return } 199 | 200 | var lightingCount = 0 201 | 202 | 203 | for lightArr in lights { 204 | for light in lightArr { 205 | if light.status { lightingCount += 1 } 206 | } 207 | } 208 | 209 | if lightingCount == size * size { 210 | timerStop() 211 | currentStatus = .lose 212 | return 213 | } 214 | 215 | if lightingCount == 0 { 216 | timerStop() 217 | currentStatus = .win 218 | isPause = true 219 | return 220 | } 221 | } 222 | 223 | private func initGameController() { 224 | gameController.isSelectX = { 225 | guard !self.isPause else { return } 226 | 227 | if $0 { 228 | if (self.row < self.lights.count - 1) { 229 | self.row += 1 230 | } 231 | } else { 232 | if self.row > 0 { 233 | self.row -= 1 234 | } 235 | } 236 | 237 | self.updateLightSelected() 238 | } 239 | 240 | gameController.isSelectY = { 241 | guard !self.isPause else { return } 242 | 243 | if $0 { 244 | if (self.column > 0) { 245 | self.column -= 1 246 | } 247 | } else { 248 | if (self.column < self.lights.count - 1) { 249 | self.column += 1 250 | } 251 | } 252 | 253 | self.updateLightSelected() 254 | } 255 | 256 | gameController.isTapButtonA = { 257 | self.clickTimes += 1 258 | self.updateLightStatus(column: self.row, row: self.column, userTouch: false) 259 | } 260 | } 261 | } 262 | 263 | extension GameManager { 264 | enum GameStatus { 265 | /// 赢 266 | case win 267 | /// 输 268 | case lose 269 | /// 进行中 270 | case during 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /lightGame/light-game/Model/Light.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Light.swift 3 | // light-game 4 | // 5 | // Created by pjhubs on 2019/8/29. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Light { 12 | /// 开关状态 13 | var status = false 14 | 15 | /// 选中状态 16 | var selected = false 17 | } 18 | -------------------------------------------------------------------------------- /lightGame/light-game/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /lightGame/light-game/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // light-game 4 | // 5 | // Created by pjhubs on 2019/8/29. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | 22 | // Create the SwiftUI view that provides the window contents. 23 | #if !os(watchOS) 24 | let contentView = ContentView() 25 | #else 26 | let contentView = WatchContentView() 27 | #endif 28 | 29 | // Use a UIHostingController as window root view controller. 30 | if let windowScene = scene as? UIWindowScene { 31 | let window = UIWindow(windowScene: windowScene) 32 | window.rootViewController = UIHostingController(rootView: contentView) 33 | self.window = window 34 | window.makeKeyAndVisible() 35 | } 36 | } 37 | 38 | func sceneDidDisconnect(_ scene: UIScene) { 39 | // Called as the scene is being released by the system. 40 | // This occurs shortly after the scene enters the background, or when its session is discarded. 41 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 42 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 43 | } 44 | 45 | func sceneDidBecomeActive(_ scene: UIScene) { 46 | // Called when the scene has moved from an inactive state to an active state. 47 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 48 | } 49 | 50 | func sceneWillResignActive(_ scene: UIScene) { 51 | // Called when the scene will move from an active state to an inactive state. 52 | // This may occur due to temporary interruptions (ex. an incoming phone call). 53 | } 54 | 55 | func sceneWillEnterForeground(_ scene: UIScene) { 56 | // Called as the scene transitions from the background to the foreground. 57 | // Use this method to undo the changes made on entering the background. 58 | } 59 | 60 | func sceneDidEnterBackground(_ scene: UIScene) { 61 | // Called as the scene transitions from the foreground to the background. 62 | // Use this method to save data, release shared resources, and store enough scene-specific state information 63 | // to restore the scene back to its current state. 64 | } 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "idiom" : "watch", 5 | "filename" : "Circular.imageset", 6 | "role" : "circular" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "filename" : "Extra Large.imageset", 11 | "role" : "extra-large" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "filename" : "Graphic Bezel.imageset", 16 | "role" : "graphic-bezel" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "filename" : "Graphic Circular.imageset", 21 | "role" : "graphic-circular" 22 | }, 23 | { 24 | "idiom" : "watch", 25 | "filename" : "Graphic Corner.imageset", 26 | "role" : "graphic-corner" 27 | }, 28 | { 29 | "idiom" : "watch", 30 | "filename" : "Graphic Large Rectangular.imageset", 31 | "role" : "graphic-large-rectangular" 32 | }, 33 | { 34 | "idiom" : "watch", 35 | "filename" : "Modular.imageset", 36 | "role" : "modular" 37 | }, 38 | { 39 | "idiom" : "watch", 40 | "filename" : "Utilitarian.imageset", 41 | "role" : "utilitarian" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // watchapp Extension 4 | // 5 | // Created by 翁培钧 on 2019/12/7. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | 11 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 12 | 13 | func applicationDidFinishLaunching() { 14 | // Perform any final initialization of your application. 15 | } 16 | 17 | func applicationDidBecomeActive() { 18 | // 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. 19 | } 20 | 21 | func applicationWillResignActive() { 22 | // 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. 23 | // Use this method to pause ongoing tasks, disable timers, etc. 24 | } 25 | 26 | func handle(_ backgroundTasks: Set) { 27 | // 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. 28 | for task in backgroundTasks { 29 | // Use a switch statement to check the task type 30 | switch task { 31 | case let backgroundTask as WKApplicationRefreshBackgroundTask: 32 | // Be sure to complete the background task once you’re done. 33 | backgroundTask.setTaskCompletedWithSnapshot(false) 34 | case let snapshotTask as WKSnapshotRefreshBackgroundTask: 35 | // Snapshot tasks have a unique completion call, make sure to set your expiration date 36 | snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) 37 | case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: 38 | // Be sure to complete the connectivity task once you’re done. 39 | connectivityTask.setTaskCompletedWithSnapshot(false) 40 | case let urlSessionTask as WKURLSessionRefreshBackgroundTask: 41 | // Be sure to complete the URL session task once you’re done. 42 | urlSessionTask.setTaskCompletedWithSnapshot(false) 43 | case let relevantShortcutTask as WKRelevantShortcutRefreshBackgroundTask: 44 | // Be sure to complete the relevant-shortcut task once you're done. 45 | relevantShortcutTask.setTaskCompletedWithSnapshot(false) 46 | case let intentDidRunTask as WKIntentDidRunRefreshBackgroundTask: 47 | // Be sure to complete the intent-did-run task once you're done. 48 | intentDidRunTask.setTaskCompletedWithSnapshot(false) 49 | default: 50 | // make sure to complete unhandled task types 51 | task.setTaskCompletedWithSnapshot(false) 52 | } 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lightGame/watchapp Extension/HostingController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HostingController.swift 3 | // watchapp Extension 4 | // 5 | // Created by 翁培钧 on 2019/12/7. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | import Foundation 11 | import SwiftUI 12 | 13 | class HostingController: WKHostingController { 14 | override var body: ContentView { 15 | return ContentView() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | watchapp Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | WKAppBundleIdentifier 28 | com.pjhubs.light-game.watchkitapp 29 | 30 | NSExtensionPointIdentifier 31 | com.apple.watchkit 32 | 33 | WKExtensionDelegateClassName 34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 35 | WKRunsIndependentlyOfCompanionApp 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /lightGame/watchapp Extension/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /lightGame/watchapp Extension/WatchContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WatchContentView.swift 3 | // watchapp Extension 4 | // 5 | // Created by 翁培钧 on 2019/12/7. 6 | // Copyright © 2019 PJHubs. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | @ObservedObject var gameManager = WatchGameManager(size: 3, lightSequence: [1, 2, 3]) 13 | 14 | @State var isShowHistory = false 15 | 16 | var body: some View { 17 | VStack { 18 | ForEach(0.. 8 { 59 | size = 7 60 | } 61 | // 太小了没意思 62 | if size < 2 { 63 | size = 2 64 | } 65 | self.size = size 66 | lights = Array(repeating: Array(repeating: Light(), count: size), count: size) 67 | 68 | // initGameController() 69 | start(lightSequence) 70 | } 71 | 72 | // MARK: Public 73 | 74 | /// 游戏配置 75 | /// - Parameter lightSequence: 亮灯序列 76 | func start(_ lightSequence: [Int]) { 77 | currentStatus = .during 78 | clickTimes = 0 79 | updateLightStatus(lightSequence) 80 | 81 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in 82 | let min = self.durations >= 60 ? self.durations / 60 : 0 83 | let seconds = self.durations - min * 60 84 | 85 | 86 | let minString = min >= 10 ? "\(min)" : "0\(min)" 87 | let secondString = self.durations - min * 60 >= 10 ? "\(seconds)" : "0\(seconds)" 88 | self.timeString = minString + ":" + secondString 89 | 90 | self.durations += 1 91 | }) 92 | } 93 | 94 | /// 停止 95 | func timerStop() { 96 | timer?.fireDate = Date.distantFuture 97 | } 98 | 99 | /// 重新创建 100 | func timerRestart() { 101 | isPause = false 102 | durations = 0 103 | timeString = "00:00" 104 | timer?.fireDate = Date() 105 | } 106 | 107 | /// 获取灯的尺寸 108 | func circleWidth() -> CGFloat { 109 | guard let size = size else { return 0 } 110 | 111 | /// 距离屏幕左右两边的间距 112 | let padding: CGFloat = 20 113 | /// 左右两灯之间的间距 114 | let innerSpacing: CGFloat = 20 115 | 116 | let screenBounds = WKInterfaceDevice.current().screenBounds 117 | var circleWidth = (screenBounds.width - padding - (CGFloat(size) * innerSpacing)) / CGFloat(size) 118 | 119 | // 太大了会很丑,过滤下 120 | if circleWidth > screenBounds.width / 5 { 121 | circleWidth = screenBounds.width / 5 122 | } 123 | 124 | return circleWidth 125 | } 126 | 127 | /// 通过坐标索引修改灯状态 128 | /// - Parameters: 129 | /// - column: 灯-列索引 130 | /// - size: 灯-行索引 131 | func updateLightStatus(column: Int, row: Int, userTouch: Bool) { 132 | lights[row][column].status.toggle() 133 | self.column = row 134 | self.row = column 135 | 136 | // 上 137 | let top = row - 1 138 | if !(top < 0) { 139 | lights[top][column].status.toggle() 140 | } 141 | // 下 142 | let bottom = row + 1 143 | if !(bottom > lights.count - 1) { 144 | lights[bottom][column].status.toggle() 145 | } 146 | // 左 147 | let left = column - 1 148 | if !(left < 0) { 149 | lights[row][left].status.toggle() 150 | } 151 | // 右 152 | let right = column + 1 153 | if !(right > lights.count - 1) { 154 | lights[row][right].status.toggle() 155 | } 156 | 157 | updateGameStatus() 158 | 159 | // if (userTouch) { 160 | // disSelectedGameControllerStatus() 161 | // } 162 | } 163 | 164 | // MARK: Private 165 | 166 | /// 通过亮灯序列修改灯状态 167 | /// - Parameter lightSequence: 亮灯序列 168 | private func updateLightStatus(_ lightSequence: [Int]) { 169 | guard let size = size else { return } 170 | for lightIndex in lightSequence { 171 | var index = lightIndex 172 | // 防止数组越出最大边界处 173 | if index >= size * size { 174 | index = size * size - 1 175 | } 176 | let row = lightIndex / size 177 | let column = lightIndex % size 178 | // column 不为 0,说明非最后一个 179 | // row 为 0,说明为第一行 180 | updateLightStatus(column: column, row: row, userTouch: true) 181 | } 182 | } 183 | 184 | private func updateLightSelected() { 185 | lights[pColumn][pRow].selected = false 186 | lights[column][row].selected = true 187 | 188 | pRow = row 189 | pColumn = column 190 | } 191 | 192 | // private func disSelectedGameControllerStatus() { 193 | // lights[pColumn][pRow].selected = false 194 | // } 195 | 196 | /// 判赢 197 | private func updateGameStatus() { 198 | guard let size = size else { return } 199 | 200 | var lightingCount = 0 201 | 202 | 203 | for lightArr in lights { 204 | for light in lightArr { 205 | if light.status { lightingCount += 1 } 206 | } 207 | } 208 | 209 | if lightingCount == size * size { 210 | timerStop() 211 | currentStatus = .lose 212 | return 213 | } 214 | 215 | if lightingCount == 0 { 216 | timerStop() 217 | currentStatus = .win 218 | isPause = true 219 | return 220 | } 221 | } 222 | 223 | // private func initGameController() { 224 | // gameController.isSelectX = { 225 | // guard !self.isPause else { return } 226 | // 227 | // if $0 { 228 | // if (self.row < self.lights.count - 1) { 229 | // self.row += 1 230 | // } 231 | // } else { 232 | // if self.row > 0 { 233 | // self.row -= 1 234 | // } 235 | // } 236 | // 237 | // self.updateLightSelected() 238 | // } 239 | // 240 | // gameController.isSelectY = { 241 | // guard !self.isPause else { return } 242 | // 243 | // if $0 { 244 | // if (self.column > 0) { 245 | // self.column -= 1 246 | // } 247 | // } else { 248 | // if (self.column < self.lights.count - 1) { 249 | // self.column += 1 250 | // } 251 | // } 252 | // 253 | // self.updateLightSelected() 254 | // } 255 | // 256 | // gameController.isTapButtonA = { 257 | // self.clickTimes += 1 258 | // self.updateLightStatus(column: self.row, row: self.column, userTouch: false) 259 | // } 260 | // } 261 | } 262 | 263 | extension WatchGameManager { 264 | enum WatchGameStatus { 265 | /// 赢 266 | case win 267 | /// 输 268 | case lose 269 | /// 进行中 270 | case during 271 | } 272 | } 273 | 274 | -------------------------------------------------------------------------------- /lightGame/watchapp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "44x44", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "appLauncher", 41 | "subtype" : "40mm" 42 | }, 43 | { 44 | "size" : "50x50", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "appLauncher", 48 | "subtype" : "44mm" 49 | }, 50 | { 51 | "size" : "86x86", 52 | "idiom" : "watch", 53 | "scale" : "2x", 54 | "role" : "quickLook", 55 | "subtype" : "38mm" 56 | }, 57 | { 58 | "size" : "98x98", 59 | "idiom" : "watch", 60 | "scale" : "2x", 61 | "role" : "quickLook", 62 | "subtype" : "42mm" 63 | }, 64 | { 65 | "size" : "108x108", 66 | "idiom" : "watch", 67 | "scale" : "2x", 68 | "role" : "quickLook", 69 | "subtype" : "44mm" 70 | }, 71 | { 72 | "idiom" : "watch-marketing", 73 | "size" : "1024x1024", 74 | "scale" : "1x" 75 | } 76 | ], 77 | "info" : { 78 | "version" : 1, 79 | "author" : "xcode" 80 | } 81 | } -------------------------------------------------------------------------------- /lightGame/watchapp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /lightGame/watchapp/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lightGame/watchapp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | light-game 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | UISupportedInterfaceOrientations 24 | 25 | UIInterfaceOrientationPortrait 26 | UIInterfaceOrientationPortraitUpsideDown 27 | 28 | WKCompanionAppBundleIdentifier 29 | com.pjhubs.light-game 30 | WKWatchKitApp 31 | 32 | 33 | 34 | --------------------------------------------------------------------------------