From 7497b61d7c425cbab71f53b62de18dffb3d04409 Mon Sep 17 00:00:00 2001 From: softdevstory Date: Thu, 28 Apr 2016 15:27:04 +0900 Subject: [PATCH 1/3] iOS version: GameCenter achievements are supported. --- AchivementHelper.swift | 346 +++++++++++++++++++++++ GameKitHelper.swift | 67 +++++ GameStatistics.swift | 35 +++ OhMyPlane.xcodeproj/project.pbxproj | 40 +++ OhMyPlane/GameViewController.swift | 21 ++ OhMyPlane/Info.plist | 5 +- OhMyPlaneTv/Info.plist | 5 +- Shared/CreditScene.swift | 6 +- Shared/FailGame.swift | 4 - Shared/GameScene.swift | 61 +++- Shared/TopRecordsScene.swift | 45 ++- Shared/TopThreeRecords.swift | 14 +- Shared/etc.atlas/back_pressed.png | Bin 4989 -> 4787 bytes Shared/etc.atlas/game_center.png | Bin 0 -> 4232 bytes Shared/etc.atlas/game_center_pressed.png | Bin 0 -> 3901 bytes 15 files changed, 619 insertions(+), 30 deletions(-) create mode 100644 AchivementHelper.swift create mode 100644 GameKitHelper.swift create mode 100644 GameStatistics.swift create mode 100644 Shared/etc.atlas/game_center.png create mode 100644 Shared/etc.atlas/game_center_pressed.png diff --git a/AchivementHelper.swift b/AchivementHelper.swift new file mode 100644 index 0000000..7e50d58 --- /dev/null +++ b/AchivementHelper.swift @@ -0,0 +1,346 @@ +// +// AchivementHelper.swift +// OhMyPlane +// +// Created by HS Song on 2016. 4. 25.. +// Copyright © 2016년 softdevstory. All rights reserved. +// + +import Foundation +import GameKit + + +enum Achievement: String { + case RedPlane50 + case RedPlane100 + case RedPlane150 + case RedPlaneBronzeMedal + case RedPlaneSilverMedal + case RedPlaneGoldMedal + case YellowPlane50 + case YellowPlane100 + case YellowPlane150 + case YellowPlaneBronzeMedal + case YellowPlaneSilverMedal + case YellowPlaneGoldMedal + case GreenPlane50 + case GreenPlane100 + case GreenPlane150 + case GreenPlaneBronzeMedal + case GreenPlaneSilverMedal + case GreenPlaneGoldMedal + case BluePlane50 + case BluePlane100 + case BluePlane150 + case BluePlaneBronzeMedal + case BluePlaneSilverMedal + case BluePlaneGoldMedal + case GoldMedal5 + case GoldMedal10 + case GoldMedal15 + case SilverMedal10 + case SilverMedal15 + case SilverMedal20 + case BronzeMedal10 + case BronzeMedal15 + case BronzeMedal20 + case Flight50 + case Flight100 + case Flight150 + case CreditWatch + + private func getPercentage(gameStatistic: GameStatistics, maxValue: Double) -> Double { + let percent: Double = Double(gameStatistic.getValue()) / maxValue + if percent > 1.0 { + return 1.0 + } + + return percent + } + + var gkAchievement: GKAchievement { + let bundleId = NSBundle.mainBundle().bundleIdentifier! + let achievement = GKAchievement(identifier: "\(bundleId).\(self.rawValue)") + + switch self { + case RedPlane50: + achievement.percentComplete = getPercentage(GameStatistics.flightCountRedPlane, maxValue: 50.0) + case RedPlane100: + achievement.percentComplete = getPercentage(GameStatistics.flightCountRedPlane, maxValue: 100.0) + case RedPlane150: + achievement.percentComplete = getPercentage(GameStatistics.flightCountRedPlane, maxValue: 150.0) + + case YellowPlane50: + achievement.percentComplete = getPercentage(GameStatistics.flightCountYellowPlane, maxValue: 50.0) + case YellowPlane100: + achievement.percentComplete = getPercentage(GameStatistics.flightCountYellowPlane, maxValue: 100.0) + case YellowPlane150: + achievement.percentComplete = getPercentage(GameStatistics.flightCountYellowPlane, maxValue: 150.0) + + case GreenPlane50: + achievement.percentComplete = getPercentage(GameStatistics.flightCountGreenPlane, maxValue: 50.0) + case GreenPlane100: + achievement.percentComplete = getPercentage(GameStatistics.flightCountGreenPlane, maxValue: 100.0) + case GreenPlane150: + achievement.percentComplete = getPercentage(GameStatistics.flightCountGreenPlane, maxValue: 150.0) + + case BluePlane50: + achievement.percentComplete = getPercentage(GameStatistics.flightCountBluePlane, maxValue: 50.0) + case BluePlane100: + achievement.percentComplete = getPercentage(GameStatistics.flightCountBluePlane, maxValue: 100.0) + case BluePlane150: + achievement.percentComplete = getPercentage(GameStatistics.flightCountBluePlane, maxValue: 150.0) + + case GoldMedal5: + achievement.percentComplete = getPercentage(GameStatistics.goldMedalCount, maxValue: 5.0) + case GoldMedal10: + achievement.percentComplete = getPercentage(GameStatistics.goldMedalCount, maxValue: 10.0) + case GoldMedal15: + achievement.percentComplete = getPercentage(GameStatistics.goldMedalCount, maxValue: 15.0) + + case SilverMedal10: + achievement.percentComplete = getPercentage(GameStatistics.silverMedalCount, maxValue: 10.0) + case SilverMedal15: + achievement.percentComplete = getPercentage(GameStatistics.silverMedalCount, maxValue: 15.0) + case SilverMedal20: + achievement.percentComplete = getPercentage(GameStatistics.silverMedalCount, maxValue: 20.0) + + case BronzeMedal10: + achievement.percentComplete = getPercentage(GameStatistics.bronzeMedalCount, maxValue: 10.0) + case BronzeMedal15: + achievement.percentComplete = getPercentage(GameStatistics.bronzeMedalCount, maxValue: 15.0) + case BronzeMedal20: + achievement.percentComplete = getPercentage(GameStatistics.bronzeMedalCount, maxValue: 20.0) + + case Flight50: + achievement.percentComplete = getPercentage(GameStatistics.flightCount, maxValue: 50.0) + case Flight100: + achievement.percentComplete = getPercentage(GameStatistics.flightCount, maxValue: 100.0) + case Flight150: + achievement.percentComplete = getPercentage(GameStatistics.flightCount, maxValue: 150.0) + + default: + break + } + + return achievement + } +} + +class AchievementHelper { + static let sharedInstance = AchievementHelper() + + private init() { + // for singleton pattern + } + + func creditWatchAchievement() -> GKAchievement { + let achievement = Achievement.CreditWatch.gkAchievement + + achievement.percentComplete = 100.0 + achievement.showsCompletionBanner = true + + return achievement + } + + func createAchievements(planeType: PlaneType, medalType: Rank) -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + let flight = flightAchievements() + achievements.appendContentsOf(flight) + + let planeFlight = planeFlightAchievements(planeType) + achievements.appendContentsOf(planeFlight) + + if medalType != .None { + let medal = medalAchievements(medalType) + achievements.appendContentsOf(medal) + + if let planeMedal = planeMedalAchievement(planeType, medalType: medalType) { + achievements.append(planeMedal) + } + } + + return achievements + } + + private func planeMedalAchievement(planeType: PlaneType, medalType: Rank) -> GKAchievement? { + var achievement: GKAchievement? = nil + + switch planeType { + case .Red: + switch medalType { + case .Gold: + achievement = Achievement.RedPlaneGoldMedal.gkAchievement + case .Silver: + achievement = Achievement.RedPlaneSilverMedal.gkAchievement + case .Bronze: + achievement = Achievement.RedPlaneBronzeMedal.gkAchievement + default: break + } + + case .Blue: + switch medalType { + case .Gold: + achievement = Achievement.BluePlaneGoldMedal.gkAchievement + case .Silver: + achievement = Achievement.BluePlaneSilverMedal.gkAchievement + case .Bronze: + achievement = Achievement.BluePlaneBronzeMedal.gkAchievement + default: break + } + + case .Green: + switch medalType { + case .Gold: + achievement = Achievement.GreenPlaneGoldMedal.gkAchievement + case .Silver: + achievement = Achievement.GreenPlaneSilverMedal.gkAchievement + case .Bronze: + achievement = Achievement.GreenPlaneBronzeMedal.gkAchievement + default: break + } + + case .Yellow: + switch medalType { + case .Gold: + achievement = Achievement.YellowPlaneGoldMedal.gkAchievement + case .Silver: + achievement = Achievement.YellowPlaneGoldMedal.gkAchievement + case .Bronze: + achievement = Achievement.YellowPlaneGoldMedal.gkAchievement + default: break + } + } + + if achievement != nil { + achievement!.percentComplete = 100.0 + achievement!.showsCompletionBanner = true + } + + return achievement + } + + private func goldMedalAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.GoldMedal5.gkAchievement) + achievements.append(Achievement.GoldMedal10.gkAchievement) + achievements.append(Achievement.GoldMedal15.gkAchievement) + + return achievements + } + + private func silverMedalAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.SilverMedal10.gkAchievement) + achievements.append(Achievement.SilverMedal15.gkAchievement) + achievements.append(Achievement.SilverMedal20.gkAchievement) + + return achievements + } + + private func bronzeMedalAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.BronzeMedal10.gkAchievement) + achievements.append(Achievement.BronzeMedal15.gkAchievement) + achievements.append(Achievement.BronzeMedal20.gkAchievement) + + return achievements + } + + private func medalAchievements(medalType: Rank) -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + guard medalType != .None else { + return achievements + } + + switch medalType { + case .Gold: + achievements.appendContentsOf(goldMedalAchievements()) + + case .Silver: + achievements.appendContentsOf(silverMedalAchievements()) + + case .Bronze: + achievements.appendContentsOf(bronzeMedalAchievements()) + + default: break + } + + return achievements + } + + private func flightAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.Flight50.gkAchievement) + achievements.append(Achievement.Flight100.gkAchievement) + achievements.append(Achievement.Flight150.gkAchievement) + + return achievements + } + + private func redPlaneFlightAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.RedPlane50.gkAchievement) + achievements.append(Achievement.RedPlane100.gkAchievement) + achievements.append(Achievement.RedPlane150.gkAchievement) + + return achievements + } + + private func bluePlaneFlightAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.BluePlane50.gkAchievement) + achievements.append(Achievement.BluePlane100.gkAchievement) + achievements.append(Achievement.BluePlane150.gkAchievement) + + return achievements + } + + private func yellowPlaneFlightAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.YellowPlane50.gkAchievement) + achievements.append(Achievement.YellowPlane100.gkAchievement) + achievements.append(Achievement.YellowPlane150.gkAchievement) + + return achievements + } + + private func greenPlaneFlightAchievements() -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + achievements.append(Achievement.GreenPlane50.gkAchievement) + achievements.append(Achievement.GreenPlane100.gkAchievement) + achievements.append(Achievement.GreenPlane150.gkAchievement) + + return achievements + } + + private func planeFlightAchievements(planeType: PlaneType) -> [GKAchievement] { + var achievements: [GKAchievement] = [] + + switch planeType { + case .Red: + achievements.appendContentsOf(redPlaneFlightAchievements()) + + case .Blue: + achievements.appendContentsOf(bluePlaneFlightAchievements()) + + case .Yellow: + achievements.appendContentsOf(redPlaneFlightAchievements()) + + case .Green: + achievements.appendContentsOf(redPlaneFlightAchievements()) + } + + return achievements + } +} diff --git a/GameKitHelper.swift b/GameKitHelper.swift new file mode 100644 index 0000000..97d11c8 --- /dev/null +++ b/GameKitHelper.swift @@ -0,0 +1,67 @@ +// +// GameKitHelper.swift +// OhMyPlane +// +// Created by HS Song on 2016. 4. 25.. +// Copyright © 2016년 softdevstory. All rights reserved. +// + +import Foundation +import UIKit +import GameKit + +let PresentAuthenticationViewController = "PresentAuthenticationViewController" +let PresentGameCenterViewController = "PresentGameCenterViewController" + +class GameKitHelper: NSObject { + static let sharedInstance = GameKitHelper() + + private override init() { + super.init() + } + + var authenticationViewController: UIViewController? + var gameCenterEnabled = false + + func authenticateLocalPlayer() { + let localPlayer = GKLocalPlayer() + localPlayer.authenticateHandler = { (viewController, error) in + if viewController != nil { + self.authenticationViewController = viewController + + NSNotificationCenter.defaultCenter().postNotificationName(PresentAuthenticationViewController, object: self) + } else if error == nil { + self.gameCenterEnabled = true + } else { + print("Game center error: \(error)") + } + } + } + + func showGKGameCenterViewController(viewController: UIViewController) { + guard gameCenterEnabled else { + return + } + + let gameCenterViewController = GKGameCenterViewController() + + gameCenterViewController.gameCenterDelegate = self + + viewController.presentViewController(gameCenterViewController, animated: true, completion: nil) + } + + func reportAchievements(achievements: [GKAchievement], errorHandler: ((NSError?) -> Void)? = nil) { + guard gameCenterEnabled else { + return + } + + GKAchievement.reportAchievements(achievements, withCompletionHandler: errorHandler) + } +} + + +extension GameKitHelper: GKGameCenterControllerDelegate { + func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) { + gameCenterViewController.dismissViewControllerAnimated(true, completion: nil) + } +} \ No newline at end of file diff --git a/GameStatistics.swift b/GameStatistics.swift new file mode 100644 index 0000000..0e19b4d --- /dev/null +++ b/GameStatistics.swift @@ -0,0 +1,35 @@ +// +// GameStatistics.swift +// OhMyPlane +// +// Created by HS Song on 2016. 4. 27.. +// Copyright © 2016년 softdevstory. All rights reserved. +// + +import Foundation + +enum GameStatistics: String { + + case flightCountRedPlane + case flightCountYellowPlane + case flightCountGreenPlane + case flightCountBluePlane + case goldMedalCount + case silverMedalCount + case bronzeMedalCount + case flightCount + + func getValue() -> Int { + return NSUserDefaults.standardUserDefaults().integerForKey(self.rawValue) + } + + private func setValue(value: Int) { + NSUserDefaults.standardUserDefaults().setInteger(value, forKey: self.rawValue) + print("\(self.rawValue) value: \(value)") + } + + func increaseCountByOne() { + let value = self.getValue() + 1 + self.setValue(value) + } +} \ No newline at end of file diff --git a/OhMyPlane.xcodeproj/project.pbxproj b/OhMyPlane.xcodeproj/project.pbxproj index 80ddf08..6249828 100644 --- a/OhMyPlane.xcodeproj/project.pbxproj +++ b/OhMyPlane.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ A427556C1CBE332600EB0E50 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E01CA228C800A448F5 /* GameScene.swift */; }; + A42DE6611CCDD8F80088AD76 /* GameKitHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42DE6601CCDD8F80088AD76 /* GameKitHelper.swift */; }; + A42DE6621CCDD8F80088AD76 /* GameKitHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42DE6601CCDD8F80088AD76 /* GameKitHelper.swift */; }; + A42DE6651CCE11970088AD76 /* AchivementHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42DE6641CCE11970088AD76 /* AchivementHelper.swift */; }; + A42DE6671CCF51320088AD76 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A42DE6661CCF51320088AD76 /* GameKit.framework */; }; A435C0601CBF4628002EFCCF /* SKTUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A435C05E1CBF45D3002EFCCF /* SKTUtils.framework */; }; A435C0621CBF61DF002EFCCF /* TVControlsScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = A435C0611CBF61DF002EFCCF /* TVControlsScene.swift */; }; A435C0631CBF61DF002EFCCF /* TVControlsScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = A435C0611CBF61DF002EFCCF /* TVControlsScene.swift */; }; @@ -23,6 +27,7 @@ A43735E41CA43F15005F5BC1 /* obstacle.atlas in Resources */ = {isa = PBXBuildFile; fileRef = A43735E31CA43F15005F5BC1 /* obstacle.atlas */; }; A43735E61CA51BA3005F5BC1 /* PauseGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = A43735E51CA51BA3005F5BC1 /* PauseGame.swift */; }; A43735EA1CA51BC8005F5BC1 /* FailGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = A43735E91CA51BC8005F5BC1 /* FailGame.swift */; }; + A46711BD1CC8BC3D005A0E6A /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A46711BC1CC8BC3D005A0E6A /* GameKit.framework */; }; A48DA0161CBE222B000DCCED /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A48DA0151CBE222B000DCCED /* AppDelegate.swift */; }; A48DA01C1CBE222B000DCCED /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A48DA01B1CBE222B000DCCED /* GameViewController.swift */; }; A48DA0211CBE222B000DCCED /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A48DA01F1CBE222B000DCCED /* Main.storyboard */; }; @@ -60,6 +65,7 @@ A48DA0671CBE2A7A000DCCED /* spark.png in Resources */ = {isa = PBXBuildFile; fileRef = A496E9091CA2853E00A448F5 /* spark.png */; }; A48DA0691CBE2AD7000DCCED /* ShareAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */; }; A48DA06A1CBE2AD7000DCCED /* ShareAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */; }; + A490077C1CD09E6A0046DAC8 /* GameStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */; }; A496E8DD1CA228C800A448F5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8DC1CA228C800A448F5 /* AppDelegate.swift */; }; A496E8E11CA228C800A448F5 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E01CA228C800A448F5 /* GameScene.swift */; }; A496E8E31CA228C800A448F5 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E21CA228C800A448F5 /* GameViewController.swift */; }; @@ -93,6 +99,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + A42DE6601CCDD8F80088AD76 /* GameKitHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameKitHelper.swift; sourceTree = ""; }; + A42DE6641CCE11970088AD76 /* AchivementHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AchivementHelper.swift; sourceTree = ""; }; + A42DE6661CCF51320088AD76 /* GameKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System/Library/Frameworks/GameKit.framework; sourceTree = SDKROOT; }; A435C05E1CBF45D3002EFCCF /* SKTUtils.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SKTUtils.framework; path = ../Carthage/Build/tvOS/SKTUtils.framework; sourceTree = ""; }; A435C0611CBF61DF002EFCCF /* TVControlsScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TVControlsScene.swift; sourceTree = ""; }; A435C0651CBF6248002EFCCF /* GameScene+TVControlsScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameScene+TVControlsScene.swift"; sourceTree = ""; }; @@ -107,6 +116,7 @@ A43735E31CA43F15005F5BC1 /* obstacle.atlas */ = {isa = PBXFileReference; lastKnownFileType = folder.skatlas; name = obstacle.atlas; path = ../Shared/obstacle.atlas; sourceTree = ""; }; A43735E51CA51BA3005F5BC1 /* PauseGame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = PauseGame.swift; path = ../Shared/PauseGame.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; A43735E91CA51BC8005F5BC1 /* FailGame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = FailGame.swift; path = ../Shared/FailGame.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + A46711BC1CC8BC3D005A0E6A /* GameKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.2.sdk/System/Library/Frameworks/GameKit.framework; sourceTree = DEVELOPER_DIR; }; A48DA0131CBE222B000DCCED /* OhMyPlaneTv.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OhMyPlaneTv.app; sourceTree = BUILT_PRODUCTS_DIR; }; A48DA0151CBE222B000DCCED /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A48DA01B1CBE222B000DCCED /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; @@ -114,6 +124,7 @@ A48DA0221CBE222B000DCCED /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A48DA0241CBE222B000DCCED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = ShareAssets.xcassets; path = ../Shared/ShareAssets.xcassets; sourceTree = ""; }; + A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameStatistics.swift; sourceTree = ""; }; A496E8D91CA228C800A448F5 /* OhMyPlane.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OhMyPlane.app; sourceTree = BUILT_PRODUCTS_DIR; }; A496E8DC1CA228C800A448F5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; A496E8E01CA228C800A448F5 /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = GameScene.swift; path = ../Shared/GameScene.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -154,6 +165,7 @@ buildActionMask = 2147483647; files = ( A435C0601CBF4628002EFCCF /* SKTUtils.framework in Frameworks */, + A46711BD1CC8BC3D005A0E6A /* GameKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -162,12 +174,23 @@ buildActionMask = 2147483647; files = ( A4D1BF481CB77895001B455A /* SKTUtils.framework in Frameworks */, + A42DE6671CCF51320088AD76 /* GameKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A42DE6631CCE117C0088AD76 /* GameCenter */ = { + isa = PBXGroup; + children = ( + A42DE6601CCDD8F80088AD76 /* GameKitHelper.swift */, + A42DE6641CCE11970088AD76 /* AchivementHelper.swift */, + A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */, + ); + name = GameCenter; + sourceTree = ""; + }; A435C0641CBF620D002EFCCF /* extensionsForTvOS */ = { isa = PBXGroup; children = ( @@ -217,6 +240,7 @@ A48DA0281CBE2261000DCCED /* Share */ = { isa = PBXGroup; children = ( + A42DE6631CCE117C0088AD76 /* GameCenter */, A496E9001CA2782D00A448F5 /* GameSetting.swift */, A4C08B2B1CB258C800FD8DAE /* TopThreeRecords.swift */, A4C08B211CAE1EB600FD8DAE /* Scenes */, @@ -228,6 +252,8 @@ A496E8D01CA228C800A448F5 = { isa = PBXGroup; children = ( + A42DE6661CCF51320088AD76 /* GameKit.framework */, + A46711BC1CC8BC3D005A0E6A /* GameKit.framework */, A48DA0281CBE2261000DCCED /* Share */, A496E8DB1CA228C800A448F5 /* OhMyPlane */, A48DA0141CBE222B000DCCED /* OhMyPlaneTv */, @@ -393,10 +419,20 @@ A48DA0121CBE222B000DCCED = { CreatedOnToolsVersion = 7.3; DevelopmentTeam = MGW797GVB2; + SystemCapabilities = { + com.apple.GameCenter = { + enabled = 1; + }; + }; }; A496E8D81CA228C800A448F5 = { CreatedOnToolsVersion = 7.3; DevelopmentTeam = MGW797GVB2; + SystemCapabilities = { + com.apple.GameCenter = { + enabled = 1; + }; + }; }; }; }; @@ -529,6 +565,7 @@ A48DA0161CBE222B000DCCED /* AppDelegate.swift in Sources */, A48DA0501CBE2A2F000DCCED /* PauseGame.swift in Sources */, A435C0661CBF6248002EFCCF /* GameScene+TVControlsScene.swift in Sources */, + A42DE6621CCDD8F80088AD76 /* GameKitHelper.swift in Sources */, A48DA04C1CBE2A2A000DCCED /* PlaneEntity.swift in Sources */, A48DA0511CBE2A2F000DCCED /* FailGame.swift in Sources */, A435C06C1CBF7264002EFCCF /* TopRecordsScene+TVControlsScene.swift in Sources */, @@ -541,6 +578,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A42DE6611CCDD8F80088AD76 /* GameKitHelper.swift in Sources */, A435C0621CBF61DF002EFCCF /* TVControlsScene.swift in Sources */, A43735DA1CA4244E005F5BC1 /* Normal.swift in Sources */, A496E8FC1CA25E0E00A448F5 /* AnimationComponent.swift in Sources */, @@ -559,6 +597,8 @@ A496E8FF1CA2763100A448F5 /* PlaneMovementComponent.swift in Sources */, A496E9071CA2823100A448F5 /* ReadyGame.swift in Sources */, A496E8DD1CA228C800A448F5 /* AppDelegate.swift in Sources */, + A42DE6651CCE11970088AD76 /* AchivementHelper.swift in Sources */, + A490077C1CD09E6A0046DAC8 /* GameStatistics.swift in Sources */, A4C08B231CAE1F2700FD8DAE /* MainScene.swift in Sources */, A43735E61CA51BA3005F5BC1 /* PauseGame.swift in Sources */, A43735DE1CA4245F005F5BC1 /* Crash.swift in Sources */, diff --git a/OhMyPlane/GameViewController.swift b/OhMyPlane/GameViewController.swift index a827571..0789e9d 100644 --- a/OhMyPlane/GameViewController.swift +++ b/OhMyPlane/GameViewController.swift @@ -29,6 +29,27 @@ class GameViewController: UIViewController { scene.scaleMode = .AspectFill skView.presentScene(scene) + + NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.showAuthenticationViewController), name: PresentAuthenticationViewController, object: nil) + + NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.showGameCenterViewController), name: PresentGameCenterViewController, object: nil) + + GameKitHelper.sharedInstance.authenticateLocalPlayer() + } + + func showAuthenticationViewController() { + let gameKitHelper = GameKitHelper.sharedInstance + if let authenticationViewController = gameKitHelper.authenticationViewController { + presentViewController(authenticationViewController, animated: true, completion: nil) + } + } + + func showGameCenterViewController() { + GameKitHelper.sharedInstance.showGKGameCenterViewController(self) + } + + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) } override func shouldAutorotate() -> Bool { diff --git a/OhMyPlane/Info.plist b/OhMyPlane/Info.plist index 538b846..49c746d 100644 --- a/OhMyPlane/Info.plist +++ b/OhMyPlane/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2 CFBundleSignature ???? CFBundleVersion - 7 + 3 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -35,6 +35,7 @@ UIRequiredDeviceCapabilities armv7 + gamekit UIRequiresFullScreen diff --git a/OhMyPlaneTv/Info.plist b/OhMyPlaneTv/Info.plist index db01328..a163e2c 100644 --- a/OhMyPlaneTv/Info.plist +++ b/OhMyPlaneTv/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2 CFBundleSignature ???? CFBundleVersion - 7 + 3 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -29,6 +29,7 @@ UIRequiredDeviceCapabilities arm64 + gamekit diff --git a/Shared/CreditScene.swift b/Shared/CreditScene.swift index e733c70..2826c1f 100644 --- a/Shared/CreditScene.swift +++ b/Shared/CreditScene.swift @@ -166,7 +166,11 @@ class CreditScene: SKScene { position.y -= label.fontSize } - creditLayer.runAction(SKAction.moveBy(CGVectorMake(0, -position.y * 2 + overlapAmount()), duration: 32.0)) + creditLayer.runAction(SKAction.moveBy(CGVectorMake(0, -position.y * 2 + overlapAmount()), duration: 32.0)) { + let achievement = AchievementHelper.sharedInstance.creditWatchAchievement() + + GameKitHelper.sharedInstance.reportAchievements([achievement]) + } } func loadCreditLabels() { diff --git a/Shared/FailGame.swift b/Shared/FailGame.swift index 0c40517..ba9c0f1 100644 --- a/Shared/FailGame.swift +++ b/Shared/FailGame.swift @@ -30,8 +30,4 @@ class FailGame: GKState { override func willExitWithNextState(nextState: GKState) { scene.hideGameOver() } - - override func updateWithDeltaTime(seconds: NSTimeInterval) { - - } } diff --git a/Shared/GameScene.swift b/Shared/GameScene.swift index dca0c5c..5ba1c31 100644 --- a/Shared/GameScene.swift +++ b/Shared/GameScene.swift @@ -227,26 +227,26 @@ class GameScene: SKScene, SKPhysicsContactDelegate { var medalNodes: [Rank: SKSpriteNode] = [:] private func prepareMedalNodes() { - let goldMedal = SKSpriteNode(imageNamed: Rank.First.imageFileName!) + let goldMedal = SKSpriteNode(imageNamed: Rank.Gold.imageFileName!) goldMedal.zPosition = SpriteZPosition.Overlay goldMedal.hidden = true cameraNode.addChild(goldMedal) - medalNodes[Rank.First] = goldMedal + medalNodes[Rank.Gold] = goldMedal - let silverMedal = SKSpriteNode(imageNamed: Rank.Second.imageFileName!) + let silverMedal = SKSpriteNode(imageNamed: Rank.Silver.imageFileName!) silverMedal.zPosition = SpriteZPosition.Overlay silverMedal.hidden = true cameraNode.addChild(silverMedal) - medalNodes[Rank.Second] = silverMedal + medalNodes[Rank.Silver] = silverMedal - let bronzeMedal = SKSpriteNode(imageNamed: Rank.Third.imageFileName!) + let bronzeMedal = SKSpriteNode(imageNamed: Rank.Bronze.imageFileName!) bronzeMedal.zPosition = SpriteZPosition.Overlay bronzeMedal.hidden = true cameraNode.addChild(bronzeMedal) - medalNodes[Rank.Third] = bronzeMedal + medalNodes[Rank.Bronze] = bronzeMedal } private func preparePauseNode() { @@ -746,14 +746,14 @@ class GameScene: SKScene, SKPhysicsContactDelegate { } private func hideMedal() { - medalNodes[Rank.First]?.removeAllActions() - medalNodes[Rank.First]?.hidden = true + medalNodes[Rank.Gold]?.removeAllActions() + medalNodes[Rank.Gold]?.hidden = true - medalNodes[Rank.Second]?.removeAllActions() - medalNodes[Rank.Second]?.hidden = true + medalNodes[Rank.Silver]?.removeAllActions() + medalNodes[Rank.Silver]?.hidden = true - medalNodes[Rank.Third]?.removeAllActions() - medalNodes[Rank.Third]?.hidden = true + medalNodes[Rank.Bronze]?.removeAllActions() + medalNodes[Rank.Bronze]?.hidden = true } // MARK: @@ -796,6 +796,37 @@ class GameScene: SKScene, SKPhysicsContactDelegate { rockXPositions.removeFirst() } } + + private func updateGameStatistics(planeType: PlaneType, medalType: Rank) { + GameStatistics.flightCount.increaseCountByOne() + + switch planeType { + case .Red: + GameStatistics.flightCountRedPlane.increaseCountByOne() + case .Green: + GameStatistics.flightCountGreenPlane.increaseCountByOne() + case .Yellow: + GameStatistics.flightCountYellowPlane.increaseCountByOne() + case .Blue: + GameStatistics.flightCountBluePlane.increaseCountByOne() + } + + switch medalType { + case .Gold: + GameStatistics.goldMedalCount.increaseCountByOne() + case .Silver: + GameStatistics.silverMedalCount.increaseCountByOne() + case .Bronze: + GameStatistics.bronzeMedalCount.increaseCountByOne() + case .None: + break + } + } + + private func reportAchievement(planeType: PlaneType, medalType: Rank) { + let achievements = AchievementHelper.sharedInstance.createAchievements(planeType, medalType: medalType) + GameKitHelper.sharedInstance.reportAchievements(achievements) + } func checkGameScore() { let topThreeRecord = TopThreeRecords() @@ -804,11 +835,15 @@ class GameScene: SKScene, SKPhysicsContactDelegate { if rank != .None { showMedal(rank) - topThreeRecord.checkAndReplacePoint(planeEntity.planeType.rawValue, point: score) } + + updateGameStatistics(planeEntity.planeType, medalType: rank) + + reportAchievement(planeEntity.planeType, medalType: rank) } + // MARK: Camera /* diff --git a/Shared/TopRecordsScene.swift b/Shared/TopRecordsScene.swift index b04b09b..ed7a8cb 100644 --- a/Shared/TopRecordsScene.swift +++ b/Shared/TopRecordsScene.swift @@ -17,6 +17,10 @@ class TopRecordsScene: SKScene { var backTextures: [SKTexture] = [] var backPressed = false + var gameCenterSprite: SKSpriteNode! + var gameCenterTextures: [SKTexture] = [] + var gameCenterPressed = false + var topThreeRecords = TopThreeRecords() override func didMoveToView(view: SKView) { @@ -58,11 +62,21 @@ class TopRecordsScene: SKScene { } func loadButtons() { + gameCenterTextures.append(SKTexture(imageNamed: "game_center")) + gameCenterTextures.append(SKTexture(imageNamed: "game_center_pressed")) + + let gameCenter = SKSpriteNode(texture: gameCenterTextures[0]) + gameCenter.position = CGPoint(x: size.width - gameCenter.size.width, y: overlapAmount() / 2 + gameCenter.size.height) + gameCenter.zPosition = SpriteZPosition.Overlay + + backgroundLayer.addChild(gameCenter) + gameCenterSprite = gameCenter + backTextures.append(SKTexture(imageNamed: "back")) backTextures.append(SKTexture(imageNamed: "back_pressed")) let back = SKSpriteNode(texture: backTextures[0]) - back.position = CGPoint(x: size.width - back.size.width, y: overlapAmount() / 2 + back.size.height) + back.position = CGPoint(x: size.width - back.size.width, y: gameCenter.position.y + back.size.height) back.zPosition = SpriteZPosition.Overlay backgroundLayer.addChild(back) @@ -77,6 +91,14 @@ class TopRecordsScene: SKScene { SKTAudio.sharedInstance().playSoundEffect("click3.wav") } + func touchDownGameCenter() { + gameCenterSprite.texture = gameCenterTextures[1] + gameCenterSprite.size = (gameCenterSprite.texture?.size())! + gameCenterPressed = true + + playClickSound() + } + func touchDownBack() { backSprite.texture = backTextures[1] backSprite.size = (backSprite.texture?.size())! @@ -85,7 +107,16 @@ class TopRecordsScene: SKScene { playClickSound() } + func showGameCenterViewController() { + NSNotificationCenter.defaultCenter().postNotificationName(PresentGameCenterViewController, object: self) + + gameCenterSprite.texture = gameCenterTextures[0] + gameCenterSprite.size = (gameCenterSprite.texture?.size())! + gameCenterPressed = false + } + func doBack() { + let scene = MainScene(size: GameSetting.SceneSize) scene.scaleMode = (self.scene?.scaleMode)! let transition = SKTransition.pushWithDirection(.Down, duration: 0.6) @@ -110,6 +141,8 @@ class TopRecordsScene: SKScene { if node == backSprite { touchDownBack() + } else if node == gameCenterSprite { + touchDownGameCenter() } } @@ -117,6 +150,10 @@ class TopRecordsScene: SKScene { if backPressed { doBack() } + + if gameCenterPressed { + showGameCenterViewController() + } } override func touchesCancelled(touches: Set?, withEvent event: UIEvent?) { @@ -125,6 +162,12 @@ class TopRecordsScene: SKScene { backSprite.size = (backSprite.texture?.size())! backPressed = false } + + if gameCenterPressed { + gameCenterSprite.texture = gameCenterTextures[0] + gameCenterSprite.size = (gameCenterSprite.texture?.size())! + gameCenterPressed = false + } } func showTopRecords() { diff --git a/Shared/TopThreeRecords.swift b/Shared/TopThreeRecords.swift index 5df52a3..3b9cba7 100644 --- a/Shared/TopThreeRecords.swift +++ b/Shared/TopThreeRecords.swift @@ -9,18 +9,18 @@ import Foundation enum Rank: Int { - case First = 3 - case Second = 2 - case Third = 1 + case Gold = 3 + case Silver = 2 + case Bronze = 1 case None = 0 var imageFileName: String? { switch self { - case .First: + case .Gold: return "gold" - case .Second: + case .Silver: return "silver" - case .Third: + case .Bronze: return "bronze" case .None: return nil @@ -163,7 +163,7 @@ class TopThreeRecords { } func getRankOfPoint(point: Int) -> Rank { - var result: Rank = .First + var result: Rank = .Gold for record in topThreeRecords { if record.point >= point { diff --git a/Shared/etc.atlas/back_pressed.png b/Shared/etc.atlas/back_pressed.png index 2eac346f8411f9b22215fad9f255345fc7f52f01..8b56c4be5ebc464291e1bc6e6c144f65c989cbed 100644 GIT binary patch literal 4787 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 b-2fO~h)(QNmt_$Sm;n0PPqaR1K;r%bn)e-@ literal 4989 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 UPg?N|?pGP0siv!1s|<_yFW;9|-v9sr diff --git a/Shared/etc.atlas/game_center.png b/Shared/etc.atlas/game_center.png new file mode 100644 index 0000000000000000000000000000000000000000..8f4103416c9fe1bef6ea81c4a697551901aa1f5c GIT binary patch literal 4232 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 literal 0 HcmV?d00001 From 959ef222bdb37c6331c7362145a4a7ef461a33ec Mon Sep 17 00:00:00 2001 From: softdevstory Date: Thu, 28 Apr 2016 16:33:05 +0900 Subject: [PATCH 2/3] iOS: Leader board of Game Center is supported. --- GameKitHelper.swift | 8 +++++ LeaderBoardHelper.swift | 49 +++++++++++++++++++++++++++++ OhMyPlane.xcodeproj/project.pbxproj | 4 +++ Shared/GameScene.swift | 9 ++++-- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 LeaderBoardHelper.swift diff --git a/GameKitHelper.swift b/GameKitHelper.swift index 97d11c8..03ab5e7 100644 --- a/GameKitHelper.swift +++ b/GameKitHelper.swift @@ -57,6 +57,14 @@ class GameKitHelper: NSObject { GKAchievement.reportAchievements(achievements, withCompletionHandler: errorHandler) } + + func reportScore(gkScore: GKScore, errorHandler: ((NSError?)->Void)? = nil) { + guard gameCenterEnabled else { + return + } + + GKScore.reportScores([gkScore], withCompletionHandler: errorHandler) + } } diff --git a/LeaderBoardHelper.swift b/LeaderBoardHelper.swift new file mode 100644 index 0000000..ab824c2 --- /dev/null +++ b/LeaderBoardHelper.swift @@ -0,0 +1,49 @@ +// +// LeaderBoardHelper.swift +// OhMyPlane +// +// Created by HS Song on 2016. 4. 28.. +// Copyright © 2016년 softdevstory. All rights reserved. +// + +import Foundation +import GameKit + +enum LeaderBoard: String { + case RedPlaneHighScore + case BluePlaneHighScore + case YellowPlaneHighScore + case GreenPlaneHighScore + + var gkScore: GKScore { + let bundleId = NSBundle.mainBundle().bundleIdentifier! + return GKScore(leaderboardIdentifier: "\(bundleId).\(self.rawValue)") + } +} + +class LeaderBoardHelper { + static let sharedInstance = LeaderBoardHelper() + + private init() { + // for singleton pattern + } + + func createScore(planeType: PlaneType, score: Int) -> GKScore { + var gkScore: GKScore! + + switch planeType { + case .Red: + gkScore = LeaderBoard.RedPlaneHighScore.gkScore + case .Yellow: + gkScore = LeaderBoard.YellowPlaneHighScore.gkScore + case .Blue: + gkScore = LeaderBoard.BluePlaneHighScore.gkScore + case .Green: + gkScore = LeaderBoard.GreenPlaneHighScore.gkScore + } + + gkScore.value = Int64(score) + + return gkScore + } +} \ No newline at end of file diff --git a/OhMyPlane.xcodeproj/project.pbxproj b/OhMyPlane.xcodeproj/project.pbxproj index 6249828..2368ac9 100644 --- a/OhMyPlane.xcodeproj/project.pbxproj +++ b/OhMyPlane.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ A48DA0691CBE2AD7000DCCED /* ShareAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */; }; A48DA06A1CBE2AD7000DCCED /* ShareAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */; }; A490077C1CD09E6A0046DAC8 /* GameStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */; }; + A490077E1CD1ED860046DAC8 /* LeaderBoardHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077D1CD1ED860046DAC8 /* LeaderBoardHelper.swift */; }; A496E8DD1CA228C800A448F5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8DC1CA228C800A448F5 /* AppDelegate.swift */; }; A496E8E11CA228C800A448F5 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E01CA228C800A448F5 /* GameScene.swift */; }; A496E8E31CA228C800A448F5 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E21CA228C800A448F5 /* GameViewController.swift */; }; @@ -125,6 +126,7 @@ A48DA0241CBE222B000DCCED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = ShareAssets.xcassets; path = ../Shared/ShareAssets.xcassets; sourceTree = ""; }; A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameStatistics.swift; sourceTree = ""; }; + A490077D1CD1ED860046DAC8 /* LeaderBoardHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeaderBoardHelper.swift; sourceTree = ""; }; A496E8D91CA228C800A448F5 /* OhMyPlane.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OhMyPlane.app; sourceTree = BUILT_PRODUCTS_DIR; }; A496E8DC1CA228C800A448F5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; A496E8E01CA228C800A448F5 /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = GameScene.swift; path = ../Shared/GameScene.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -185,6 +187,7 @@ isa = PBXGroup; children = ( A42DE6601CCDD8F80088AD76 /* GameKitHelper.swift */, + A490077D1CD1ED860046DAC8 /* LeaderBoardHelper.swift */, A42DE6641CCE11970088AD76 /* AchivementHelper.swift */, A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */, ); @@ -589,6 +592,7 @@ A4C08B301CB3A10D00FD8DAE /* TopRecordsScene.swift in Sources */, A496E8F41CA23DBB00A448F5 /* SpriteComponent.swift in Sources */, A43735DC1CA42455005F5BC1 /* Broken.swift in Sources */, + A490077E1CD1ED860046DAC8 /* LeaderBoardHelper.swift in Sources */, A4C08B2C1CB258C800FD8DAE /* TopThreeRecords.swift in Sources */, A496E9011CA2782D00A448F5 /* GameSetting.swift in Sources */, A43735D51CA3D9F9005F5BC1 /* RockEntity.swift in Sources */, diff --git a/Shared/GameScene.swift b/Shared/GameScene.swift index 5ba1c31..ede4f74 100644 --- a/Shared/GameScene.swift +++ b/Shared/GameScene.swift @@ -823,9 +823,12 @@ class GameScene: SKScene, SKPhysicsContactDelegate { } } - private func reportAchievement(planeType: PlaneType, medalType: Rank) { + private func reportToGameCenter(planeType: PlaneType, medalType: Rank, score: Int) { let achievements = AchievementHelper.sharedInstance.createAchievements(planeType, medalType: medalType) GameKitHelper.sharedInstance.reportAchievements(achievements) + + let gkScore = LeaderBoardHelper.sharedInstance.createScore(planeType, score: score) + GameKitHelper.sharedInstance.reportScore(gkScore) } func checkGameScore() { @@ -839,8 +842,8 @@ class GameScene: SKScene, SKPhysicsContactDelegate { } updateGameStatistics(planeEntity.planeType, medalType: rank) - - reportAchievement(planeEntity.planeType, medalType: rank) + + reportToGameCenter(planeEntity.planeType, medalType: rank, score: score) } From cbd36b76c72d914842a75c4e66890b147bfa59f1 Mon Sep 17 00:00:00 2001 From: softdevstory Date: Fri, 29 Apr 2016 11:52:53 +0900 Subject: [PATCH 3/3] tvOS: Game Center is supported. --- OhMyPlane.xcodeproj/project.pbxproj | 6 ++ .../TopRecordsScene+TVControlsScene.swift | 63 ++++++++++++++++++- OhMyPlaneTv/GameViewController.swift | 18 ++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/OhMyPlane.xcodeproj/project.pbxproj b/OhMyPlane.xcodeproj/project.pbxproj index 2368ac9..c939ef4 100644 --- a/OhMyPlane.xcodeproj/project.pbxproj +++ b/OhMyPlane.xcodeproj/project.pbxproj @@ -67,6 +67,9 @@ A48DA06A1CBE2AD7000DCCED /* ShareAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A48DA0681CBE2AD7000DCCED /* ShareAssets.xcassets */; }; A490077C1CD09E6A0046DAC8 /* GameStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */; }; A490077E1CD1ED860046DAC8 /* LeaderBoardHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077D1CD1ED860046DAC8 /* LeaderBoardHelper.swift */; }; + A490077F1CD300AF0046DAC8 /* LeaderBoardHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077D1CD1ED860046DAC8 /* LeaderBoardHelper.swift */; }; + A49007801CD300B20046DAC8 /* AchivementHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42DE6641CCE11970088AD76 /* AchivementHelper.swift */; }; + A49007811CD300B60046DAC8 /* GameStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A490077B1CD09E6A0046DAC8 /* GameStatistics.swift */; }; A496E8DD1CA228C800A448F5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8DC1CA228C800A448F5 /* AppDelegate.swift */; }; A496E8E11CA228C800A448F5 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E01CA228C800A448F5 /* GameScene.swift */; }; A496E8E31CA228C800A448F5 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A496E8E21CA228C800A448F5 /* GameViewController.swift */; }; @@ -549,9 +552,11 @@ buildActionMask = 2147483647; files = ( A435C0631CBF61DF002EFCCF /* TVControlsScene.swift in Sources */, + A49007811CD300B60046DAC8 /* GameStatistics.swift in Sources */, A435C0681CBF62AE002EFCCF /* MainScene+TVControlsScene.swift in Sources */, A48DA04E1CBE2A2F000DCCED /* ReadyGame.swift in Sources */, A48DA04F1CBE2A2F000DCCED /* PlayGame.swift in Sources */, + A490077F1CD300AF0046DAC8 /* LeaderBoardHelper.swift in Sources */, A48DA0481CBE2A1C000DCCED /* TopThreeRecords.swift in Sources */, A48DA0471CBE2A19000DCCED /* GameSetting.swift in Sources */, A48DA0581CBE2A40000DCCED /* TopRecordsScene.swift in Sources */, @@ -560,6 +565,7 @@ A48DA04A1CBE2A23000DCCED /* AnimationComponent.swift in Sources */, A48DA0531CBE2A37000DCCED /* Broken.swift in Sources */, A48DA04D1CBE2A2A000DCCED /* RockEntity.swift in Sources */, + A49007801CD300B20046DAC8 /* AchivementHelper.swift in Sources */, A435C06A1CBF713A002EFCCF /* CreditScene+TVControlsScene.swift in Sources */, A427556C1CBE332600EB0E50 /* GameScene.swift in Sources */, A48DA04B1CBE2A23000DCCED /* PlaneMovementComponent.swift in Sources */, diff --git a/OhMyPlane/TopRecordsScene+TVControlsScene.swift b/OhMyPlane/TopRecordsScene+TVControlsScene.swift index 86687b7..63c5281 100644 --- a/OhMyPlane/TopRecordsScene+TVControlsScene.swift +++ b/OhMyPlane/TopRecordsScene+TVControlsScene.swift @@ -8,12 +8,30 @@ import SpriteKit +private var activeNodes: [SKNode] = [] +private var currentNodeIndex = 0 + private let fadeOut = SKAction.fadeAlphaTo(0.5, duration: 0.5) private let fadeIn = SKAction.fadeAlphaTo(1.0, duration: 0.5) extension TopRecordsScene: TVControlsScene { func setupTVControls() { - backSprite.runAction(SKAction.repeatActionForever(SKAction.sequence([fadeOut, fadeIn]))) + let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(MainScene.didSwipeOnRemote(_:))) + swipeLeft.direction = .Left + swipeLeft.delaysTouchesBegan = true + view!.addGestureRecognizer(swipeLeft) + + let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(MainScene.didSwipeOnRemote(_:))) + swipeRight.direction = .Right + swipeRight.delaysTouchesBegan = true + view!.addGestureRecognizer(swipeRight) + + activeNodes = [] + + activeNodes.append(backSprite) + activeNodes.append(gameCenterSprite) + + selectNodeAtIndex(0) } func touchOnRemoteBegan() { @@ -24,11 +42,48 @@ extension TopRecordsScene: TVControlsScene { // nothing to do } + func didSwipeOnRemote(swipe: UISwipeGestureRecognizer) { + var newIndexToSelect = currentNodeIndex + if swipe.direction == .Right { + newIndexToSelect += 1 + } else { + newIndexToSelect -= 1 + } + + if newIndexToSelect < 0 { + newIndexToSelect = activeNodes.count - 1 + } else if newIndexToSelect > activeNodes.count - 1 { + newIndexToSelect = 0 + } + + selectNodeAtIndex(newIndexToSelect) + } + + func selectNodeAtIndex(index: Int) { + activeNodes[index].runAction(SKAction.repeatActionForever(SKAction.sequence([fadeOut, fadeIn]))) + + if currentNodeIndex < activeNodes.count && index != currentNodeIndex, let node = activeNodes[currentNodeIndex] as? SKSpriteNode { + node.removeAllActions() + node.alpha = 1.0 + } + + currentNodeIndex = index + } + override func pressesBegan(presses: Set, withEvent event: UIPressesEvent?) { for press in presses { switch press.type { case .Select: - touchDownBack() + let node = activeNodes[currentNodeIndex] as! SKSpriteNode + + switch node { + case backSprite: + touchDownBack() + case gameCenterSprite: + touchDownGameCenter() + default: + break + } case .Menu: touchDownBack() @@ -43,5 +98,9 @@ extension TopRecordsScene: TVControlsScene { if backPressed { doBack() } + + if gameCenterPressed { + showGameCenterViewController() + } } } diff --git a/OhMyPlaneTv/GameViewController.swift b/OhMyPlaneTv/GameViewController.swift index bc82fba..1d6a660 100644 --- a/OhMyPlaneTv/GameViewController.swift +++ b/OhMyPlaneTv/GameViewController.swift @@ -28,6 +28,13 @@ class GameViewController: UIViewController { scene.scaleMode = .AspectFill skView.presentScene(scene) + + NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.showAuthenticationViewController), name: PresentAuthenticationViewController, object: nil) + + NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.showGameCenterViewController), name: PresentGameCenterViewController, object: nil) + + GameKitHelper.sharedInstance.authenticateLocalPlayer() + } override func didReceiveMemoryWarning() { @@ -66,4 +73,15 @@ class GameViewController: UIViewController { scene.pressesEnded(presses, withEvent: event) } + + func showAuthenticationViewController() { + let gameKitHelper = GameKitHelper.sharedInstance + if let authenticationViewController = gameKitHelper.authenticationViewController { + presentViewController(authenticationViewController, animated: true, completion: nil) + } + } + + func showGameCenterViewController() { + GameKitHelper.sharedInstance.showGKGameCenterViewController(self) + } }