├── GameCenterMultiplayer.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── pedrocontine.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── pedrocontine.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── GameCenterMultiplayer ├── Assets.xcassets ├── .DS_Store ├── AccentColor.colorset │ └── Contents.json ├── Animations │ ├── .DS_Store │ ├── Contents.json │ ├── One │ │ ├── Contents.json │ │ ├── one_attack.imageset │ │ │ ├── Contents.json │ │ │ └── blue_kick_2.png │ │ ├── one_hit.imageset │ │ │ ├── Contents.json │ │ │ └── blue_hit_2.png │ │ └── one_idle.imageset │ │ │ ├── Contents.json │ │ │ └── blue_idle_1.png │ └── Two │ │ ├── Contents.json │ │ ├── two_attack.imageset │ │ ├── Contents.json │ │ └── two_kick_3.png │ │ ├── two_hit.imageset │ │ ├── Contents.json │ │ └── two_hit_2.png │ │ └── two_idle.imageset │ │ ├── Contents.json │ │ └── two_idle_1.png ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj └── Main.storyboard ├── GameCenterHelper.swift ├── GameViewController.swift ├── Info.plist ├── MenuViewController.swift ├── Models ├── GameModel.swift ├── Player.swift └── PlayerStatus.swift └── Source ├── AppDelegate.swift ├── Base.lproj └── LaunchScreen.storyboard └── SceneDelegate.swift /GameCenterMultiplayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 20116AE324AA1A6100EABDCF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116AE224AA1A6100EABDCF /* AppDelegate.swift */; }; 11 | 20116AE524AA1A6100EABDCF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116AE424AA1A6100EABDCF /* SceneDelegate.swift */; }; 12 | 20116AE724AA1A6100EABDCF /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116AE624AA1A6100EABDCF /* MenuViewController.swift */; }; 13 | 20116AEA24AA1A6100EABDCF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 20116AE824AA1A6100EABDCF /* Main.storyboard */; }; 14 | 20116AEC24AA1A6300EABDCF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 20116AEB24AA1A6300EABDCF /* Assets.xcassets */; }; 15 | 20116AEF24AA1A6300EABDCF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 20116AED24AA1A6300EABDCF /* LaunchScreen.storyboard */; }; 16 | 20116AF824AA1B0300EABDCF /* GameCenterHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116AF724AA1B0300EABDCF /* GameCenterHelper.swift */; }; 17 | 20116AFB24AA1E0400EABDCF /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20116AFA24AA1E0400EABDCF /* GameKit.framework */; }; 18 | 20116AFD24AA20DB00EABDCF /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116AFC24AA20DB00EABDCF /* GameViewController.swift */; }; 19 | 20116AFF24AA23BC00EABDCF /* GameModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116AFE24AA23BC00EABDCF /* GameModel.swift */; }; 20 | 20116B0324AA4B9400EABDCF /* PlayerStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116B0224AA4B9400EABDCF /* PlayerStatus.swift */; }; 21 | 20116B0524AA4BCA00EABDCF /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20116B0424AA4BCA00EABDCF /* Player.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 20116ADF24AA1A6100EABDCF /* GameCenterMultiplayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GameCenterMultiplayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 20116AE224AA1A6100EABDCF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 27 | 20116AE424AA1A6100EABDCF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 28 | 20116AE624AA1A6100EABDCF /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; 29 | 20116AE924AA1A6100EABDCF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | 20116AEB24AA1A6300EABDCF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 20116AEE24AA1A6300EABDCF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 20116AF024AA1A6300EABDCF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 20116AF724AA1B0300EABDCF /* GameCenterHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameCenterHelper.swift; sourceTree = ""; }; 34 | 20116AFA24AA1E0400EABDCF /* GameKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System/Library/Frameworks/GameKit.framework; sourceTree = SDKROOT; }; 35 | 20116AFC24AA20DB00EABDCF /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 36 | 20116AFE24AA23BC00EABDCF /* GameModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameModel.swift; sourceTree = ""; }; 37 | 20116B0224AA4B9400EABDCF /* PlayerStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStatus.swift; sourceTree = ""; }; 38 | 20116B0424AA4BCA00EABDCF /* Player.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 20116ADC24AA1A6100EABDCF /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 20116AFB24AA1E0400EABDCF /* GameKit.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 20116AD624AA1A6100EABDCF = { 54 | isa = PBXGroup; 55 | children = ( 56 | 20116AE124AA1A6100EABDCF /* GameCenterMultiplayer */, 57 | 20116AE024AA1A6100EABDCF /* Products */, 58 | 20116AF924AA1E0400EABDCF /* Frameworks */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | 20116AE024AA1A6100EABDCF /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 20116ADF24AA1A6100EABDCF /* GameCenterMultiplayer.app */, 66 | ); 67 | name = Products; 68 | sourceTree = ""; 69 | }; 70 | 20116AE124AA1A6100EABDCF /* GameCenterMultiplayer */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 20116AF624AA1A8800EABDCF /* Source */, 74 | 20116B0124AA4B8500EABDCF /* Models */, 75 | 20116AE824AA1A6100EABDCF /* Main.storyboard */, 76 | 20116AE624AA1A6100EABDCF /* MenuViewController.swift */, 77 | 20116AFC24AA20DB00EABDCF /* GameViewController.swift */, 78 | 20116AF724AA1B0300EABDCF /* GameCenterHelper.swift */, 79 | 20116AEB24AA1A6300EABDCF /* Assets.xcassets */, 80 | 20116AF024AA1A6300EABDCF /* Info.plist */, 81 | ); 82 | path = GameCenterMultiplayer; 83 | sourceTree = ""; 84 | }; 85 | 20116AF624AA1A8800EABDCF /* Source */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 20116AED24AA1A6300EABDCF /* LaunchScreen.storyboard */, 89 | 20116AE224AA1A6100EABDCF /* AppDelegate.swift */, 90 | 20116AE424AA1A6100EABDCF /* SceneDelegate.swift */, 91 | ); 92 | path = Source; 93 | sourceTree = ""; 94 | }; 95 | 20116AF924AA1E0400EABDCF /* Frameworks */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 20116AFA24AA1E0400EABDCF /* GameKit.framework */, 99 | ); 100 | name = Frameworks; 101 | sourceTree = ""; 102 | }; 103 | 20116B0124AA4B8500EABDCF /* Models */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 20116AFE24AA23BC00EABDCF /* GameModel.swift */, 107 | 20116B0224AA4B9400EABDCF /* PlayerStatus.swift */, 108 | 20116B0424AA4BCA00EABDCF /* Player.swift */, 109 | ); 110 | path = Models; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | 20116ADE24AA1A6100EABDCF /* GameCenterMultiplayer */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = 20116AF324AA1A6300EABDCF /* Build configuration list for PBXNativeTarget "GameCenterMultiplayer" */; 119 | buildPhases = ( 120 | 20116ADB24AA1A6100EABDCF /* Sources */, 121 | 20116ADC24AA1A6100EABDCF /* Frameworks */, 122 | 20116ADD24AA1A6100EABDCF /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = GameCenterMultiplayer; 129 | productName = GameCenterMultiplayer; 130 | productReference = 20116ADF24AA1A6100EABDCF /* GameCenterMultiplayer.app */; 131 | productType = "com.apple.product-type.application"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | 20116AD724AA1A6100EABDCF /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastSwiftUpdateCheck = 1200; 140 | LastUpgradeCheck = 1200; 141 | TargetAttributes = { 142 | 20116ADE24AA1A6100EABDCF = { 143 | CreatedOnToolsVersion = 12.0; 144 | }; 145 | }; 146 | }; 147 | buildConfigurationList = 20116ADA24AA1A6100EABDCF /* Build configuration list for PBXProject "GameCenterMultiplayer" */; 148 | compatibilityVersion = "Xcode 9.3"; 149 | developmentRegion = en; 150 | hasScannedForEncodings = 0; 151 | knownRegions = ( 152 | en, 153 | Base, 154 | ); 155 | mainGroup = 20116AD624AA1A6100EABDCF; 156 | productRefGroup = 20116AE024AA1A6100EABDCF /* Products */; 157 | projectDirPath = ""; 158 | projectRoot = ""; 159 | targets = ( 160 | 20116ADE24AA1A6100EABDCF /* GameCenterMultiplayer */, 161 | ); 162 | }; 163 | /* End PBXProject section */ 164 | 165 | /* Begin PBXResourcesBuildPhase section */ 166 | 20116ADD24AA1A6100EABDCF /* Resources */ = { 167 | isa = PBXResourcesBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | 20116AEF24AA1A6300EABDCF /* LaunchScreen.storyboard in Resources */, 171 | 20116AEC24AA1A6300EABDCF /* Assets.xcassets in Resources */, 172 | 20116AEA24AA1A6100EABDCF /* Main.storyboard in Resources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXResourcesBuildPhase section */ 177 | 178 | /* Begin PBXSourcesBuildPhase section */ 179 | 20116ADB24AA1A6100EABDCF /* Sources */ = { 180 | isa = PBXSourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 20116AFD24AA20DB00EABDCF /* GameViewController.swift in Sources */, 184 | 20116AF824AA1B0300EABDCF /* GameCenterHelper.swift in Sources */, 185 | 20116AE724AA1A6100EABDCF /* MenuViewController.swift in Sources */, 186 | 20116AE324AA1A6100EABDCF /* AppDelegate.swift in Sources */, 187 | 20116B0524AA4BCA00EABDCF /* Player.swift in Sources */, 188 | 20116B0324AA4B9400EABDCF /* PlayerStatus.swift in Sources */, 189 | 20116AE524AA1A6100EABDCF /* SceneDelegate.swift in Sources */, 190 | 20116AFF24AA23BC00EABDCF /* GameModel.swift in Sources */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXSourcesBuildPhase section */ 195 | 196 | /* Begin PBXVariantGroup section */ 197 | 20116AE824AA1A6100EABDCF /* Main.storyboard */ = { 198 | isa = PBXVariantGroup; 199 | children = ( 200 | 20116AE924AA1A6100EABDCF /* Base */, 201 | ); 202 | name = Main.storyboard; 203 | sourceTree = ""; 204 | }; 205 | 20116AED24AA1A6300EABDCF /* LaunchScreen.storyboard */ = { 206 | isa = PBXVariantGroup; 207 | children = ( 208 | 20116AEE24AA1A6300EABDCF /* Base */, 209 | ); 210 | name = LaunchScreen.storyboard; 211 | sourceTree = ""; 212 | }; 213 | /* End PBXVariantGroup section */ 214 | 215 | /* Begin XCBuildConfiguration section */ 216 | 20116AF124AA1A6300EABDCF /* Debug */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | ALWAYS_SEARCH_USER_PATHS = NO; 220 | CLANG_ANALYZER_NONNULL = YES; 221 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_ENABLE_OBJC_WEAK = YES; 227 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 228 | CLANG_WARN_BOOL_CONVERSION = YES; 229 | CLANG_WARN_COMMA = YES; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 233 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 234 | CLANG_WARN_EMPTY_BODY = YES; 235 | CLANG_WARN_ENUM_CONVERSION = YES; 236 | CLANG_WARN_INFINITE_RECURSION = YES; 237 | CLANG_WARN_INT_CONVERSION = YES; 238 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 239 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 240 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 242 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 243 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 244 | CLANG_WARN_STRICT_PROTOTYPES = YES; 245 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 246 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 247 | CLANG_WARN_UNREACHABLE_CODE = YES; 248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 249 | COPY_PHASE_STRIP = NO; 250 | DEBUG_INFORMATION_FORMAT = dwarf; 251 | ENABLE_STRICT_OBJC_MSGSEND = YES; 252 | ENABLE_TESTABILITY = YES; 253 | GCC_C_LANGUAGE_STANDARD = gnu11; 254 | GCC_DYNAMIC_NO_PIC = NO; 255 | GCC_NO_COMMON_BLOCKS = YES; 256 | GCC_OPTIMIZATION_LEVEL = 0; 257 | GCC_PREPROCESSOR_DEFINITIONS = ( 258 | "DEBUG=1", 259 | "$(inherited)", 260 | ); 261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 263 | GCC_WARN_UNDECLARED_SELECTOR = YES; 264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 265 | GCC_WARN_UNUSED_FUNCTION = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 268 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 269 | MTL_FAST_MATH = YES; 270 | ONLY_ACTIVE_ARCH = YES; 271 | SDKROOT = iphoneos; 272 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 273 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 274 | }; 275 | name = Debug; 276 | }; 277 | 20116AF224AA1A6300EABDCF /* Release */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ALWAYS_SEARCH_USER_PATHS = NO; 281 | CLANG_ANALYZER_NONNULL = YES; 282 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 283 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 284 | CLANG_CXX_LIBRARY = "libc++"; 285 | CLANG_ENABLE_MODULES = YES; 286 | CLANG_ENABLE_OBJC_ARC = YES; 287 | CLANG_ENABLE_OBJC_WEAK = YES; 288 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 289 | CLANG_WARN_BOOL_CONVERSION = YES; 290 | CLANG_WARN_COMMA = YES; 291 | CLANG_WARN_CONSTANT_CONVERSION = YES; 292 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 293 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 294 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 295 | CLANG_WARN_EMPTY_BODY = YES; 296 | CLANG_WARN_ENUM_CONVERSION = YES; 297 | CLANG_WARN_INFINITE_RECURSION = YES; 298 | CLANG_WARN_INT_CONVERSION = YES; 299 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 300 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 301 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 302 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 303 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 304 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 305 | CLANG_WARN_STRICT_PROTOTYPES = YES; 306 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 307 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 308 | CLANG_WARN_UNREACHABLE_CODE = YES; 309 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 310 | COPY_PHASE_STRIP = NO; 311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 312 | ENABLE_NS_ASSERTIONS = NO; 313 | ENABLE_STRICT_OBJC_MSGSEND = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu11; 315 | GCC_NO_COMMON_BLOCKS = YES; 316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 318 | GCC_WARN_UNDECLARED_SELECTOR = YES; 319 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 320 | GCC_WARN_UNUSED_FUNCTION = YES; 321 | GCC_WARN_UNUSED_VARIABLE = YES; 322 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 323 | MTL_ENABLE_DEBUG_INFO = NO; 324 | MTL_FAST_MATH = YES; 325 | SDKROOT = iphoneos; 326 | SWIFT_COMPILATION_MODE = wholemodule; 327 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 328 | VALIDATE_PRODUCT = YES; 329 | }; 330 | name = Release; 331 | }; 332 | 20116AF424AA1A6300EABDCF /* Debug */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 336 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 337 | CODE_SIGN_STYLE = Automatic; 338 | DEVELOPMENT_TEAM = FS3B5ULP9R; 339 | INFOPLIST_FILE = GameCenterMultiplayer/Info.plist; 340 | LD_RUNPATH_SEARCH_PATHS = ( 341 | "$(inherited)", 342 | "@executable_path/Frameworks", 343 | ); 344 | PRODUCT_BUNDLE_IDENTIFIER = continepedro.GameCenterMultiplayer; 345 | PRODUCT_NAME = "$(TARGET_NAME)"; 346 | SWIFT_VERSION = 5.0; 347 | TARGETED_DEVICE_FAMILY = 1; 348 | }; 349 | name = Debug; 350 | }; 351 | 20116AF524AA1A6300EABDCF /* Release */ = { 352 | isa = XCBuildConfiguration; 353 | buildSettings = { 354 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 355 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 356 | CODE_SIGN_STYLE = Automatic; 357 | DEVELOPMENT_TEAM = FS3B5ULP9R; 358 | INFOPLIST_FILE = GameCenterMultiplayer/Info.plist; 359 | LD_RUNPATH_SEARCH_PATHS = ( 360 | "$(inherited)", 361 | "@executable_path/Frameworks", 362 | ); 363 | PRODUCT_BUNDLE_IDENTIFIER = continepedro.GameCenterMultiplayer; 364 | PRODUCT_NAME = "$(TARGET_NAME)"; 365 | SWIFT_VERSION = 5.0; 366 | TARGETED_DEVICE_FAMILY = 1; 367 | }; 368 | name = Release; 369 | }; 370 | /* End XCBuildConfiguration section */ 371 | 372 | /* Begin XCConfigurationList section */ 373 | 20116ADA24AA1A6100EABDCF /* Build configuration list for PBXProject "GameCenterMultiplayer" */ = { 374 | isa = XCConfigurationList; 375 | buildConfigurations = ( 376 | 20116AF124AA1A6300EABDCF /* Debug */, 377 | 20116AF224AA1A6300EABDCF /* Release */, 378 | ); 379 | defaultConfigurationIsVisible = 0; 380 | defaultConfigurationName = Release; 381 | }; 382 | 20116AF324AA1A6300EABDCF /* Build configuration list for PBXNativeTarget "GameCenterMultiplayer" */ = { 383 | isa = XCConfigurationList; 384 | buildConfigurations = ( 385 | 20116AF424AA1A6300EABDCF /* Debug */, 386 | 20116AF524AA1A6300EABDCF /* Release */, 387 | ); 388 | defaultConfigurationIsVisible = 0; 389 | defaultConfigurationName = Release; 390 | }; 391 | /* End XCConfigurationList section */ 392 | }; 393 | rootObject = 20116AD724AA1A6100EABDCF /* Project object */; 394 | } 395 | -------------------------------------------------------------------------------- /GameCenterMultiplayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GameCenterMultiplayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GameCenterMultiplayer.xcodeproj/project.xcworkspace/xcuserdata/pedrocontine.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer.xcodeproj/project.xcworkspace/xcuserdata/pedrocontine.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /GameCenterMultiplayer.xcodeproj/xcuserdata/pedrocontine.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /GameCenterMultiplayer.xcodeproj/xcuserdata/pedrocontine.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | GameCenterMultiplayer.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/.DS_Store -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/one_attack.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "blue_kick_2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/one_attack.imageset/blue_kick_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/One/one_attack.imageset/blue_kick_2.png -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/one_hit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "blue_hit_2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/one_hit.imageset/blue_hit_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/One/one_hit.imageset/blue_hit_2.png -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/one_idle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "blue_idle_1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/One/one_idle.imageset/blue_idle_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/One/one_idle.imageset/blue_idle_1.png -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_attack.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "two_kick_3.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_attack.imageset/two_kick_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_attack.imageset/two_kick_3.png -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_hit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "two_hit_2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_hit.imageset/two_hit_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_hit.imageset/two_hit_2.png -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_idle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "two_idle_1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_idle.imageset/two_idle_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geovanacontine/GameKitMultiplayerTutorial/ec75def579485049953c14cab620277b179f375e/GameCenterMultiplayer/Assets.xcassets/Animations/Two/two_idle.imageset/two_idle_1.png -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/GameCenterHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Matchmaking.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import Foundation 9 | import GameKit 10 | 11 | protocol GameCenterHelperDelegate: class { 12 | func didChangeAuthStatus(isAuthenticated: Bool) 13 | func presentGameCenterAuth(viewController: UIViewController?) 14 | func presentMatchmaking(viewController: UIViewController?) 15 | func presentGame(match: GKMatch) 16 | } 17 | 18 | final class GameCenterHelper: NSObject, GKLocalPlayerListener { 19 | weak var delegate: GameCenterHelperDelegate? 20 | 21 | private let minPlayers: Int = 2 22 | private let maxPlayers: Int = 3 23 | private let inviteMessage = "Write your default invite message!" 24 | 25 | private var currentVC: GKMatchmakerViewController? 26 | 27 | var isAuthenticated: Bool { 28 | return GKLocalPlayer.local.isAuthenticated 29 | } 30 | 31 | func authenticatePlayer() { 32 | GKLocalPlayer.local.authenticateHandler = { (gameCenterAuthViewController, error) in 33 | self.delegate?.didChangeAuthStatus(isAuthenticated: self.isAuthenticated) 34 | 35 | guard GKLocalPlayer.local.isAuthenticated else { 36 | self.delegate?.presentGameCenterAuth(viewController: gameCenterAuthViewController) 37 | return 38 | } 39 | 40 | GKLocalPlayer.local.register(self) 41 | } 42 | } 43 | 44 | func presentMatchmaker(withInvite invite: GKInvite? = nil) { 45 | guard GKLocalPlayer.local.isAuthenticated, 46 | let vc = createMatchmaker(withInvite: invite) else { 47 | return 48 | } 49 | 50 | currentVC = vc 51 | vc.matchmakerDelegate = self 52 | delegate?.presentMatchmaking(viewController: vc) 53 | } 54 | 55 | private func createMatchmaker(withInvite invite: GKInvite? = nil) -> GKMatchmakerViewController? { 56 | 57 | //If there is an invite, create the matchmaker vc with it 58 | if let invite = invite { 59 | return GKMatchmakerViewController(invite: invite) 60 | } 61 | 62 | return GKMatchmakerViewController(matchRequest: createRequest()) 63 | } 64 | 65 | private func createRequest() -> GKMatchRequest { 66 | let request = GKMatchRequest() 67 | request.minPlayers = minPlayers 68 | request.maxPlayers = maxPlayers 69 | request.inviteMessage = inviteMessage 70 | 71 | return request 72 | } 73 | } 74 | 75 | 76 | extension GameCenterHelper: GKMatchmakerViewControllerDelegate { 77 | func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) { 78 | viewController.dismiss(animated: true) 79 | delegate?.presentGame(match: match) 80 | } 81 | 82 | func player(_ player: GKPlayer, didAccept invite: GKInvite) { 83 | currentVC?.dismiss(animated: true, completion: { 84 | self.presentMatchmaker(withInvite: invite) 85 | }) 86 | } 87 | 88 | func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) { 89 | viewController.dismiss(animated: true) 90 | } 91 | 92 | func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) { 93 | print("Matchmaker vc did fail with error: \(error.localizedDescription).") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import UIKit 9 | import GameKit 10 | 11 | class GameViewController: UIViewController { 12 | 13 | @IBOutlet weak var player1: UIImageView! 14 | @IBOutlet weak var progressPlayer1: UIProgressView! 15 | @IBOutlet weak var player2: UIImageView! 16 | @IBOutlet weak var progressPlayer2: UIProgressView! 17 | @IBOutlet weak var buttonAttack: UIButton! 18 | @IBOutlet weak var labelTime: UILabel! 19 | 20 | var match: GKMatch? 21 | private var timer: Timer! 22 | 23 | private var gameModel: GameModel! { 24 | didSet { 25 | updateUI() 26 | } 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | gameModel = GameModel() 33 | match?.delegate = self 34 | 35 | //Mirror player 2 images 36 | player2.transform = CGAffineTransform(scaleX: -1, y: 1) 37 | 38 | savePlayers() 39 | 40 | if getLocalPlayerType() == .one, timer == nil { 41 | self.initTimer() 42 | } 43 | } 44 | 45 | private func initTimer() { 46 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in 47 | let player = self.getLocalPlayerType() 48 | if player == .one, self.gameModel.time >= 1 { 49 | self.gameModel.time -= 1 50 | self.sendData() 51 | } 52 | }) 53 | } 54 | 55 | private func savePlayers() { 56 | guard let player2Name = match?.players.first?.displayName else { return } 57 | let player1 = Player(displayName: GKLocalPlayer.local.displayName) 58 | let player2 = Player(displayName: player2Name) 59 | 60 | gameModel.players = [player1, player2] 61 | 62 | gameModel.players.sort { (player1, player2) -> Bool in 63 | player1.displayName < player2.displayName 64 | } 65 | 66 | sendData() 67 | } 68 | 69 | private func getLocalPlayerType() -> PlayerType { 70 | if gameModel.players.first?.displayName == GKLocalPlayer.local.displayName { 71 | return .one 72 | } else { 73 | return .two 74 | } 75 | } 76 | 77 | private func updateUI() { 78 | guard gameModel.players.count >= 2 else { return } 79 | 80 | labelTime.text = "\(gameModel.time)" 81 | player1.image = gameModel.players[0].status.image(player: .one) 82 | progressPlayer1.progress = gameModel.players[0].life / 100.0 83 | player2.image = gameModel.players[1].status.image(player: .two) 84 | progressPlayer2.progress = gameModel.players[1].life / 100.0 85 | 86 | let player = getLocalPlayerType() 87 | buttonAttack.backgroundColor = player.color() 88 | } 89 | 90 | @IBAction func buttonAttackPressed() { 91 | let localPlayer = getLocalPlayerType() 92 | 93 | //Change status to attacking 94 | gameModel.players[localPlayer.index()].status = .attack 95 | gameModel.players[localPlayer.enemyIndex()].status = .hit 96 | gameModel.players[localPlayer.enemyIndex()].life -= 10 97 | sendData() 98 | 99 | //Reset status after 1 second 100 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 101 | self.gameModel.players[localPlayer.index()].status = .idle 102 | self.gameModel.players[localPlayer.enemyIndex()].status = .idle 103 | self.sendData() 104 | } 105 | } 106 | 107 | private func sendData() { 108 | guard let match = match else { return } 109 | 110 | do { 111 | guard let data = gameModel.encode() else { return } 112 | try match.sendData(toAllPlayers: data, with: .reliable) 113 | } catch { 114 | print("Send data failed") 115 | } 116 | } 117 | } 118 | 119 | extension GameViewController: GKMatchDelegate { 120 | func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) { 121 | guard let model = GameModel.decode(data: data) else { return } 122 | gameModel = model 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/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 | 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 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | gamekit 52 | 53 | UISupportedInterfaceOrientations 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | UISupportedInterfaceOrientations~ipad 60 | 61 | UIInterfaceOrientationPortrait 62 | UIInterfaceOrientationPortraitUpsideDown 63 | UIInterfaceOrientationLandscapeLeft 64 | UIInterfaceOrientationLandscapeRight 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/MenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import UIKit 9 | import GameKit 10 | 11 | class MenuViewController: UIViewController { 12 | 13 | @IBOutlet weak var buttonMultiplayer: UIButton! 14 | 15 | private var gameCenterHelper: GameCenterHelper! 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | buttonMultiplayer.isEnabled = false 20 | buttonMultiplayer.layer.cornerRadius = 10 21 | 22 | gameCenterHelper = GameCenterHelper() 23 | gameCenterHelper.delegate = self 24 | gameCenterHelper.authenticatePlayer() 25 | } 26 | 27 | @IBAction func buttonMultiplayerPressed() { 28 | gameCenterHelper.presentMatchmaker() 29 | } 30 | 31 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 32 | guard let vc = segue.destination as? GameViewController, 33 | let match = sender as? GKMatch else { return } 34 | 35 | vc.match = match 36 | } 37 | } 38 | 39 | extension MenuViewController: GameCenterHelperDelegate { 40 | func didChangeAuthStatus(isAuthenticated: Bool) { 41 | buttonMultiplayer.isEnabled = isAuthenticated 42 | } 43 | 44 | func presentGameCenterAuth(viewController: UIViewController?) { 45 | guard let vc = viewController else {return} 46 | self.present(vc, animated: true) 47 | } 48 | 49 | func presentMatchmaking(viewController: UIViewController?) { 50 | guard let vc = viewController else {return} 51 | self.present(vc, animated: true) 52 | } 53 | 54 | func presentGame(match: GKMatch) { 55 | performSegue(withIdentifier: "showGame", sender: match) 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Models/GameModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameModel.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | import GameKit 11 | 12 | struct GameModel: Codable { 13 | var players: [Player] = [] 14 | var time: Int = 60 15 | } 16 | 17 | extension GameModel { 18 | func encode() -> Data? { 19 | return try? JSONEncoder().encode(self) 20 | } 21 | 22 | static func decode(data: Data) -> GameModel? { 23 | return try? JSONDecoder().decode(GameModel.self, from: data) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Models/Player.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Player.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | struct Player: Codable { 12 | var displayName: String 13 | var status: PlayerStatus = .idle 14 | var life: Float = 100 15 | } 16 | 17 | enum PlayerType: String, Codable, CaseIterable { 18 | case one 19 | case two 20 | } 21 | 22 | extension PlayerType { 23 | func enemyIndex() -> Int { 24 | switch self { 25 | case .one: 26 | return 1 27 | case .two: 28 | return 0 29 | } 30 | } 31 | 32 | func index() -> Int { 33 | switch self { 34 | case .one: 35 | return 0 36 | case .two: 37 | return 1 38 | } 39 | } 40 | 41 | func color() -> UIColor { 42 | switch self { 43 | case .one: 44 | return .systemBlue 45 | case .two: 46 | return .systemRed 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Models/PlayerStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationTypes.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | enum PlayerStatus: String, Codable { 12 | case idle 13 | case attack 14 | case hit 15 | 16 | func image(player: PlayerType) -> UIImage { 17 | return UIImage(named: "\(player.rawValue)_\(self.rawValue)")! 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Source/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Source/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 | -------------------------------------------------------------------------------- /GameCenterMultiplayer/Source/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // GameCenterMultiplayer 4 | // 5 | // Created by Pedro Contine on 29/06/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | --------------------------------------------------------------------------------